/** * Adds a "invokeJoinPoint()" method to the current proxy class. * * @param string $targetClassName * @return void */ protected function buildInvokeJoinPointMethodCode($targetClassName) { $proxyMethod = $this->compiler->getProxyClass($targetClassName)->getMethod('FLOW3_Aop_Proxy_invokeJoinPoint'); $proxyMethod->setMethodParametersCode('\\TYPO3\\FLOW3\\Aop\\JoinPointInterface $joinPoint'); $code = <<<'EOT' if (__CLASS__ !== $joinPoint->getClassName()) return parent::FLOW3_Aop_Proxy_invokeJoinPoint($joinPoint); if (isset($this->FLOW3_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 ($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\FLOW3\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('FLOW3_Proxy_injectProperties')->addPreParentCallCode($injectPropertiesCode); $proxyClass->getMethod('FLOW3_Proxy_injectProperties')->overrideMethodVisibility('private'); $wakeupMethod->addPreParentCallCode("\t\t\$this->FLOW3_Proxy_injectProperties();\n"); $constructorPostCode .= ' if (\'' . $className . '\' === get_class($this)) {' . "\n"; $constructorPostCode .= ' $this->FLOW3_Proxy_injectProperties();' . "\n"; $constructorPostCode .= ' }' . "\n"; } $constructorPostCode .= $this->buildLifecycleInitializationCode($objectConfiguration, \TYPO3\FLOW3\Object\ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED); $constructorPostCode .= $this->buildLifecycleShutdownCode($objectConfiguration); $constructor = $proxyClass->getConstructor(); $constructor->addPreParentCallCode($constructorPreCode); $constructor->addPostParentCallCode($constructorPostCode); } }
/** * Explicitly compile proxy classes * * The compile command triggers the proxy class compilation. * Although a compilation run is triggered automatically by FLOW3, there might * be cases in a production context where a manual compile run is needed. * * @FLOW3\Internal * @param boolean $force If set, classes will be compiled even though the cache says that everything is up to date. * @return void */ public function compileCommand($force = FALSE) { $objectConfigurationCache = $this->cacheManager->getCache('FLOW3_Object_Configuration'); if ($force === FALSE) { if ($objectConfigurationCache->has('allCompiledCodeUpToDate')) { return; } } $classesCache = $this->cacheManager->getCache('FLOW3_Object_Classes'); $this->proxyClassCompiler->injectClassesCache($classesCache); $this->aopProxyClassBuilder->injectObjectConfigurationCache($objectConfigurationCache); $this->aopProxyClassBuilder->build(); $this->dependencyInjectionProxyClassBuilder->build(); $classCount = $this->proxyClassCompiler->compile(); $objectConfigurationCache->set('allCompiledCodeUpToDate', TRUE); $classesCacheBackend = $classesCache->getBackend(); if ($this->bootstrap->getContext()->isProduction() && $classesCacheBackend instanceof FreezableBackendInterface) { $classesCache->getBackend()->freeze(); } $this->emitFinishedCompilationRun($classCount); }
/** * @dataProvider annotationsAndStrings * @test */ public function renderAnnotationRendersCorrectly($annotation, $expectedString) { $this->assertEquals($expectedString, \TYPO3\FLOW3\Object\Proxy\Compiler::renderAnnotation($annotation)); }
/** * Builds the method documentation block for the specified method keeping the vital annotations * * @param string $className Name of the class the method is declared in * @param string $methodName Name of the method to create the parameters code for * @return string $methodDocumentation DocComment for the given method */ protected function buildMethodDocumentation($className, $methodName) { $methodDocumentation = "\t/**\n\t * Autogenerated Proxy Method\n"; if ($this->reflectionService->hasMethod($className, $methodName)) { $methodTags = $this->reflectionService->getMethodTagsValues($className, $methodName); $allowedTags = array('param', 'return', 'throws'); foreach ($methodTags as $tag => $values) { if (in_array($tag, $allowedTags)) { if (count($values) === 0) { $methodDocumentation .= ' * @' . $tag . "\n"; } else { foreach ($values as $value) { $methodDocumentation .= ' * @' . $tag . ' ' . $value . "\n"; } } } } $methodAnnotations = $this->reflectionService->getMethodAnnotations($className, $methodName); foreach ($methodAnnotations as $annotation) { $methodDocumentation .= ' * ' . \TYPO3\FLOW3\Object\Proxy\Compiler::renderAnnotation($annotation) . "\n"; } } $methodDocumentation .= "\t */\n"; return $methodDocumentation; }
/** * Builds the class documentation block for the specified class keeping doc comments and vital annotations * * @return string $methodDocumentation DocComment for the given method */ protected function buildClassDocumentation() { $classDocumentation = "/**\n"; $classReflection = new \TYPO3\FLOW3\Reflection\ClassReflection($this->fullOriginalClassName); $classDescription = $classReflection->getDescription(); $classDocumentation .= ' * ' . str_replace("\n", "\n * ", $classDescription) . "\n"; foreach ($this->reflectionService->getClassAnnotations($this->fullOriginalClassName) as $annotation) { $classDocumentation .= ' * ' . \TYPO3\FLOW3\Object\Proxy\Compiler::renderAnnotation($annotation) . "\n"; } $classDocumentation .= " */\n"; return $classDocumentation; }