/** * Registers aspects written within source files which we might encounter * * @param \AppserverIo\Psr\Application\ApplicationInterface $application The application instance * * @return void */ public function registerAspectXml(ApplicationInterface $application) { /** @var \AppserverIo\Appserver\Core\Api\ConfigurationService $configurationService */ $configurationService = $application->newService('AppserverIo\\Appserver\\Core\\Api\\ConfigurationService'); // check if we even have a XMl file to read from $xmlPaths = $configurationService->globDir($this->getWebappPath() . DIRECTORY_SEPARATOR . '{WEB-INF,META-INF,common}' . DIRECTORY_SEPARATOR . self::CONFIG_FILE, GLOB_BRACE); foreach ($xmlPaths as $xmlPath) { // iterate all XML configuration files we found if (is_readable($xmlPath)) { // validate the file here, if it is not valid we can skip further steps try { $configurationService->validateFile($xmlPath, null, true); } catch (InvalidConfigurationException $e) { /** @var \Psr\Log\LoggerInterface $systemLogger */ $systemLogger = $this->getApplication()->getInitialContext()->getSystemLogger(); $systemLogger->error($e->getMessage()); $systemLogger->critical(sprintf('Pointcuts configuration file %s is invalid, AOP functionality might not work as expected.', $xmlPath)); continue; } // load the aop config $config = new \SimpleXMLElement(file_get_contents($xmlPath)); $config->registerXPathNamespace('a', 'http://www.appserver.io/appserver'); // create us an aspect // name of the aspect will be the application name $aspect = new Aspect(); $aspect->setName($xmlPath); // check if we got some pointcuts foreach ($config->xpath('/a:pointcuts/a:pointcut') as $pointcutConfiguration) { // build up the pointcut and add it to the collection $pointcut = new Pointcut(); $pointcut->setAspectName($aspect->getName()); $pointcut->setName((string) $pointcutConfiguration->{'pointcut-name'}); $pointcut->setPointcutExpression(new PointcutExpression((string) $pointcutConfiguration->{'pointcut-pattern'})); $aspect->getPointcuts()->add($pointcut); } // check if we got some advices foreach ($config->xpath('/a:pointcuts/a:advice') as $adviceConfiguration) { // build up the advice and add it to the aspect $advice = new Advice(); $advice->setAspectName((string) $adviceConfiguration->{'advice-aspect'}); $advice->setName($advice->getAspectName() . '->' . (string) $adviceConfiguration->{'advice-name'}); $advice->setCodeHook((string) $adviceConfiguration->{'advice-type'}); $pointcutPointcut = $this->generatePointcutPointcut((array) $adviceConfiguration->{'advice-pointcuts'}->{'pointcut-name'}, $aspect); $advice->getPointcuts()->add($pointcutPointcut); // finally add the advice to our aspect (we will also add it without pointcuts of its own) $aspect->getAdvices()->add($advice); } // if the aspect contains pointcuts or advices it can be used if ($aspect->getPointcuts()->count() > 0 || $aspect->getAdvices()->count() > 0) { $this->getAspectRegister()->set($aspect->getName(), $aspect); } } } }
/** * Will register a complete aspect to the AspectRegister. * This include its advices and pointcuts which can be looked up from this point on * * @param \AppserverIo\Doppelgaenger\Entities\Definitions\AspectDefinition $aspectDefinition Structure to register as an aspect * * @return null */ public function register(AspectDefinition $aspectDefinition) { // create the new aspect and fill it with things we already know $aspect = new Aspect(); $aspect->setName($aspectDefinition->getName()); $aspect->setNamespace($aspectDefinition->getNamespace()); // prepare the tokenizer we will need for further processing $needles = array(AfterReturning::ANNOTATION, AfterThrowing::ANNOTATION, After::ANNOTATION, Around::ANNOTATION, Before::ANNOTATION); $tokenizer = new Tokenizer(); $tokenizer->ignore(array('param', 'return', 'throws')); // iterate the functions and filter out the ones used as advices $scheduledAdviceDefinitions = array(); foreach ($aspectDefinition->getFunctionDefinitions() as $functionDefinition) { $foundNeedle = false; foreach ($needles as $needle) { // create the advice if (strpos($functionDefinition->getDocBlock(), '@' . $needle) !== false) { $foundNeedle = true; $scheduledAdviceDefinitions[$needle][] = $functionDefinition; break; } } // create the pointcut if (!$foundNeedle && strpos($functionDefinition->getDocBlock(), '@' . Pointcut::ANNOTATION) !== false) { $pointcut = new PointcutDefinition(); $pointcut->setName($functionDefinition->getName()); $tokens = new Tokens($tokenizer->parse($functionDefinition->getDocBlock())); // convert to array and run it through our advice factory $toArray = new ToArray(); $annotations = $toArray->convert($tokens); // create the entities for the join-points and advices the pointcut describes $pointcut->setPointcutExpression(new PointcutExpression(array_pop(array_pop($annotations)->values))); $aspect->getPointcuts()->add($pointcut); } } $this->add($aspect); // do the pointcut lookups where we will need the pointcut factory for later use $pointcutFactory = new PointcutFactory(); foreach ($scheduledAdviceDefinitions as $codeHook => $hookedAdviceDefinitions) { foreach ($hookedAdviceDefinitions as $scheduledAdviceDefinition) { // create our advice $advice = new Advice(); $advice->setAspectName($aspectDefinition->getQualifiedName()); $advice->setName($scheduledAdviceDefinition->getName()); $advice->setCodeHook((string) $codeHook); $tokens = new Tokens($tokenizer->parse($scheduledAdviceDefinition->getDocBlock())); // convert to array and run it through our advice factory $toArray = new ToArray(); $annotations = $toArray->convert($tokens); // create the entities for the join-points and advices the pointcut describes foreach ($annotations as $annotation) { $pointcut = $pointcutFactory->getInstance(array_pop($annotation->values)); if ($pointcut instanceof PointcutPointcut) { // get the referenced pointcuts for the split parts of the expression $expressionParts = explode(PointcutPointcut::EXPRESSION_CONNECTOR, $pointcut->getExpression()); // lookup all the referenced pointcuts $referencedPointcuts = array(); foreach ($expressionParts as $expressionPart) { $referencedPointcuts = array_merge($referencedPointcuts, $this->lookupPointcuts($expressionPart)); } $pointcut->setReferencedPointcuts($referencedPointcuts); } $advice->getPointcuts()->add($pointcut); } $aspect->getAdvices()->add($advice); } } $this->set($aspectDefinition->getQualifiedName(), $aspect); }