/** * Makes sure that any sub classes of an adviced class also build the advices array on construction. * * @param string $className The adviced class name * @param ClassNameIndex $targetClassNameCandidates target class names for advices * @param ClassNameIndex $treatedSubClasses Already treated (sub) classes to avoid duplication * @return ClassNameIndex The new collection of already treated classes */ protected function proxySubClassesOfClassToEnsureAdvices($className, ClassNameIndex $targetClassNameCandidates, ClassNameIndex $treatedSubClasses) { if ($this->reflectionService->isClassReflected($className) === false) { return $treatedSubClasses; } if (trait_exists($className)) { return $treatedSubClasses; } if (interface_exists($className)) { return $treatedSubClasses; } $subClassNames = $this->reflectionService->getAllSubClassNamesForClass($className); foreach ($subClassNames as $subClassName) { if ($treatedSubClasses->hasClassName($subClassName)) { continue; } if ($this->reflectionService->isClassReflected($subClassName) === false || $targetClassNameCandidates->hasClassName($subClassName)) { continue; } $proxyClass = $this->compiler->getProxyClass($subClassName); if ($proxyClass === false) { continue; } $callBuildMethodsAndAdvicesArrayCode = " if (method_exists(get_parent_class(), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); } $treatedSubClasses = $treatedSubClasses->union(new ClassNameIndex($subClassNames)); return $treatedSubClasses; }
/** * Analyzes the Object Configuration provided by the compiler and builds the necessary PHP code for the proxy classes * to realize dependency injection. * * @return void */ public function build() { $this->objectConfigurations = $this->objectManager->getObjectConfigurations(); foreach ($this->objectConfigurations as $objectName => $objectConfiguration) { $className = $objectConfiguration->getClassName(); if ($className === '' || $this->compiler->hasCacheEntryForClass($className) === true) { continue; } if ($objectName !== $className || $this->reflectionService->isClassAbstract($className)) { continue; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === false) { continue; } $this->systemLogger->log('Building DI proxy for "' . $className . '".', LOG_DEBUG); $constructorPreCode = ''; $constructorPostCode = ''; $constructorPreCode .= $this->buildSetInstanceCode($objectConfiguration); $constructorPreCode .= $this->buildConstructorInjectionCode($objectConfiguration); $setRelatedEntitiesCode = ''; if (!$this->reflectionService->hasMethod($className, '__sleep')) { $proxyClass->addTraits(['\\TYPO3\\Flow\\Object\\Proxy\\ObjectSerializationTrait']); $sleepMethod = $proxyClass->getMethod('__sleep'); $sleepMethod->addPostParentCallCode($this->buildSerializeRelatedEntitiesCode($objectConfiguration)); $setRelatedEntitiesCode = "\n " . '$this->Flow_setRelatedEntities();' . "\n"; } $wakeupMethod = $proxyClass->getMethod('__wakeup'); $wakeupMethod->addPreParentCallCode($this->buildSetInstanceCode($objectConfiguration)); $wakeupMethod->addPreParentCallCode($setRelatedEntitiesCode); $wakeupMethod->addPostParentCallCode($this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED)); $wakeupMethod->addPostParentCallCode($this->buildLifecycleShutdownCode($objectConfiguration)); $injectPropertiesCode = $this->buildPropertyInjectionCode($objectConfiguration); if ($injectPropertiesCode !== '') { $proxyClass->addTraits(['\\TYPO3\\Flow\\Object\\DependencyInjection\\PropertyInjectionTrait']); $proxyClass->getMethod('Flow_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('Flow_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode(" \$this->Flow_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->Flow_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); if ($this->objectManager->getContext()->isProduction()) { $this->compileStaticMethods($className, $proxyClass); } } }
/** * 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 = array(); $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, \TYPO3\Flow\Annotations\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); 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(['\\TYPO3\\Flow\\Object\\Proxy\\DoctrineProxyFixingTrait', '\\TYPO3\\Flow\\Aop\\AdvicesTrait']); $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; }
/** * Adds a "invokeJoinPoint()" method to the current proxy class. * * @param string $targetClassName * @return void */ protected function buildInvokeJoinPointMethodCode($targetClassName) { $proxyMethod = $this->compiler->getProxyClass($targetClassName)->getMethod('Flow_Aop_Proxy_invokeJoinPoint'); $proxyMethod->setMethodParametersCode('\\TYPO3\\Flow\\Aop\\JoinPointInterface $joinPoint'); $code = <<<'EOT' if (__CLASS__ !== $joinPoint->getClassName()) return parent::Flow_Aop_Proxy_invokeJoinPoint($joinPoint); if (isset($this->Flow_Aop_Proxy_methodIsInAdviceMode[$joinPoint->getMethodName()])) { return call_user_func_array(array('self', $joinPoint->getMethodName()), $joinPoint->getMethodArguments()); } EOT; $proxyMethod->addPreParentCallCode($code); }
/** * Analyzes the Object Configuration provided by the compiler and builds the necessary PHP code for the proxy classes * to realize dependency injection. * * @return void */ public function build() { $this->objectConfigurations = $this->objectManager->getObjectConfigurations(); foreach ($this->objectConfigurations as $objectName => $objectConfiguration) { $className = $objectConfiguration->getClassName(); if ($className === '' || $this->compiler->hasCacheEntryForClass($className) === TRUE) { continue; } if ($objectName !== $className || $this->reflectionService->isClassAbstract($className) || $this->reflectionService->isClassFinal($className)) { continue; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === FALSE) { continue; } $this->systemLogger->log('Building DI proxy for "' . $className . '".', LOG_DEBUG); $constructorPreCode = ''; $constructorPostCode = ''; $constructorPreCode .= $this->buildSetInstanceCode($objectConfiguration); $constructorPreCode .= $this->buildConstructorInjectionCode($objectConfiguration); $wakeupMethod = $proxyClass->getMethod('__wakeup'); $wakeupMethod->addPreParentCallCode($this->buildSetInstanceCode($objectConfiguration)); $wakeupMethod->addPreParentCallCode($this->buildSetRelatedEntitiesCode()); $wakeupMethod->addPostParentCallCode($this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED)); $wakeupMethod->addPostParentCallCode($this->buildLifecycleShutdownCode($objectConfiguration)); $sleepMethod = $proxyClass->getMethod('__sleep'); $sleepMethod->addPostParentCallCode($this->buildSerializeRelatedEntitiesCode($objectConfiguration)); $searchForEntitiesAndStoreIdentifierArrayMethod = $proxyClass->getMethod('searchForEntitiesAndStoreIdentifierArray'); $searchForEntitiesAndStoreIdentifierArrayMethod->setMethodParametersCode('$path, $propertyValue, $originalPropertyName'); $searchForEntitiesAndStoreIdentifierArrayMethod->overrideMethodVisibility('private'); $searchForEntitiesAndStoreIdentifierArrayMethod->addPreParentCallCode($this->buildSearchForEntitiesAndStoreIdentifierArrayCode()); $injectPropertiesCode = $this->buildPropertyInjectionCode($objectConfiguration); if ($injectPropertiesCode !== '') { $proxyClass->getMethod('Flow_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('Flow_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode("\t\t\$this->Flow_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->Flow_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\Flow\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); if ($this->objectManager->getContext()->isProduction()) { $this->compileStaticMethods($className, $proxyClass); } } }
/** * Adds code to build the methods and advices array in case the parent class has some. * * @param string $className * @param ClassNameIndex $treatedSubClasses * @return ClassNameIndex */ protected function addBuildMethodsAndAdvicesCodeToClass($className, ClassNameIndex $treatedSubClasses) { if ($treatedSubClasses->hasClassName($className)) { return $treatedSubClasses; } $treatedSubClasses = $treatedSubClasses->union(new ClassNameIndex([$className])); if ($this->reflectionService->isClassReflected($className) === false) { return $treatedSubClasses; } $proxyClass = $this->compiler->getProxyClass($className); if ($proxyClass === false) { return $treatedSubClasses; } $callBuildMethodsAndAdvicesArrayCode = " if (method_exists(get_parent_class(), 'Flow_Aop_Proxy_buildMethodsAndAdvicesArray') && is_callable('parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray')) parent::Flow_Aop_Proxy_buildMethodsAndAdvicesArray();\n"; $proxyClass->getConstructor()->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); $proxyClass->getMethod('__wakeup')->addPreParentCallCode($callBuildMethodsAndAdvicesArrayCode); return $treatedSubClasses; }