/** * @param Mapping $mapping * @param string $className * @param string $propertyName * * @throws \Flowpack\ElasticSearch\Exception * @return void */ protected function augmentMappingByProperty(Mapping $mapping, $className, $propertyName) { list($propertyType) = $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var'); if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Transform')) !== NULL) { $mappingType = $this->transformerFactory->create($transformAnnotation->type)->getTargetMappingType(); } elseif (\TYPO3\Flow\Utility\TypeHandling::isSimpleType($propertyType)) { $mappingType = $propertyType; } elseif ($propertyType === '\\DateTime') { $mappingType = 'date'; } else { throw new \Flowpack\ElasticSearch\Exception('Mapping is only supported for simple types and DateTime objects; "' . $propertyType . '" given but without a Transform directive.'); } $mapping->setPropertyByPath($propertyName, array('type' => $mappingType)); $annotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Mapping'); if ($annotation instanceof MappingAnnotation) { $mapping->setPropertyByPath($propertyName, $this->processMappingAnnotation($annotation, $mapping->getPropertyByPath($propertyName))); if ($annotation->getFields()) { foreach ($annotation->getFields() as $multiFieldAnnotation) { $multiFieldIndexName = trim($multiFieldAnnotation->index_name); if ($multiFieldIndexName === '') { throw new \Flowpack\ElasticSearch\Exception('Multi field require an unique index name "' . $className . '::' . $propertyName . '".'); } if (isset($multiFields[$multiFieldIndexName])) { throw new \Flowpack\ElasticSearch\Exception('Duplicate index name in the same multi field is not allowed "' . $className . '::' . $propertyName . '".'); } $multiFieldAnnotation->type = $mappingType; $multiFields[$multiFieldIndexName] = $this->processMappingAnnotation($multiFieldAnnotation); } $mapping->setPropertyByPath(array($propertyName, 'fields'), $multiFields); } } }
/** * @param string $className * @return string */ protected function createTableDefinitionQueryForClassName($className) { /** @var Table $table */ $table = $this->reflectionService->getClassAnnotation($className, Table::class); $columns = $this->reflectionService->getPropertyNamesByAnnotation($className, Column::class); $columnDefinitions = []; foreach ($columns as $columnName) { /** @var Column $column */ $column = $this->reflectionService->getPropertyAnnotation($className, $columnName, Column::class); $columnDefinitions[] = '`' . $columnName . '` ' . $column->definition; } if ($table->indexes !== NULL) { $columnDefinitions[] = $table->indexes; } return 'CREATE TABLE ' . $table->name . ' (' . implode(', ', $columnDefinitions) . ') ENGINE = InnoDB'; }
/** * @param string $model * @param object $context an arbitrary object which is available in all actions and nested functionality * @return array * @throws MissingModelTypeException * @throws \Exception */ protected function getProperties($model, $context = null) { if ($model === NULL) { throw new MissingModelTypeException('The "model" property has not been specified as parameter to the ViewHelper ' . get_class($this) . '.', 1452715128); } $classSchema = $this->reflectionService->getClassSchema($model); if ($classSchema === NULL) { throw new MissingModelTypeException('No class schema could be resolved for model ' . $model . '.', 1452715183); } $classSchema->getProperties(); $fields = []; foreach ($classSchema->getProperties() as $propertyName => $propertyDefinition) { if ($propertyName === 'Persistence_Object_Identifier') { continue; } /* @var $formFieldAnnotation FormField */ $formFieldAnnotation = $this->reflectionService->getPropertyAnnotation($model, $propertyName, FormField::class); $fields[$propertyName] = []; if ($formFieldAnnotation) { $fields[$propertyName] = get_object_vars($formFieldAnnotation); } $this->addDefaultsToFields($fields, $propertyName); } foreach (get_class_methods($model) as $methodName) { if (substr($methodName, 0, 3) === 'get') { $methodAnnotation = $this->reflectionService->getMethodAnnotation($model, $methodName, FormField::class); if ($methodAnnotation) { $propertyName = lcfirst(substr($methodName, 3)); $fields[$propertyName] = get_object_vars($methodAnnotation); $this->addDefaultsToFields($fields, $propertyName); } } } $generatorAnnotation = $this->reflectionService->getClassAnnotation($model, FieldGenerator::class); if ($generatorAnnotation !== NULL) { $generator = $this->objectManager->get($generatorAnnotation->className); if (!$generator instanceof FieldGeneratorInterface) { throw new \Exception('TODO: generator must implement FieldGeneratorInterface, ' . get_class($generator) . ' given.'); } foreach ($generator->generate($context) as $propertyName => $annotation) { $fields[$propertyName] = get_object_vars($annotation); $this->addDefaultsToFields($fields, $propertyName); } } return (new PositionalArraySorter($fields))->toArray(); }
/** * Returns a multidimensional array with the indexable, probably transformed values of an object * * @param $object * * @return array */ protected function getIndexablePropertiesAndValuesFromObject($object) { $className = $this->reflectionService->getClassNameByObject($object); $data = array(); foreach ($this->indexInformer->getClassProperties($className) as $propertyName) { $value = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($object, $propertyName); if (($transformAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'Flowpack\\ElasticSearch\\Annotations\\Transform')) !== null) { $value = $this->transformerFactory->create($transformAnnotation->type)->transformByAnnotation($value, $transformAnnotation); } $data[$propertyName] = $value; } return $data; }
/** * Builds code which injects an object which was specified by its object name * * @param Configuration $objectConfiguration Configuration of the object to inject into * @param $propertyName * @param $propertyObjectName * @param string $propertyName Name of the property to inject * @param string $propertyObjectName Object name of the object to inject * @return array PHP code * @throws \TYPO3\Flow\Object\Exception\UnknownObjectException */ public function buildPropertyInjectionCodeByString(Configuration $objectConfiguration, $propertyName, $propertyObjectName) { $className = $objectConfiguration->getClassName(); $injectAnnotation = $this->reflectionService->getPropertyAnnotation($className, $propertyName, 'TYPO3\\Flow\\Annotations\\Inject'); if (strpos($propertyObjectName, '.') !== FALSE) { $settingPath = explode('.', $propertyObjectName); $settings = Arrays::getValueByPath($this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS), array_shift($settingPath)); $propertyObjectName = Arrays::getValueByPath($settings, $settingPath); } if ($injectAnnotation !== NULL && $injectAnnotation->setting !== NULL) { if ($injectAnnotation->package === NULL) { $settingPath = $objectConfiguration->getPackageKey(); } else { $settingPath = $injectAnnotation->package; } $settingPath .= '.' . $injectAnnotation->setting; return array('$this->' . $propertyName . ' = \\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'TYPO3\\Flow\\Configuration\\ConfigurationManager\')->getConfiguration(\\TYPO3\\Flow\\Configuration\\ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, \'' . $settingPath . '\');'); } if (!isset($this->objectConfigurations[$propertyObjectName])) { $configurationSource = $objectConfiguration->getConfigurationSourceHint(); if (!isset($propertyObjectName[0])) { throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('Malformed DocComent block for a property in class "' . $className . '".', 1360171313); } if ($propertyObjectName[0] === '\\') { throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('The object name "' . $propertyObjectName . '" which was specified as a property in the object configuration of object "' . $objectConfiguration->getObjectName() . '" (' . $configurationSource . ') starts with a leading backslash.', 1277827579); } else { throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('The object "' . $propertyObjectName . '" which was specified as a property in the object configuration of object "' . $objectConfiguration->getObjectName() . '" (' . $configurationSource . ') does not exist. Check for spelling mistakes and if that dependency is correctly configured.', 1265213849); } } $propertyClassName = $this->objectConfigurations[$propertyObjectName]->getClassName(); if ($this->objectConfigurations[$propertyObjectName]->getScope() === Configuration::SCOPE_PROTOTYPE) { $preparedSetterArgument = 'new \\' . $propertyClassName . '(' . $this->buildMethodParametersCode($this->objectConfigurations[$propertyObjectName]->getArguments()) . ')'; } else { $preparedSetterArgument = '\\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'' . $propertyObjectName . '\')'; } $result = $this->buildSetterInjectionCode($className, $propertyName, $preparedSetterArgument); if ($result !== NULL) { return $result; } if ($injectAnnotation->lazy === TRUE && $this->objectConfigurations[$propertyObjectName]->getScope() !== Configuration::SCOPE_PROTOTYPE) { return $this->buildLazyPropertyInjectionCode($propertyObjectName, $propertyClassName, $propertyName, $preparedSetterArgument); } else { return array('$this->' . $propertyName . ' = ' . $preparedSetterArgument . ';'); } }
/** * 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 \TYPO3\Flow\Aop\Exception */ protected function buildAspectContainer($aspectClassName) { $aspectContainer = new \TYPO3\Flow\Aop\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 \TYPO3\Flow\Annotations\Around::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new \TYPO3\Flow\Aop\Advice\AroundAdvice($aspectClassName, $methodName); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case \TYPO3\Flow\Annotations\Before::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new \TYPO3\Flow\Aop\Advice\BeforeAdvice($aspectClassName, $methodName); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case \TYPO3\Flow\Annotations\AfterReturning::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new \TYPO3\Flow\Aop\Advice\AfterReturningAdvice($aspectClassName, $methodName); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case \TYPO3\Flow\Annotations\AfterThrowing::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new \TYPO3\Flow\Aop\Advice\AfterThrowingAdvice($aspectClassName, $methodName); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case \TYPO3\Flow\Annotations\After::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $advice = new \TYPO3\Flow\Aop\Advice\AfterAdvice($aspectClassName, $methodName); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); $advisor = new \TYPO3\Flow\Aop\Advisor($advice, $pointcut); $aspectContainer->addAdvisor($advisor); break; case \TYPO3\Flow\Annotations\Pointcut::class: $pointcutFilterComposite = $this->pointcutExpressionParser->parse($annotation->expression, $this->renderSourceHint($aspectClassName, $methodName, $annotationClass)); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($annotation->expression, $pointcutFilterComposite, $aspectClassName, $methodName); $aspectContainer->addPointcut($pointcut); break; } } } $introduceAnnotation = $this->reflectionService->getClassAnnotation($aspectClassName, \TYPO3\Flow\Annotations\Introduce::class); if ($introduceAnnotation !== null) { if ($introduceAnnotation->interfaceName === null && $introduceAnnotation->traitName === null) { throw new \TYPO3\Flow\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, \TYPO3\Flow\Annotations\Introduce::class)); $pointcut = new \TYPO3\Flow\Aop\Pointcut\Pointcut($introduceAnnotation->pointcutExpression, $pointcutFilterComposite, $aspectClassName); if ($introduceAnnotation->interfaceName !== null) { $introduction = new \TYPO3\Flow\Aop\InterfaceIntroduction($aspectClassName, $introduceAnnotation->interfaceName, $pointcut); $aspectContainer->addInterfaceIntroduction($introduction); } if ($introduceAnnotation->traitName !== null) { $introduction = new \TYPO3\Flow\Aop\TraitIntroduction($aspectClassName, $introduceAnnotation->traitName, $pointcut); $aspectContainer->addTraitIntroduction($introduction); } } foreach ($this->reflectionService->getClassPropertyNames($aspectClassName) as $propertyName) { $introduceAnnotation = $this->reflectionService->getPropertyAnnotation($aspectClassName, $propertyName, \TYPO3\Flow\Annotations\Introduce::class); if ($introduceAnnotation !== null) { $pointcutFilterComposite = $this->pointcutExpressionParser->parse($introduceAnnotation->pointcutExpression, $this->renderSourceHint($aspectClassName, $propertyName, \TYPO3\Flow\Annotations\Introduce::class)); $pointcut = new \TYPO3\Flow\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 \TYPO3\Flow\Aop\Exception('The class "' . $aspectClassName . '" is tagged to be an aspect but doesn\'t contain advices nor pointcut or introduction declarations.', 1169124534); } return $aspectContainer; }
/** * This function tries to find yet unmatched dependencies which need to be injected via "inject*" setter methods. * * @param array &$objectConfigurations * @return void * @throws \TYPO3\Flow\Object\Exception 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 \TYPO3\Flow\Object\Exception\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 \TYPO3\Flow\Object\Exception\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, \TYPO3\Flow\Annotations\Autowiring::class); if ($autowiringAnnotation !== NULL && $autowiringAnnotation->enabled === FALSE) { continue; } if ($methodName === 'injectSettings') { $packageKey = $objectConfiguration->getPackageKey(); if ($packageKey !== NULL) { $properties[$propertyName] = new ConfigurationProperty($propertyName, array('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, \TYPO3\Flow\Annotations\Inject::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new \TYPO3\Flow\Object\Exception(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, \TYPO3\Flow\Annotations\Inject::class); // TODO: Should be removed with 3.2. Inject settings by Inject-Annotation is deprecated since 3.0. Injecting settings by annotation should be done using the InjectConfiguration annotation if ($injectAnnotation->setting !== NULL) { $packageKey = $injectAnnotation->package !== NULL ? $injectAnnotation->package : $objectConfiguration->getPackageKey(); $configurationPath = rtrim($packageKey . '.' . $injectAnnotation->setting, '.'); $properties[$propertyName] = new ConfigurationProperty($propertyName, array('type' => ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } else { $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, \TYPO3\Flow\Annotations\InjectConfiguration::class) as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { throw new \TYPO3\Flow\Object\Exception(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, \TYPO3\Flow\Annotations\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 \TYPO3\Flow\Object\Exception(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, array('type' => $injectConfigurationAnnotation->type, 'path' => $configurationPath), ConfigurationProperty::PROPERTY_TYPES_CONFIGURATION); } $objectConfiguration->setProperties($properties); } }