/**
  * @return string
  */
 public function getLabel()
 {
     $arguments = [];
     foreach ($this->arguments as $argumentValue) {
         if (TypeHandling::isSimpleType($argumentValue)) {
             $arguments[] = $argumentValue;
         } else {
             $arguments[] = '[' . gettype($argumentValue) . ']';
         }
     }
     return sprintf('%s::%s(%s)', $this->className, $this->methodName, implode(', ', $arguments));
 }
 /**
  * Determine the type converter to be used. If no converter has been found, an exception is raised.
  *
  * @param mixed $source
  * @param string $targetType
  * @param PropertyMappingConfigurationInterface $configuration
  * @return TypeConverterInterface Type Converter which should be used to convert between $source and $targetType.
  * @throws Exception\TypeConverterException
  * @throws Exception\InvalidTargetException
  */
 protected function findTypeConverter($source, $targetType, PropertyMappingConfigurationInterface $configuration)
 {
     if ($configuration->getTypeConverter() !== null) {
         return $configuration->getTypeConverter();
     }
     if (!is_string($targetType)) {
         throw new Exception\InvalidTargetException('The target type was no string, but of type "' . gettype($targetType) . '"', 1297941727);
     }
     $normalizedTargetType = TypeHandling::normalizeType($targetType);
     $truncatedTargetType = TypeHandling::truncateElementType($normalizedTargetType);
     $converter = null;
     $sourceTypes = $this->determineSourceTypes($source);
     foreach ($sourceTypes as $sourceType) {
         if (TypeHandling::isSimpleType($truncatedTargetType)) {
             if (isset($this->typeConverters[$sourceType][$truncatedTargetType])) {
                 $converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$truncatedTargetType], $source, $normalizedTargetType);
             }
         } else {
             $converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($source, $sourceType, $normalizedTargetType);
         }
         if ($converter !== null) {
             return $converter;
         }
     }
     throw new Exception\TypeConverterException('No converter found which can be used to convert from "' . implode('" or "', $sourceTypes) . '" to "' . $normalizedTargetType . '".');
 }
 /**
  * Builds a base validator conjunction for the given data type.
  *
  * The base validation rules are those which were declared directly in a class (typically
  * a model) through some validate annotations on properties.
  *
  * If a property holds a class for which a base validator exists, that property will be
  * checked as well, regardless of a validate annotation
  *
  * Additionally, if a custom validator was defined for the class in question, it will be added
  * to the end of the conjunction. A custom validator is found if it follows the naming convention
  * "Replace '\Model\' by '\Validator\' and append 'Validator'".
  *
  * Example: $targetClassName is Neos\Foo\Domain\Model\Quux, then the validator will be found if it has the
  * name Neos\Foo\Domain\Validator\QuuxValidator
  *
  * @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups
  * @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name.
  * @param array $validationGroups The validation groups to build the validator for
  * @return void
  * @throws Exception\NoSuchValidatorException
  * @throws \InvalidArgumentException
  */
 protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups)
 {
     $conjunctionValidator = new ConjunctionValidator();
     $this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator;
     if (!TypeHandling::isSimpleType($targetClassName) && class_exists($targetClassName)) {
         // Model based validator
         $classSchema = $this->reflectionService->getClassSchema($targetClassName);
         if ($classSchema !== null && $classSchema->isAggregateRoot()) {
             $objectValidator = new AggregateBoundaryValidator(array());
         } else {
             $objectValidator = new GenericObjectValidator([]);
         }
         $conjunctionValidator->addValidator($objectValidator);
         foreach ($this->reflectionService->getClassPropertyNames($targetClassName) as $classPropertyName) {
             $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($targetClassName, $classPropertyName);
             if (!isset($classPropertyTagsValues['var'])) {
                 throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
             }
             try {
                 $parsedType = TypeHandling::parseType(trim(implode('', $classPropertyTagsValues['var']), ' \\'));
             } catch (InvalidTypeException $exception) {
                 throw new \InvalidArgumentException(sprintf(' @var annotation of ' . $exception->getMessage(), 'class "' . $targetClassName . '", property "' . $classPropertyName . '"'), 1315564744, $exception);
             }
             if ($this->reflectionService->isPropertyAnnotatedWith($targetClassName, $classPropertyName, Flow\IgnoreValidation::class)) {
                 continue;
             }
             $propertyTargetClassName = $parsedType['type'];
             if (TypeHandling::isCollectionType($propertyTargetClassName) === true) {
                 $collectionValidator = $this->createValidator(Validator\CollectionValidator::class, ['elementType' => $parsedType['elementType'], 'validationGroups' => $validationGroups]);
                 $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
             } elseif (!TypeHandling::isSimpleType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === Configuration::SCOPE_PROTOTYPE) {
                 $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName, $validationGroups);
                 if (count($validatorForProperty) > 0) {
                     $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
                 }
             }
             $validateAnnotations = $this->reflectionService->getPropertyAnnotations($targetClassName, $classPropertyName, Flow\Validate::class);
             foreach ($validateAnnotations as $validateAnnotation) {
                 if (count(array_intersect($validateAnnotation->validationGroups, $validationGroups)) === 0) {
                     // In this case, the validation groups for the property do not match current validation context
                     continue;
                 }
                 $newValidator = $this->createValidator($validateAnnotation->type, $validateAnnotation->options);
                 if ($newValidator === null) {
                     throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for  validator "' . $validateAnnotation->type . '".', 1241098027);
                 }
                 $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
             }
         }
         if (count($objectValidator->getPropertyValidators()) === 0) {
             $conjunctionValidator->removeValidator($objectValidator);
         }
     }
     $this->addCustomValidators($targetClassName, $conjunctionValidator);
 }
Esempio n. 4
0
 /**
  * Expand shortened class names in "var" and "param" annotations, taking use statements into account.
  *
  * @param ClassReflection $class
  * @param string $type the type inside var/param annotation
  * @return string the possibly expanded type
  */
 protected function expandType(ClassReflection $class, $type)
 {
     // expand "SomeType<SomeElementType>" to "\SomeTypeNamespace\SomeType<\ElementTypeNamespace\ElementType>"
     if (strpos($type, '<') !== false) {
         $typeParts = explode('<', $type);
         $type = $typeParts[0];
         $elementType = rtrim($typeParts[1], '>');
         return $this->expandType($class, $type) . '<' . $this->expandType($class, $elementType) . '>';
     }
     // skip simple types and types with fully qualified namespaces
     if ($type === 'mixed' || $type[0] === '\\' || TypeHandling::isSimpleType($type)) {
         return TypeHandling::normalizeType($type);
     }
     // we try to find the class relative to the current namespace...
     $possibleFullyQualifiedClassName = sprintf('%s\\%s', $class->getNamespaceName(), $type);
     if (class_exists($possibleFullyQualifiedClassName) || interface_exists($possibleFullyQualifiedClassName)) {
         return $possibleFullyQualifiedClassName;
     }
     // and then we try to find "use" statements for the class.
     $className = $class->getName();
     if (!isset($this->useStatementsForClassCache[$className])) {
         $this->useStatementsForClassCache[$className] = $this->getDoctrinePhpParser()->parseClass($class);
     }
     $useStatementsForClass = $this->useStatementsForClassCache[$className];
     // ... and try to expand them
     $typeParts = explode('\\', $type, 2);
     $lowercasedFirstTypePart = strtolower($typeParts[0]);
     if (isset($useStatementsForClass[$lowercasedFirstTypePart])) {
         $typeParts[0] = $useStatementsForClass[$lowercasedFirstTypePart];
         return implode('\\', $typeParts);
     }
     return $type;
 }
 /**
  * Convert a value to the internal object data format
  *
  * @param string $identifier The object's identifier
  * @param object $object The object with the property to flatten
  * @param string $propertyName The name of the property
  * @param array $propertyMetaData The property metadata
  * @param array $propertyData Reference to the property data array
  * @return void
  * @api
  */
 protected function flattenValue($identifier, $object, $propertyName, array $propertyMetaData, array &$propertyData)
 {
     $propertyValue = ObjectAccess::getProperty($object, $propertyName, true);
     if ($propertyValue instanceof PersistenceMagicInterface) {
         $propertyData[$propertyName] = ['type' => get_class($propertyValue), 'multivalue' => false, 'value' => $this->processObject($propertyValue, $identifier)];
     } else {
         switch ($propertyMetaData['type']) {
             case 'DateTime':
                 $propertyData[$propertyName] = ['multivalue' => false, 'value' => $this->processDateTime($propertyValue)];
                 break;
             case Collection::class:
             case ArrayCollection::class:
                 $propertyValue = $propertyValue === null ? [] : $propertyValue->toArray();
             case 'array':
                 $propertyData[$propertyName] = ['multivalue' => true, 'value' => $this->processArray($propertyValue, $identifier, $this->persistenceSession->getCleanStateOfProperty($object, $propertyName))];
                 break;
             case 'SplObjectStorage':
                 $propertyData[$propertyName] = ['multivalue' => true, 'value' => $this->processSplObjectStorage($propertyValue, $identifier, $this->persistenceSession->getCleanStateOfProperty($object, $propertyName))];
                 break;
             default:
                 if ($propertyValue === null && !TypeHandling::isSimpleType($propertyMetaData['type'])) {
                     $this->removeDeletedReference($object, $propertyName, $propertyMetaData);
                 }
                 $propertyData[$propertyName] = ['multivalue' => false, 'value' => $propertyValue];
                 break;
         }
         $propertyData[$propertyName]['type'] = $propertyMetaData['type'];
     }
 }
 /**
  * @param mixed $propertyValue
  * @param string $dataType
  * @return mixed
  * @throws PropertyException
  */
 protected function convertValue($propertyValue, $dataType)
 {
     $rawType = TypeHandling::truncateElementType($dataType);
     // This hardcoded handling is to circumvent rewriting PropertyMappers that convert objects. Usually they expect the source to be an object already and break if not.
     if (!TypeHandling::isSimpleType($rawType) && !is_object($propertyValue) && !is_array($propertyValue)) {
         return null;
     }
     if ($rawType === 'array') {
         $conversionTargetType = 'array<string>';
     } elseif (TypeHandling::isSimpleType($rawType)) {
         $conversionTargetType = TypeHandling::normalizeType($rawType);
     } else {
         $conversionTargetType = 'array';
     }
     $propertyMappingConfiguration = $this->createConfiguration($dataType);
     $convertedValue = $this->propertyMapper->convert($propertyValue, $conversionTargetType, $propertyMappingConfiguration);
     if ($convertedValue instanceof \Neos\Error\Messages\Error) {
         throw new PropertyException($convertedValue->getMessage(), $convertedValue->getCode());
     }
     return $convertedValue;
 }