/** * 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 . '".'); }
/** * Constructs this controller argument * * @param string $name Name of this argument * @param string $dataType The data type of this argument * @throws \InvalidArgumentException if $name is not a string or empty * @api */ public function __construct($name, $dataType) { if (!is_string($name)) { throw new \InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688); } if (strlen($name) === 0) { throw new \InvalidArgumentException('$name must be a non-empty string, ' . strlen($name) . ' characters given.', 1232551853); } $this->name = $name; $this->dataType = TypeHandling::normalizeType($dataType); }
/** * @param string $operand * @param string $value * @return boolean TRUE if $value is of type $operand; FALSE otherwise */ protected function handleSimpleTypeOperand($operand, $value) { $operand = TypeHandling::normalizeType($operand); if ($operand === 'object') { return is_object($value); } elseif ($operand === 'string') { return is_string($value); } elseif ($operand === 'integer') { return is_integer($value); } elseif ($operand === 'boolean') { return is_bool($value); } elseif ($operand === 'float') { return is_float($value); } elseif ($operand === 'array') { return is_array($value); } return false; }
/** * @test * @dataProvider normalizeTypes */ public function normalizeTypesReturnsNormalizedType($type, $normalized) { $this->assertEquals(TypeHandling::normalizeType($type), $normalized); }
/** * 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; }
/** * @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; }