Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 66 additions & 7 deletions Neos.Flow/Classes/Annotations/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@
*/

use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Neos\Flow\ObjectManagement\Exception\ProxyCompilerException;

/**
* Used to disable proxy building for an object.
* Controls proxy class generation behavior for a class.
*
* If disabled, neither Dependency Injection nor AOP can be used
* on the object.
* This annotation allows you to:
* - Disable proxy building entirely (enabled=false) - useful for value objects, DTOs,
* or classes that should not use Dependency Injection or AOP
* - Force generation of serialization code (forceSerializationCode=true) - rarely needed
* escape hatch for edge cases where automatic detection of entity relationships fails
*
* When proxy building is disabled (enabled=false), neither Dependency Injection nor AOP
* can be used on the object. The class will be instantiated directly without any
* framework enhancements.
*
* @Annotation
* @NamedArgumentConstructor
Expand All @@ -27,13 +35,64 @@
final class Proxy
{
/**
* Whether proxy building for the target is disabled. (Can be given as anonymous argument.)
* @var boolean
* Whether proxy building is enabled for this class.
*
* When set to false, Flow will not generate a proxy class, meaning:
* - No Dependency Injection (no Flow\Inject annotations)
* - No Aspect-Oriented Programming (no AOP advices)
* - No automatic serialization handling
* - The class is instantiated directly without any framework enhancements
*
* This is useful for simple value objects, DTOs, or utility classes that don't need
* framework features and where you want to avoid the minimal overhead of proxy classes.
*
* (Can be given as anonymous argument.)
*/
public $enabled = true;
public bool $enabled = true;

public function __construct(bool $enabled = true)
/**
* Force the generation of serialization code (__sleep/__wakeup methods) in the proxy class.
*
* Flow automatically detects when serialization code is needed (e.g., when a class has entity
* properties, injected dependencies, or transient properties) and generates the appropriate
* __sleep() and __wakeup() methods. These methods handle:
* - Converting entity references to metadata (class name + persistence identifier)
* - Removing injected and framework-internal properties before serialization
* - Restoring entity references and re-injecting dependencies after deserialization
*
* This flag serves as an **escape hatch for rare edge cases** where automatic detection fails,
* such as:
* - Complex generic/template types that aren't fully parsed (e.g., ComplexType<Entity>)
* - Deeply nested entity structures where type hints don't reveal the entity relationship
* - Union or intersection types with entities that the reflection system cannot fully analyze
* - Properties with dynamic types where documentation hints are non-standard
*
* IMPORTANT: You should rarely need this flag. Flow's automatic detection handles:
* - Properties typed with Flow\Entity classes
* - Properties with Flow\Inject annotations
* - Properties with Flow\Transient annotations
* - Classes with AOP advices
* - Session-scoped objects
*
* If you find yourself needing this flag for standard entity properties, injected dependencies,
* or other common cases, this indicates a bug in Flow's detection logic that should be reported
* at https://github.com/neos/flow-development-collection/issues
*
* Note: Disabling serialization code (not possible via this flag) would break classes with
* AOP, injections, or entity relationships. To completely opt out of proxy features, use
* enabled=false instead.
*
* @see https://flowframework.readthedocs.io/ for more information on object serialization
*/
public bool $forceSerializationCode = false;

public function __construct(bool $enabled = true, bool $forceSerializationCode = false)
{
if ($enabled === false && $forceSerializationCode === true) {
throw new ProxyCompilerException('Cannot disable a Proxy but forceSerializationCode at the same time.', 1756813222);
}

$this->enabled = $enabled;
$this->forceSerializationCode = $forceSerializationCode;
}
}
3 changes: 2 additions & 1 deletion Neos.Flow/Classes/Aop/Builder/ProxyClassBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ public function buildProxyClass(string $targetClassName, array $aspectContainers
$proxyClass->addProperty($propertyName, var_export($propertyIntroduction->getInitialValue(), true), $propertyIntroduction->getPropertyVisibility(), $propertyIntroduction->getPropertyDocComment());
}

$proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode(" if (method_exists(get_parent_class(\$this), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable([parent::class, 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray'])) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n");
$proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode(" if (method_exists(parent::class, 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable([parent::class, 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray'])) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n");
$proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->addPreParentCallCode($this->buildMethodsAndAdvicesArrayCode($interceptedMethods));
$proxyClass->getMethod('Flow_Aop_Proxy_buildMethodsAndAdvicesArray')->setVisibility(ProxyMethodGenerator::VISIBILITY_PROTECTED);

Expand All @@ -424,6 +424,7 @@ public function buildProxyClass(string $targetClassName, array $aspectContainers
PHP);
}
$proxyClass->addTraits(['\\' . AdvicesTrait::class]);
$proxyClass->addInterfaces(['\\' . Aop\ProxyInterface::class]);

$this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods);

Expand Down
Loading