/** * Renders the PHP code for this Proxy Method * * @return string PHP code */ public function render() { $methodDocumentation = $this->buildMethodDocumentation($this->fullOriginalClassName, $this->methodName); $methodParametersCode = $this->methodParametersCode !== '' ? $this->methodParametersCode : $this->buildMethodParametersCode($this->fullOriginalClassName, $this->methodName); $callParentMethodCode = $this->buildCallParentMethodCode($this->fullOriginalClassName, $this->methodName); $staticKeyword = $this->reflectionService->isMethodStatic($this->fullOriginalClassName, $this->methodName) ? 'static' : ''; $visibility = $this->visibility === NULL ? $this->getMethodVisibilityString() : $this->visibility; $code = ''; if ($this->addedPreParentCallCode !== '' || $this->addedPostParentCallCode !== '' || $this->methodBody !== '') { $code = "\n" . $methodDocumentation . ' ' . $staticKeyword . ' ' . $visibility . ' function ' . $this->methodName . '(' . $methodParametersCode . ") {\n"; if ($this->methodBody !== '') { $code .= "\n" . $this->methodBody . "\n"; } else { $code .= $this->addedPreParentCallCode; if ($this->addedPostParentCallCode !== '') { $code .= ' $result = ' . ($callParentMethodCode === '' ? "NULL;\n" : $callParentMethodCode); $code .= $this->addedPostParentCallCode; $code .= "\t\treturn \$result;\n"; } else { $code .= $callParentMethodCode === '' ? '' : ' return ' . $callParentMethodCode . ";\n"; } } $code .= "\t}\n"; } return $code; }
/** * Traverses all aspect containers, their aspects and their advisors and adds the * methods and their advices to the (usually empty) array of intercepted methods. * * @param array &$interceptedMethods An array (empty or not) which contains the names of the intercepted methods and additional information * @param array $methods An array of class and method names which are matched against the pointcut (class name = name of the class or interface the method was declared) * @param string $targetClassName Name of the class the pointcut should match with * @param array &$aspectContainers All aspects to take into consideration * @return void */ protected function addAdvicedMethodsToInterceptedMethods(array &$interceptedMethods, array $methods, $targetClassName, array &$aspectContainers) { $pointcutQueryIdentifier = 0; foreach ($aspectContainers as $aspectContainer) { if (!$aspectContainer->getCachedTargetClassNameCandidates()->hasClassName($targetClassName)) { continue; } foreach ($aspectContainer->getAdvisors() as $advisor) { $pointcut = $advisor->getPointcut(); foreach ($methods as $method) { list($methodDeclaringClassName, $methodName) = $method; if ($this->reflectionService->isMethodFinal($targetClassName, $methodName)) { continue; } if ($this->reflectionService->isMethodStatic($targetClassName, $methodName)) { continue; } if ($pointcut->matches($targetClassName, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier)) { $advice = $advisor->getAdvice(); $interceptedMethods[$methodName]['groupedAdvices'][get_class($advice)][] = array('advice' => $advice, 'runtimeEvaluationsClosureCode' => $pointcut->getRuntimeEvaluationsClosureCode()); $interceptedMethods[$methodName]['declaringClassName'] = $methodDeclaringClassName; } $pointcutQueryIdentifier++; } } } }
/** * @return array */ protected function getDeclarations() { $declarations = array(); $methodNames = get_class_methods(get_class($this)); foreach ($methodNames as $methodName) { if (substr($methodName, -11, 11) === 'Declaration' && $this->reflectionService->isMethodStatic(get_class($this), $methodName)) { $declarations[] = substr($methodName, 0, -11); } } return $declarations; }
/** * Compile the result of methods marked with CompileStatic into the proxy class * * @param string $className * @param \TYPO3\Flow\Object\Proxy\ProxyClass $proxyClass * @return void */ protected function compileStaticMethods($className, $proxyClass) { if ($this->classesWithCompileStaticAnnotation === null) { $this->classesWithCompileStaticAnnotation = array_flip($this->reflectionService->getClassesContainingMethodsAnnotatedWith(\TYPO3\Flow\Annotations\CompileStatic::class)); } if (!isset($this->classesWithCompileStaticAnnotation[$className])) { return; } $methodNames = get_class_methods($className); foreach ($methodNames as $methodName) { if ($this->reflectionService->isMethodStatic($className, $methodName) && $this->reflectionService->isMethodAnnotatedWith($className, $methodName, \TYPO3\Flow\Annotations\CompileStatic::class)) { $compiledMethod = $proxyClass->getMethod($methodName); $value = call_user_func(array($className, $methodName), $this->objectManager); $compiledResult = var_export($value, true); $compiledMethod->setMethodBody('return ' . $compiledResult . ';'); } } }
/** * Compile the result of methods marked with CompileStatic into the proxy class * * @param string $className * @param ProxyClass $proxyClass * @return void * @throws ObjectException */ protected function compileStaticMethods($className, ProxyClass $proxyClass) { $methodNames = $this->reflectionService->getMethodsAnnotatedWith($className, Flow\CompileStatic::class); foreach ($methodNames as $methodName) { if (!$this->reflectionService->isMethodStatic($className, $methodName)) { throw new ObjectException(sprintf('The method %s:%s() is annotated CompileStatic so it must be static', $className, $methodName), 1476348303); } if ($this->reflectionService->isMethodPrivate($className, $methodName)) { throw new ObjectException(sprintf('The method %s:%s() is annotated CompileStatic so it must not be private', $className, $methodName), 1476348306); } $reflectedMethod = new MethodReflection($className, $methodName); $reflectedMethod->setAccessible(true); $value = $reflectedMethod->invoke(null, $this->objectManager); $compiledResult = var_export($value, true); $compiledMethod = $proxyClass->getMethod($methodName); $compiledMethod->setMethodBody('return ' . $compiledResult . ';'); } }