/** * Builds PHP code which calls the original (ie. parent) method after the added code has been executed. * * @param string $fullClassName Fully qualified name of the original class * @param string $methodName Name of the original method * @return string PHP code */ protected function buildCallParentMethodCode($fullClassName, $methodName) { if (!$this->reflectionService->hasMethod($fullClassName, $methodName)) { return ''; } return 'parent::' . $methodName . '(' . $this->buildMethodParametersCode($fullClassName, $methodName, false) . ");\n"; }
/** * Get the constructor argument reflection for the given object type. * * @param string $className * @return array<array> */ protected function getConstructorArgumentsForClass($className) { if (!isset($this->constructorReflectionFirstLevelCache[$className])) { $constructorSignature = []; // TODO: Check if we can get rid of this reflection service usage, directly reflecting doesn't work as the proxy class __construct has no arguments. if ($this->reflectionService->hasMethod($className, '__construct')) { $constructorSignature = $this->reflectionService->getMethodParameters($className, '__construct'); } $this->constructorReflectionFirstLevelCache[$className] = $constructorSignature; } return $this->constructorReflectionFirstLevelCache[$className]; }
/** * Builds code which registers the lifecycle shutdown method, if any. * * @param Configuration $objectConfiguration * @return string */ protected function buildLifecycleShutdownCode(Configuration $objectConfiguration) { $lifecycleShutdownMethodName = $objectConfiguration->getLifecycleShutdownMethodName(); if (!$this->reflectionService->hasMethod($objectConfiguration->getClassName(), $lifecycleShutdownMethodName)) { return ''; } $className = $objectConfiguration->getClassName(); $code = "\n" . ' if (get_class($this) === \'' . $className . '\') {' . "\n"; $code .= ' \\Neos\\Flow\\Core\\Bootstrap::$staticObjectManager->registerShutdownObject($this, \'' . $lifecycleShutdownMethodName . '\');' . PHP_EOL; $code .= ' }' . "\n"; return $code; }
/** * Builds methods for a single AOP proxy class for the specified class. * * @param string $targetClassName Name of the class to create a proxy class file for * @param array &$aspectContainers The array of aspect containers from the AOP Framework * @return boolean TRUE if the proxy class could be built, FALSE otherwise. */ public function buildProxyClass($targetClassName, array &$aspectContainers) { $interfaceIntroductions = $this->getMatchingInterfaceIntroductions($aspectContainers, $targetClassName); $introducedInterfaces = $this->getInterfaceNamesFromIntroductions($interfaceIntroductions); $introducedTraits = $this->getMatchingTraitNamesFromIntroductions($aspectContainers, $targetClassName); $propertyIntroductions = $this->getMatchingPropertyIntroductions($aspectContainers, $targetClassName); $methodsFromTargetClass = $this->getMethodsFromTargetClass($targetClassName); $methodsFromIntroducedInterfaces = $this->getIntroducedMethodsFromInterfaceIntroductions($interfaceIntroductions, $targetClassName); $interceptedMethods = []; $this->addAdvicedMethodsToInterceptedMethods($interceptedMethods, array_merge($methodsFromTargetClass, $methodsFromIntroducedInterfaces), $targetClassName, $aspectContainers); $this->addIntroducedMethodsToInterceptedMethods($interceptedMethods, $methodsFromIntroducedInterfaces); if (count($interceptedMethods) < 1 && count($introducedInterfaces) < 1 && count($propertyIntroductions) < 1) { return false; } $proxyClass = $this->compiler->getProxyClass($targetClassName); if ($proxyClass === false) { return false; } $proxyClass->addInterfaces($introducedInterfaces); $proxyClass->addTraits($introducedTraits); /** @var $propertyIntroduction PropertyIntroduction */ foreach ($propertyIntroductions as $propertyIntroduction) { $propertyName = $propertyIntroduction->getPropertyName(); $declaringAspectClassName = $propertyIntroduction->getDeclaringAspectClassName(); $possiblePropertyTypes = $this->reflectionService->getPropertyTagValues($declaringAspectClassName, $propertyName, 'var'); if (count($possiblePropertyTypes) > 0 && !$this->reflectionService->isPropertyAnnotatedWith($declaringAspectClassName, $propertyName, Flow\Transient::class)) { $classSchema = $this->reflectionService->getClassSchema($targetClassName); if ($classSchema !== null) { $classSchema->addProperty($propertyName, $possiblePropertyTypes[0]); } } $propertyReflection = new PropertyReflection($declaringAspectClassName, $propertyName); $propertyReflection->setIsAopIntroduced(true); $this->reflectionService->reflectClassProperty($targetClassName, $propertyReflection, new ClassReflection($declaringAspectClassName)); $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(), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::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')->overrideMethodVisibility('protected'); $callBuildMethodsAndAdvicesArrayCode = "\n \$this->Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__clone')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); if (!$this->reflectionService->hasMethod($targetClassName, '__wakeup')) { $proxyClass->getMethod('__wakeup')->addPostParentCallCode(" if (method_exists(get_parent_class(), '__wakeup') && is_callable('parent::__wakeup')) parent::__wakeup();\n"); } $proxyClass->addTraits(['\\' . AdvicesTrait::class]); $this->buildMethodsInterceptorCode($targetClassName, $interceptedMethods); $proxyClass->addProperty('Flow_Aop_Proxy_targetMethodsAndGroupedAdvices', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_groupedAdviceChains', 'array()'); $proxyClass->addProperty('Flow_Aop_Proxy_methodIsInAdviceMode', 'array()'); return true; }
/** * Builds code which registers the lifecycle shutdown method, if any. * * @param Configuration $objectConfiguration * @param int $cause a ObjectManagerInterface::INITIALIZATIONCAUSE_* constant which is the cause of the initialization command being called. * @return string */ protected function buildLifecycleShutdownCode(Configuration $objectConfiguration, $cause) { $lifecycleShutdownMethodName = $objectConfiguration->getLifecycleShutdownMethodName(); if (!$this->reflectionService->hasMethod($objectConfiguration->getClassName(), $lifecycleShutdownMethodName)) { return ''; } $className = $objectConfiguration->getClassName(); $code = "\n" . ' $isSameClass = get_class($this) === \'' . $className . '\';'; if ($cause === ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED) { $code .= "\n" . ' $classParents = class_parents($this);'; $code .= "\n" . ' $classImplements = class_implements($this);'; $code .= "\n" . ' $isClassProxy = array_search(\'' . $className . '\', $classParents) !== FALSE && array_search(\'Doctrine\\ORM\\Proxy\\Proxy\', $classImplements) !== FALSE;' . "\n"; $code .= "\n" . ' if ($isSameClass || $isClassProxy) {' . "\n"; } else { $code .= "\n" . ' if ($isSameClass) {' . "\n"; } $code .= ' \\Neos\\Flow\\Core\\Bootstrap::$staticObjectManager->registerShutdownObject($this, \'' . $lifecycleShutdownMethodName . '\');' . PHP_EOL; $code .= ' }' . "\n"; return $code; }
/** * If mandatory constructor arguments have not been defined yet, this function tries to autowire * them if possible. * * @param array &$objectConfigurations * @return void * @throws UnresolvedDependenciesException */ protected function autowireArguments(array &$objectConfigurations) { foreach ($objectConfigurations as $objectConfiguration) { /** @var Configuration $objectConfiguration */ if ($objectConfiguration->getClassName() === '') { continue; } $className = $objectConfiguration->getClassName(); if (!$this->reflectionService->hasMethod($className, '__construct')) { continue; } $arguments = $objectConfiguration->getArguments(); $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, '__construct', Flow\Autowiring::class); foreach ($this->reflectionService->getMethodParameters($className, '__construct') as $parameterName => $parameterInformation) { $debuggingHint = ''; $index = $parameterInformation['position'] + 1; if (!isset($arguments[$index])) { if ($parameterInformation['optional'] === true) { $defaultValue = isset($parameterInformation['defaultValue']) ? $parameterInformation['defaultValue'] : null; $arguments[$index] = new ConfigurationArgument($index, $defaultValue, ConfigurationArgument::ARGUMENT_TYPES_STRAIGHTVALUE); $arguments[$index]->setAutowiring(Configuration::AUTOWIRING_MODE_OFF); } elseif ($parameterInformation['class'] !== null && isset($objectConfigurations[$parameterInformation['class']])) { $arguments[$index] = new ConfigurationArgument($index, $parameterInformation['class'], ConfigurationArgument::ARGUMENT_TYPES_OBJECT); } elseif ($parameterInformation['allowsNull'] === true) { $arguments[$index] = new ConfigurationArgument($index, null, ConfigurationArgument::ARGUMENT_TYPES_STRAIGHTVALUE); $arguments[$index]->setAutowiring(Configuration::AUTOWIRING_MODE_OFF); } elseif (interface_exists($parameterInformation['class'])) { $debuggingHint = sprintf('No default implementation for the required interface %s was configured, therefore no specific class name could be used for this dependency. ', $parameterInformation['class']); } if (isset($arguments[$index]) && ($objectConfiguration->getAutowiring() === Configuration::AUTOWIRING_MODE_OFF || $autowiringAnnotation !== null && $autowiringAnnotation->enabled === false)) { $arguments[$index]->setAutowiring(Configuration::AUTOWIRING_MODE_OFF); $arguments[$index]->set($index, null); } } if (!isset($arguments[$index]) && $objectConfiguration->getScope() === Configuration::SCOPE_SINGLETON) { throw new UnresolvedDependenciesException(sprintf('Could not autowire required constructor argument $%s for singleton class %s. %sCheck the type hint of that argument and your Objects.yaml configuration.', $parameterName, $className, $debuggingHint), 1298629392); } } $objectConfiguration->setArguments($arguments); } }