/** * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods. * * @param array &$objectConfigurations * @return void * @throws ObjectException if an injected property is private */ protected function autowireProperties(array &$objectConfigurations) { /** @var Configuration $objectConfiguration */ foreach ($objectConfigurations as $objectConfiguration) { $className = $objectConfiguration->getClassName(); $properties = $objectConfiguration->getProperties(); if ($className === '') { continue; } $classMethodNames = get_class_methods($className); if (!is_array($classMethodNames)) { if (!class_exists($className)) { throw new UnknownClassException(sprintf('The class "%s" defined in the object configuration for object "%s", defined in package: %s, does not exist.', $className, $objectConfiguration->getObjectName(), $objectConfiguration->getPackageKey()), 1352371371); } else { throw new UnknownClassException(sprintf('Could not autowire properties of class "%s" because names of methods contained in that class could not be retrieved using get_class_methods().', $className), 1352386418); } } foreach ($classMethodNames as $methodName) { if (isset($methodName[6]) && strpos($methodName, 'inject') === 0 && $methodName[6] === strtoupper($methodName[6])) { $propertyName = lcfirst(substr($methodName, 6)); $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, Flow\Autowiring::class); if ($autowiringAnnotation !== null && $autowiringAnnotation->enabled === false) { continue; } if ($methodName === 'injectSettings') { $packageKey = $objectConfiguration->getPackageKey(); if ($packageKey !== null) { $properties[$propertyName] = new ConfigurationProperty($propertyName, ['type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $packageKey], ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } } else { if (array_key_exists($propertyName, $properties)) { continue; } $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName); if (count($methodParameters) !== 1) { $this->systemLogger->log(sprintf('Could not autowire property %s because %s() expects %s instead of exactly 1 parameter.', $className . '::' . $propertyName, $methodName, count($methodParameters) ?: 'none'), LOG_DEBUG); continue; } $methodParameter = array_pop($methodParameters); if ($methodParameter['class'] === null) { $this->systemLogger->log(sprintf('Could not autowire property %s because the method parameter in %s() contained no class type hint.', $className . '::' . $propertyName, $methodName), LOG_DEBUG); continue; } $properties[$propertyName] = new ConfigurationProperty($propertyName, $methodParameter['class'], ConfigurationProperty::PROPERTY_TYPES_OBJECT); } } } foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, Inject::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new ObjectException(sprintf('The property "%%s" in class "%s" must not be private when annotated for injection.', $propertyName, $className), 1328109641); } if (!array_key_exists($propertyName, $properties)) { /** @var Inject $injectAnnotation */ $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, Inject::class); $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\'); $configurationProperty = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT, null, $injectAnnotation->lazy); $properties[$propertyName] = $configurationProperty; } } foreach ($this->reflectionService->getPropertyNamesByAnnotation($className, InjectConfiguration::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new ObjectException(sprintf('The property "%s" in class "%s" must not be private when annotated for configuration injection.', $propertyName, $className), 1416765599); } if (array_key_exists($propertyName, $properties)) { continue; } /** @var InjectConfiguration $injectConfigurationAnnotation */ $injectConfigurationAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, InjectConfiguration::class); if ($injectConfigurationAnnotation->type === ConfigurationManager::CONFIGURATION_TYPE_SETTINGS) { $packageKey = $injectConfigurationAnnotation->package !== null ? $injectConfigurationAnnotation->package : $objectConfiguration->getPackageKey(); $configurationPath = rtrim($packageKey . '.' . $injectConfigurationAnnotation->path, '.'); } else { if ($injectConfigurationAnnotation->package !== null) { throw new ObjectException(sprintf('The InjectConfiguration annotation for property "%s" in class "%s" specifies a "package" key for configuration type "%s", but this is only supported for injection of "Settings".', $propertyName, $className, $injectConfigurationAnnotation->type), 1420811958); } $configurationPath = $injectConfigurationAnnotation->path; } $properties[$propertyName] = new ConfigurationProperty($propertyName, ['type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath], ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } $objectConfiguration->setProperties($properties); } }
/** * Creates and returns an aspect from the annotations found in a class which * is tagged as an aspect. The object acting as an advice will already be * fetched (and therefore instantiated if necessary). * * @param string $aspectClassName Name of the class which forms the aspect, contains advices etc. * @return mixed The aspect container containing one or more advisors or FALSE if no container could be built * @throws Aop\Exception */ protected function buildAspectContainer($aspectClassName) { $aspectContainer = new AspectContainer($aspectClassName); $methodNames = get_class_methods($aspectClassName); foreach ($methodNames as $methodName) { foreach ($this->reflectionService->getMethodAnnotations($aspectClassName, $methodName) as $annotation) { $annotationClass = get_class($annotation); switch ($annotationClass) { case Flow\Around::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AroundAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\Before::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\BeforeAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\AfterReturning::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterReturningAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\AfterThrowing::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterThrowingAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\After::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new Aop\Advice\AfterAdvice($aspectClassName, $methodName); $pointcut = new Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case Flow\Pointcut::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->expression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $pointcut = new Aop\Pointcut\Pointcut($annotation->expression, $pointcutFilterComposite, $aspectClassName, $methodName); $aspectContainer->addPointcut($pointcut); break; } } } $introduceAnnotation = $this->reflectionService->getClassAnnotation($aspectClassName, Flow\Introduce::class); if ($introduceAnnotation !== null) { if ($introduceAnnotation->interfaceName === null && $introduceAnnotation->traitName === null) { throw new Aop\Exception('The introduction in class "' . $aspectClassName . '" does neither contain an interface name nor a trait name, at least one is required.', 1172694761); } $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $introduceAnnotation->interfaceName, Flow\Introduce::class)); $pointcut = new Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); if ($introduceAnnotation->interfaceName !== null) { $introduction = new Aop\InterfaceIntroduction($aspectClassName, $introduceAnnotation->interfaceName, $pointcut); $aspectContainer->addInterfaceIntroduction($introduction); } if ($introduceAnnotation->traitName !== null) { $introduction = new TraitIntroduction($aspectClassName, $introduceAnnotation->traitName, $pointcut); $aspectContainer->addTraitIntroduction($introduction); } } foreach ($this->reflectionService->getClassPropertyNames($aspectClassName) as $propertyName) { $introduceAnnotation = $this->reflectionService->getPropertyAnnotation($aspectClassName, $propertyName, Flow\Introduce::class); if ($introduceAnnotation !== null) { $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $propertyName, Flow\Introduce::class)); $pointcut = new Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $introduction = new PropertyIntroduction($aspectClassName, $propertyName, $pointcut); $aspectContainer->addPropertyIntroduction($introduction); } } if (count($aspectContainer->getAdvisors()) < 1 && count($aspectContainer->getPointcuts()) < 1 && count($aspectContainer->getInterfaceIntroductions()) < 1 && count($aspectContainer->getTraitIntroductions()) < 1 && count($aspectContainer->getPropertyIntroductions()) < 1) { throw new Aop\Exception('The class "' . $aspectClassName . '" is tagged to be an aspect but doesn\'t contain advices nor pointcut or introduction declarations.', 1169124534); } return $aspectContainer; }