/** * Returns the JavaScript to declare the Ext Direct provider for all * controller actions that are annotated with "@TYPO3\ExtJS\Annotations\ExtDirect" * * = Examples = * * <code title="Simple"> * {namespace ext=TYPO3\ExtJS\ViewHelpers} * ... * <script type="text/javascript"> * <ext:extdirect.provider /> * </script> * ... * </code> * * TODO Cache ext direct provider config * @param string $namespace The base ExtJS namespace (with dots) for the direct provider methods * @return string JavaScript needed to include Ext Direct provider * @api */ public function render($namespace = NULL) { $providerConfig = array('url' => '?TYPO3_ExtJS_ExtDirectRequest=1&__csrfToken=' . $this->securityContext->getCsrfProtectionToken(), 'type' => 'remoting', 'actions' => array()); if (!empty($namespace)) { $providerConfig['namespace'] = $namespace; } $controllerClassNames = $this->localReflectionService->getAllImplementationClassNamesForInterface('TYPO3\\Flow\\Mvc\\Controller\\ControllerInterface'); foreach ($controllerClassNames as $controllerClassName) { $methodNames = get_class_methods($controllerClassName); foreach ($methodNames as $methodName) { $methodTagsValues = $this->localReflectionService->getMethodTagsValues($controllerClassName, $methodName); if (isset($methodTagsValues['extdirect'])) { $methodParameters = $this->localReflectionService->getMethodParameters($controllerClassName, $methodName); $requiredMethodParametersCount = 0; foreach ($methodParameters as $methodParameter) { if ($methodParameter['optional'] === TRUE) { break; } $requiredMethodParametersCount++; } $extDirectAction = str_replace('\\', '_', $controllerClassName); $providerConfig['actions'][$extDirectAction][] = array('name' => substr($methodName, 0, -6), 'len' => $requiredMethodParametersCount); } } } return 'Ext.Direct.addProvider(' . json_encode($providerConfig) . ');' . chr(10); }
/** * get indexed arguments for method * * @param string $class * @param string $method * @return array */ protected function getArguments($class, $method) { $arguments = array(); $parameters = $this->reflectionService->getMethodParameters($class, $method); foreach ($parameters as $name => $parameter) { $arguments[$parameter['position']] = $this->request->hasArgument($name) ? $this->request->getArgument($name) : null; } return $arguments; }
/** * @test */ public function methodParameterTypeExpansionDoesNotModifySimpleTypes() { $methodParameters = $this->reflectionService->getMethodParameters('TYPO3\\Flow\\Tests\\Functional\\Reflection\\Fixtures\\Model\\EntityWithUseStatements', 'simpleType'); $expectedType = 'float'; $actualType = $methodParameters['parameter']['type']; $this->assertSame($expectedType, $actualType); }
/** * @test */ public function methodParametersGetNormalizedType() { $methodParameters = $this->reflectionService->getMethodParameters(\TYPO3\Flow\Tests\Functional\Reflection\Fixtures\AnnotatedClass::class, 'intAndIntegerParameters'); foreach ($methodParameters as $methodParameter) { $this->assertEquals('integer', $methodParameter['type']); } }
/** * Checks if the specified method matches against the method name * expression. * * Returns TRUE if method name, visibility and arguments constraints match and the target * method is not final. * * @param string $className Ignored in this pointcut filter * @param string $methodName Name of the method to match against * @param string $methodDeclaringClassName Name of the class the method was originally declared in * @param mixed $pointcutQueryIdentifier Some identifier for this query - must at least differ from a previous identifier. Used for circular reference detection. * @return boolean TRUE if the class matches, otherwise FALSE * @throws \TYPO3\Flow\Aop\Exception */ public function matches($className, $methodName, $methodDeclaringClassName, $pointcutQueryIdentifier) { $matchResult = preg_match('/^' . $this->methodNameFilterExpression . '$/', $methodName); if ($matchResult === false) { throw new \TYPO3\Flow\Aop\Exception('Error in regular expression', 1168876915); } elseif ($matchResult !== 1) { return false; } switch ($this->methodVisibility) { case 'public': if (!($methodDeclaringClassName !== null && $this->reflectionService->isMethodPublic($methodDeclaringClassName, $methodName))) { return false; } break; case 'protected': if (!($methodDeclaringClassName !== null && $this->reflectionService->isMethodProtected($methodDeclaringClassName, $methodName))) { return false; } break; } if ($methodDeclaringClassName !== null && $this->reflectionService->isMethodFinal($methodDeclaringClassName, $methodName)) { return false; } $methodArguments = $methodDeclaringClassName === null ? array() : $this->reflectionService->getMethodParameters($methodDeclaringClassName, $methodName); foreach (array_keys($this->methodArgumentConstraints) as $argumentName) { $objectAccess = explode('.', $argumentName, 2); $argumentName = $objectAccess[0]; if (!array_key_exists($argumentName, $methodArguments)) { $this->systemLogger->log('The argument "' . $argumentName . '" declared in pointcut does not exist in method ' . $methodDeclaringClassName . '->' . $methodName, LOG_NOTICE); return false; } } return true; }
/** * Takes an array of unparsed command line arguments and options and converts it separated * by named arguments, options and unnamed arguments. * * @param array $rawCommandLineArguments The unparsed command parts (such as "--foo") as an array * @param string $controllerObjectName Object name of the designated command controller * @param string $controllerCommandName Command name of the recognized command (ie. method name without "Command" suffix) * @return array All and exceeding command line arguments * @throws \TYPO3\Flow\Mvc\Exception\InvalidArgumentMixingException */ protected function parseRawCommandLineArguments(array $rawCommandLineArguments, $controllerObjectName, $controllerCommandName) { $commandLineArguments = array(); $exceedingArguments = array(); $commandMethodName = $controllerCommandName . 'Command'; $commandMethodParameters = $this->reflectionService->getMethodParameters($controllerObjectName, $commandMethodName); $requiredArguments = array(); $optionalArguments = array(); $argumentNames = array(); foreach ($commandMethodParameters as $parameterName => $parameterInfo) { $argumentNames[] = $parameterName; if ($parameterInfo['optional'] === FALSE) { $requiredArguments[strtolower($parameterName)] = array('parameterName' => $parameterName, 'type' => $parameterInfo['type']); } else { $optionalArguments[strtolower($parameterName)] = array('parameterName' => $parameterName, 'type' => $parameterInfo['type']); } } $decidedToUseNamedArguments = FALSE; $decidedToUseUnnamedArguments = FALSE; $argumentIndex = 0; while (count($rawCommandLineArguments) > 0) { $rawArgument = array_shift($rawCommandLineArguments); if ($rawArgument !== '' && $rawArgument[0] === '-') { if ($rawArgument[1] === '-') { $rawArgument = substr($rawArgument, 2); } else { $rawArgument = substr($rawArgument, 1); } $argumentName = $this->extractArgumentNameFromCommandLinePart($rawArgument); if (isset($optionalArguments[$argumentName])) { $argumentValue = $this->getValueOfCurrentCommandLineOption($rawArgument, $rawCommandLineArguments, $optionalArguments[$argumentName]['type']); $commandLineArguments[$optionalArguments[$argumentName]['parameterName']] = $argumentValue; } elseif (isset($requiredArguments[$argumentName])) { if ($decidedToUseUnnamedArguments) { throw new \TYPO3\Flow\Mvc\Exception\InvalidArgumentMixingException(sprintf('Unexpected named argument "%s". If you use unnamed arguments, all required arguments must be passed without a name.', $argumentName), 1309971821); } $decidedToUseNamedArguments = TRUE; $argumentValue = $this->getValueOfCurrentCommandLineOption($rawArgument, $rawCommandLineArguments, $requiredArguments[$argumentName]['type']); $commandLineArguments[$requiredArguments[$argumentName]['parameterName']] = $argumentValue; unset($requiredArguments[$argumentName]); } } else { if (count($requiredArguments) > 0) { if ($decidedToUseNamedArguments) { throw new \TYPO3\Flow\Mvc\Exception\InvalidArgumentMixingException(sprintf('Unexpected unnamed argument "%s". If you use named arguments, all required arguments must be passed named.', $rawArgument), 1309971820); } $argument = array_shift($requiredArguments); $commandLineArguments[$argument['parameterName']] = $rawArgument; $decidedToUseUnnamedArguments = TRUE; } else { $exceedingArguments[] = $rawArgument; } } $argumentIndex++; } return array($commandLineArguments, $exceedingArguments); }
/** * 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) { foreach ($objectConfigurations as $objectConfiguration) { $className = $objectConfiguration->getClassName(); $properties = $objectConfiguration->getProperties(); $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 (strlen($methodName) > 6 && substr($methodName, 0, 6) === 'inject' && $methodName[6] === strtoupper($methodName[6])) { $propertyName = lcfirst(substr($methodName, 6)); $autowiringAnnotation = $this->reflectionService->getMethodAnnotation($className, $methodName, 'TYPO3\\Flow\\Annotations\\Autowiring'); if ($autowiringAnnotation !== NULL && $autowiringAnnotation->enabled === FALSE) { continue; } if ($methodName === 'injectSettings') { $packageKey = $objectConfiguration->getPackageKey(); if ($packageKey !== NULL) { $properties[$propertyName] = new ConfigurationProperty($propertyName, $packageKey, ConfigurationProperty::PROPERTY_TYPES_SETTING); } } 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') as $propertyName) { if ($this->reflectionService->isPropertyPrivate($className, $propertyName)) { $exceptionMessage = 'The property "' . $propertyName . '" in class "' . $className . '" must not be private when annotated for injection.'; throw new \TYPO3\Flow\Object\Exception($exceptionMessage, 1328109641); } if (!array_key_exists($propertyName, $properties)) { $objectName = trim(implode('', $this->reflectionService->getPropertyTagValues($className, $propertyName, 'var')), ' \\'); $properties[$propertyName] = new ConfigurationProperty($propertyName, $objectName, ConfigurationProperty::PROPERTY_TYPES_OBJECT); } } $objectConfiguration->setProperties($properties); } }
/** * Get the constructor argument reflection for the given object type. * * @param string $className * @return array<array> */ protected function getConstructorArgumentsForClass($className) { if (!isset($this->constructorReflectionFirstLevelCache[$className])) { $constructorSignature = array(); // TODO: Check if we can get rid of this reflection service usage, directly reflecting doesn't work as the proxy class __construct has no arguments. if ($this->reflectionService->hasMethod($className, '__construct')) { $constructorSignature = $this->reflectionService->getMethodParameters($className, '__construct'); } $this->constructorReflectionFirstLevelCache[$className] = $constructorSignature; } return $this->constructorReflectionFirstLevelCache[$className]; }
/** * Detects and registers any validators for arguments: * - by the data type specified in the param annotations * - additional validators specified in the validate annotations of a method * * @param string $className * @param string $methodName * @param array $methodParameters Optional pre-compiled array of method parameters * @param array $methodValidateAnnotations Optional pre-compiled array of validate annotations (as array) * @return array An Array of ValidatorConjunctions for each method parameters. * @throws \TYPO3\Flow\Validation\Exception\InvalidValidationConfigurationException * @throws \TYPO3\Flow\Validation\Exception\NoSuchValidatorException * @throws \TYPO3\Flow\Validation\Exception\InvalidTypeHintException */ public function buildMethodArgumentsValidatorConjunctions($className, $methodName, array $methodParameters = NULL, array $methodValidateAnnotations = NULL) { $validatorConjunctions = array(); if ($methodParameters === NULL) { $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName); } if (count($methodParameters) === 0) { return $validatorConjunctions; } foreach ($methodParameters as $parameterName => $methodParameter) { $validatorConjunction = $this->createValidator('TYPO3\\Flow\\Validation\\Validator\\ConjunctionValidator'); if (!array_key_exists('type', $methodParameter)) { throw new Exception\InvalidTypeHintException('Missing type information, probably no @param annotation for parameter "$' . $parameterName . '" in ' . $className . '->' . $methodName . '()', 1281962564); } if (strpos($methodParameter['type'], '\\') === FALSE) { $typeValidator = $this->createValidator($methodParameter['type']); } elseif (strpos($methodParameter['type'], '\\Model\\') !== FALSE) { $possibleValidatorClassName = str_replace('\\Model\\', '\\Validator\\', $methodParameter['type']) . 'Validator'; $typeValidator = $this->createValidator($possibleValidatorClassName); } else { $typeValidator = NULL; } if ($typeValidator !== NULL) { $validatorConjunction->addValidator($typeValidator); } $validatorConjunctions[$parameterName] = $validatorConjunction; } if ($methodValidateAnnotations === NULL) { $validateAnnotations = $this->reflectionService->getMethodAnnotations($className, $methodName, 'TYPO3\\Flow\\Annotations\\Validate'); $methodValidateAnnotations = array_map(function ($validateAnnotation) { return array('type' => $validateAnnotation->type, 'options' => $validateAnnotation->options, 'argumentName' => $validateAnnotation->argumentName); }, $validateAnnotations); } foreach ($methodValidateAnnotations as $annotationParameters) { $newValidator = $this->createValidator($annotationParameters['type'], $annotationParameters['options']); if ($newValidator === NULL) { throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $annotationParameters['type'] . '".', 1239853109); } if (isset($validatorConjunctions[$annotationParameters['argumentName']])) { $validatorConjunctions[$annotationParameters['argumentName']]->addValidator($newValidator); } elseif (strpos($annotationParameters['argumentName'], '.') !== FALSE) { $objectPath = explode('.', $annotationParameters['argumentName']); $argumentName = array_shift($objectPath); $validatorConjunctions[$argumentName]->addValidator($this->buildSubObjectValidator($objectPath, $newValidator)); } else { throw new Exception\InvalidValidationConfigurationException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Validator specified for argument name "' . $annotationParameters['argumentName'] . '", but this argument does not exist.', 1253172726); } } return $validatorConjunctions; }
/** * Generates the parameters code needed to call the constructor with the saved parameters. * * @param string $className Name of the class the method is declared in * @return string The generated parameters code */ protected function buildSavedConstructorParametersCode($className) { if ($className === NULL) { return ''; } $parametersCode = ''; $methodParameters = $this->reflectionService->getMethodParameters($className, '__construct'); $methodParametersCount = count($methodParameters); if ($methodParametersCount > 0) { foreach ($methodParameters as $methodParameterName => $methodParameterInfo) { $methodParametersCount--; $parametersCode .= '$this->Flow_Aop_Proxy_originalConstructorArguments[\'' . $methodParameterName . '\']' . ($methodParametersCount > 0 ? ', ' : ''); } } return $parametersCode; }
/** * intialize the arguments * */ protected function initializeArguments() { $methodParameters = $this->reflectionService->getMethodParameters($this->class, $this->method); $validators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions($this->class, $this->method); $validationGroups = array('Default', ucfirst($this->method)); foreach ($methodParameters as $name => $methodParameter) { $methodParameterType = $methodParameter['type'] == 'mixed' ? 'string' : $methodParameter['type']; $validator = $validators[$name]; $validator->addValidator($this->validatorResolver->getBaseValidatorConjunction($methodParameterType, $validationGroups)); $argument = new WebserviceCallArgument($name, $methodParameterType); $argument->setPosition($methodParameter['position']); $argument->setRequired(!$methodParameter['optional']); $argument->setDefaultValue($methodParameter['defaultValue']); $argument->setValidator($validator); $this->arguments[$name] = $argument; } }
/** * Initializes the arguments array of this controller by creating an empty argument object for each of the * method arguments found in the designated command method. * * @return void * @throws InvalidArgumentTypeException */ protected function initializeCommandMethodArguments() { $this->arguments->removeAll(); $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->commandMethodName); foreach ($methodParameters as $parameterName => $parameterInfo) { $dataType = null; if (isset($parameterInfo['type'])) { $dataType = $parameterInfo['type']; } elseif ($parameterInfo['array']) { $dataType = 'array'; } if ($dataType === null) { throw new InvalidArgumentTypeException(sprintf('The argument type for parameter $%s of method %s->%s() could not be detected.', $parameterName, get_class($this), $this->commandMethodName), 1306755296); } $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : null; $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue); } }
/** * Builds the PHP code for the parameters of the specified method to be * used in a method interceptor in the proxy class * * @param string $fullClassName Name of the class the method is declared in * @param string $methodName Name of the method to create the parameters code for * @param boolean $addTypeAndDefaultValue If the type and default value for each parameters should be rendered * @return string A comma speparated list of parameters */ public function buildMethodParametersCode($fullClassName, $methodName, $addTypeAndDefaultValue = true) { $methodParametersCode = ''; $methodParameterTypeName = ''; $defaultValue = ''; $byReferenceSign = ''; if ($fullClassName === null || $methodName === null) { return ''; } $methodParameters = $this->reflectionService->getMethodParameters($fullClassName, $methodName); if (count($methodParameters) > 0) { $methodParametersCount = 0; foreach ($methodParameters as $methodParameterName => $methodParameterInfo) { if ($addTypeAndDefaultValue) { if ($methodParameterInfo['array'] === true) { $methodParameterTypeName = 'array'; } elseif ($methodParameterInfo['scalarDeclaration']) { $methodParameterTypeName = $methodParameterInfo['type']; } else { $methodParameterTypeName = $methodParameterInfo['class'] === null ? '' : '\\' . $methodParameterInfo['class']; } if ($methodParameterInfo['optional'] === true) { $rawDefaultValue = isset($methodParameterInfo['defaultValue']) ? $methodParameterInfo['defaultValue'] : null; if ($rawDefaultValue === null) { $defaultValue = ' = NULL'; } elseif (is_bool($rawDefaultValue)) { $defaultValue = $rawDefaultValue ? ' = TRUE' : ' = FALSE'; } elseif (is_numeric($rawDefaultValue)) { $defaultValue = ' = ' . $rawDefaultValue; } elseif (is_string($rawDefaultValue)) { $defaultValue = " = '" . $rawDefaultValue . "'"; } elseif (is_array($rawDefaultValue)) { $defaultValue = ' = ' . $this->buildArraySetupCode($rawDefaultValue); } } $byReferenceSign = $methodParameterInfo['byReference'] ? '&' : ''; } $methodParametersCode .= ($methodParametersCount > 0 ? ', ' : '') . ($methodParameterTypeName ? $methodParameterTypeName . ' ' : '') . $byReferenceSign . '$' . $methodParameterName . $defaultValue; $methodParametersCount++; } } return $methodParametersCode; }
/** * Ext Direct does not provide named arguments by now, so we have * to map them by reflecting on the action parameters. * * @return array The mapped arguments */ public function getArguments() { if ($this->data === array()) { return array(); } $arguments = array(); if (!$this->request->isFormPost()) { $parameters = $this->reflectionService->getMethodParameters($this->getControllerObjectName(), $this->method . 'Action'); // TODO Add checks for parameters foreach ($parameters as $name => $options) { $parameterIndex = $options['position']; if (isset($this->data[$parameterIndex])) { $arguments[$name] = $this->convertObjectToArray($this->data[$parameterIndex]); } } } else { // TODO Reuse setArgumentsFromRawRequestData from Web/RequestBuilder } return $arguments; }
/** * Returns an array of \TYPO3\Flow\Cli\CommandArgumentDefinition that contains * information about required/optional arguments of this command. * If the command does not expect any arguments, an empty array is returned * * @return array<\TYPO3\Flow\Cli\CommandArgumentDefinition> */ public function getArgumentDefinitions() { if (!$this->hasArguments()) { return array(); } $commandArgumentDefinitions = array(); $commandMethodReflection = $this->getCommandMethodReflection(); $annotations = $commandMethodReflection->getTagsValues(); $commandParameters = $this->reflectionService->getMethodParameters($this->controllerClassName, $this->controllerCommandName . 'Command'); $i = 0; foreach ($commandParameters as $commandParameterName => $commandParameterDefinition) { $explodedAnnotation = explode(' ', $annotations['param'][$i]); array_shift($explodedAnnotation); array_shift($explodedAnnotation); $description = implode(' ', $explodedAnnotation); $required = $commandParameterDefinition['optional'] !== TRUE; $commandArgumentDefinitions[] = new CommandArgumentDefinition($commandParameterName, $required, $description); $i++; } return $commandArgumentDefinitions; }
/** * Builds a new instance of $objectType with the given $possibleConstructorArgumentValues. * If constructor argument values are missing from the given array the method looks for a * default value in the constructor signature. * * Furthermore, the constructor arguments are removed from $possibleConstructorArgumentValues * * @param array &$possibleConstructorArgumentValues * @param string $objectType * @return object The created instance * @throws \TYPO3\Flow\Property\Exception\InvalidTargetException if a required constructor argument is missing */ protected function buildObject(array &$possibleConstructorArgumentValues, $objectType) { $className = $this->objectManager->getClassNameByObjectName($objectType); if ($this->reflectionService->hasMethod($className, '__construct')) { $constructorSignature = $this->reflectionService->getMethodParameters($className, '__construct'); $constructorArguments = array(); foreach ($constructorSignature as $constructorArgumentName => $constructorArgumentInformation) { if (array_key_exists($constructorArgumentName, $possibleConstructorArgumentValues)) { $constructorArguments[] = $possibleConstructorArgumentValues[$constructorArgumentName]; unset($possibleConstructorArgumentValues[$constructorArgumentName]); } elseif ($constructorArgumentInformation['optional'] === TRUE) { $constructorArguments[] = $constructorArgumentInformation['defaultValue']; } else { throw new \TYPO3\Flow\Property\Exception\InvalidTargetException('Missing constructor argument "' . $constructorArgumentName . '" for object of type "' . $objectType . '".', 1268734872); } } $classReflection = new \ReflectionClass($className); return $classReflection->newInstanceArgs($constructorArguments); } else { return new $className(); } }
/** * Convert array to change interface * * @param array $changeData * @return ChangeInterface */ protected function convertChangeData($changeData) { $type = $changeData['type']; if (!isset($this->typeMap[$type])) { return new \TYPO3\Flow\Error\Error(sprintf('Could not convert change type %s, it is unknown to the system', $type)); } $changeClass = $this->typeMap[$type]; $changeClassInstance = $this->objectManager->get($changeClass); $changeClassInstance->injectPersistenceManager($this->persistenceManager); $subjectContextPath = $changeData['subject']; $subject = $this->nodeService->getNodeFromContextPath($subjectContextPath); if ($subject instanceof \TYPO3\Flow\Error\Error) { return $subject; } $changeClassInstance->setSubject($subject); if (isset($changeData['reference']) && method_exists($changeClassInstance, 'setReference')) { $referenceContextPath = $changeData['reference']; $reference = $this->nodeService->getNodeFromContextPath($referenceContextPath); if ($reference instanceof \TYPO3\Flow\Error\Error) { return $reference; } $changeClassInstance->setReference($reference); } if (isset($changeData['payload'])) { foreach ($changeData['payload'] as $propertyName => $value) { if (!in_array($propertyName, $this->disallowedPayloadProperties)) { $methodParameters = $this->reflectionService->getMethodParameters($changeClass, ObjectAccess::buildSetterMethodName($propertyName)); $methodParameter = current($methodParameters); $targetType = $methodParameter['type']; $value = $this->propertyMapper->convert($value, $targetType); ObjectAccess::setProperty($changeClassInstance, $propertyName, $value); } } } return $changeClassInstance; }
/** * Register method arguments for "render" by analysing the doc comment above. * * @return void * @throws Parser\Exception */ private function registerRenderMethodArguments() { $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), 'render'); if (count($methodParameters) === 0) { return; } if (Fluid::$debugMode) { $methodTags = $this->reflectionService->getMethodTagsValues(get_class($this), 'render'); $paramAnnotations = array(); if (isset($methodTags['param'])) { $paramAnnotations = $methodTags['param']; } } $i = 0; foreach ($methodParameters as $parameterName => $parameterInfo) { $dataType = NULL; if (isset($parameterInfo['type'])) { $dataType = $parameterInfo['type']; } elseif ($parameterInfo['array']) { $dataType = 'array'; } if ($dataType === NULL) { throw new Parser\Exception('could not determine type of argument "' . $parameterName . '" of the render-method in ViewHelper "' . get_class($this) . '". Either the methods docComment is invalid or some PHP optimizer strips off comments.', 1242292003); } $description = ''; if (Fluid::$debugMode && isset($paramAnnotations[$i])) { $explodedAnnotation = explode(' ', $paramAnnotations[$i]); array_shift($explodedAnnotation); array_shift($explodedAnnotation); $description = implode(' ', $explodedAnnotation); } $defaultValue = NULL; if (isset($parameterInfo['defaultValue'])) { $defaultValue = $parameterInfo['defaultValue']; } $this->argumentDefinitions[$parameterName] = new ArgumentDefinition($parameterName, $dataType, $description, $parameterInfo['optional'] === FALSE, $defaultValue, TRUE); $i++; } }
/** * 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); } }
/** * Renders additional code for the __construct() method of the Proxy Class which realizes constructor injection. * * @param Configuration $objectConfiguration * @return string The built code * @throws \TYPO3\Flow\Object\Exception\UnknownObjectException */ protected function buildConstructorInjectionCode(Configuration $objectConfiguration) { $assignments = array(); $argumentConfigurations = $objectConfiguration->getArguments(); $constructorParameterInfo = $this->reflectionService->getMethodParameters($objectConfiguration->getClassName(), '__construct'); $argumentNumberToOptionalInfo = array(); foreach ($constructorParameterInfo as $parameterInfo) { $argumentNumberToOptionalInfo[$parameterInfo['position'] + 1] = $parameterInfo['optional']; } foreach ($argumentConfigurations as $argumentNumber => $argumentConfiguration) { if ($argumentConfiguration === null) { continue; } $argumentValue = $argumentConfiguration->getValue(); $assignmentPrologue = 'if (!array_key_exists(' . ($argumentNumber - 1) . ', $arguments)) $arguments[' . ($argumentNumber - 1) . '] = '; if ($argumentValue === null && isset($argumentNumberToOptionalInfo[$argumentNumber]) && $argumentNumberToOptionalInfo[$argumentNumber] === true) { $assignments[] = $assignmentPrologue . 'NULL'; } else { switch ($argumentConfiguration->getType()) { case ConfigurationArgument::ARGUMENT_TYPES_OBJECT: if ($argumentValue instanceof Configuration) { $argumentValueObjectName = $argumentValue->getObjectName(); $argumentValueClassName = $argumentValue->getClassName(); if ($argumentValueClassName === null) { $preparedArgument = $this->buildCustomFactoryCall($argumentValue->getFactoryObjectName(), $argumentValue->getFactoryMethodName(), $argumentValue->getArguments()); $assignments[] = $assignmentPrologue . $preparedArgument; } else { if ($this->objectConfigurations[$argumentValueObjectName]->getScope() === Configuration::SCOPE_PROTOTYPE) { $assignments[] = $assignmentPrologue . 'new \\' . $argumentValueObjectName . '(' . $this->buildMethodParametersCode($argumentValue->getArguments()) . ')'; } else { $assignments[] = $assignmentPrologue . '\\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'' . $argumentValueObjectName . '\')'; } } } else { if (strpos($argumentValue, '.') !== false) { $settingPath = explode('.', $argumentValue); $settings = Arrays::getValueByPath($this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS), array_shift($settingPath)); $argumentValue = Arrays::getValueByPath($settings, $settingPath); } if (!isset($this->objectConfigurations[$argumentValue])) { throw new \TYPO3\Flow\Object\Exception\UnknownObjectException('The object "' . $argumentValue . '" which was specified as an argument in the object configuration of object "' . $objectConfiguration->getObjectName() . '" does not exist.', 1264669967); } $assignments[] = $assignmentPrologue . '\\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->get(\'' . $argumentValue . '\')'; } break; case ConfigurationArgument::ARGUMENT_TYPES_STRAIGHTVALUE: $assignments[] = $assignmentPrologue . var_export($argumentValue, true); break; case ConfigurationArgument::ARGUMENT_TYPES_SETTING: $assignments[] = $assignmentPrologue . '\\TYPO3\\Flow\\Core\\Bootstrap::$staticObjectManager->getSettingsByPath(explode(\'.\', \'' . $argumentValue . '\'))'; break; } } } $code = count($assignments) > 0 ? "\n\t\t" . implode(";\n\t\t", $assignments) . ";\n" : ''; $index = 0; foreach ($constructorParameterInfo as $parameterName => $parameterInfo) { if ($parameterInfo['optional'] === true) { break; } if ($objectConfiguration->getScope() === Configuration::SCOPE_SINGLETON) { $code .= ' if (!array_key_exists(' . $index . ', $arguments)) throw new \\TYPO3\\Flow\\Object\\Exception\\UnresolvedDependenciesException(\'Missing required constructor argument $' . $parameterName . ' in class \' . __CLASS__ . \'. Please check your calling code and Dependency Injection configuration.\', 1296143787);' . "\n"; } else { $code .= ' if (!array_key_exists(' . $index . ', $arguments)) throw new \\TYPO3\\Flow\\Object\\Exception\\UnresolvedDependenciesException(\'Missing required constructor argument $' . $parameterName . ' in class \' . __CLASS__ . \'. Note that constructor injection is only support for objects of scope singleton (and this is not a singleton) – for other scopes you must pass each required argument to the constructor yourself.\', 1296143788);' . "\n"; } $index++; } return $code; }