/**
  * 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;
 }
 /**
  * Explicitly compile proxy classes
  *
  * The compile command triggers the proxy class compilation.
  * Although a compilation run is triggered automatically by Flow, there might
  * be cases in a production context where a manual compile run is needed.
  *
  * @Flow\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)
 {
     /** @var VariableFrontend $objectConfigurationCache */
     $objectConfigurationCache = $this->cacheManager->getCache('Flow_Object_Configuration');
     if ($force === false) {
         if ($objectConfigurationCache->has('allCompiledCodeUpToDate')) {
             return;
         }
     }
     /** @var PhpFrontend $classesCache */
     $classesCache = $this->cacheManager->getCache('Flow_Object_Classes');
     $this->proxyClassCompiler->injectClassesCache($classesCache);
     $this->aopProxyClassBuilder->injectObjectConfigurationCache($objectConfigurationCache);
     $this->aopProxyClassBuilder->build();
     $this->dependencyInjectionProxyClassBuilder->build();
     $classCount = $this->proxyClassCompiler->compile();
     $dataTemporaryPath = $this->environment->getPathToTemporaryDirectory();
     Files::createDirectoryRecursively($dataTemporaryPath);
     file_put_contents($dataTemporaryPath . 'AvailableProxyClasses.php', $this->proxyClassCompiler->getStoredProxyClassMap());
     $objectConfigurationCache->set('allCompiledCodeUpToDate', true);
     $classesCacheBackend = $classesCache->getBackend();
     if ($this->bootstrap->getContext()->isProduction() && $classesCacheBackend instanceof FreezableBackendInterface) {
         /** @var FreezableBackendInterface $backend */
         $backend = $classesCache->getBackend();
         $backend->freeze();
     }
     $this->emitFinishedCompilationRun($classCount);
 }
 /**
  * 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;
 }
 /**
  * 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\Flow\Reflection\ClassReflection($this->fullOriginalClassName);
     $classDescription = $classReflection->getDescription();
     $classDocumentation .= ' * ' . str_replace("\n", "\n * ", $classDescription) . "\n";
     foreach ($this->reflectionService->getClassAnnotations($this->fullOriginalClassName) as $annotation) {
         $classDocumentation .= ' * ' . \TYPO3\Flow\Object\Proxy\Compiler::renderAnnotation($annotation) . "\n";
     }
     $classDocumentation .= " */\n";
     return $classDocumentation;
 }
 /**
  * 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\Flow\Object\Proxy\Compiler::renderAnnotation($annotation) . "\n";
         }
     }
     $methodDocumentation .= "\t */\n";
     return $methodDocumentation;
 }
 /**
  * @param string $classCode
  * @param string $expectedResult
  * @test
  * @dataProvider stripOpeningPhpTagCorrectlyStripsPhpTagDataProvider
  */
 public function stripOpeningPhpTagCorrectlyStripsPhpTagTests($classCode, $expectedResult)
 {
     $actualResult = $this->compiler->_call('stripOpeningPhpTag', $classCode);
     $this->assertSame($expectedResult, $actualResult);
 }
예제 #11
0
 /**
  * @dataProvider annotationsAndStrings
  * @test
  */
 public function renderAnnotationRendersCorrectly($annotation, $expectedString)
 {
     $this->assertEquals($expectedString, \TYPO3\Flow\Object\Proxy\Compiler::renderAnnotation($annotation));
 }