>> */ protected array $typeConverters = []; /** * Used in the TypeConverterPass only. * * @param array|string[] $sources * @throws Exception\DuplicateTypeConverterException * @internal */ public function add(TypeConverterInterface $converter, int $priority, array $sources, string $target): void { foreach ($sources as $source) { if (isset($this->typeConverters[$source][$target][$priority])) { throw new Exception\DuplicateTypeConverterException( sprintf( 'There exist at least two type converters which handle the conversion from "%s" to "%s" with priority "%d": %s and %s', $source, $target, $priority, get_class($this->typeConverters[$source][$target][$priority]), get_class($converter) ), 1297951378 ); } $this->typeConverters[$source][$target][$priority] = $converter; } } /** * @throws Exception\DuplicateTypeConverterException * @throws Exception\InvalidTargetException * @throws Exception\TypeConverterException */ public function findTypeConverter(string $sourceType, string $targetType): TypeConverterInterface { $converter = null; if (TypeHandlingUtility::isSimpleType($targetType)) { if (isset($this->typeConverters[$sourceType][$targetType])) { $converter = $this->findEligibleConverterWithHighestPriority($this->typeConverters[$sourceType][$targetType]); } } else { $converter = $this->findFirstEligibleTypeConverterInObjectHierarchy($sourceType, $targetType); } if ($converter === null) { throw new Exception\TypeConverterException( 'No converter found which can be used to convert from "' . $sourceType . '" to "' . $targetType . '".', 1476044883 ); } return $converter; } /** * Tries to find a suitable type converter for the given source type and target type. * * @param string $sourceType Type of the source to convert from * @param class-string $targetClass Name of the target class to find a type converter for * * * @throws Exception\InvalidTargetException * @throws Exception\DuplicateTypeConverterException */ protected function findFirstEligibleTypeConverterInObjectHierarchy(string $sourceType, string $targetClass): ?TypeConverterInterface { if (!class_exists($targetClass) && !interface_exists($targetClass)) { throw new Exception\InvalidTargetException('Could not find a suitable type converter for "' . $targetClass . '" because no such class or interface exists.', 1297948764); } if (!isset($this->typeConverters[$sourceType])) { return null; } $convertersForSource = $this->typeConverters[$sourceType]; if (isset($convertersForSource[$targetClass])) { $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$targetClass]); if ($converter !== null) { return $converter; } } foreach (class_parents($targetClass) as $parentClass) { if (!isset($convertersForSource[$parentClass])) { continue; } $converter = $this->findEligibleConverterWithHighestPriority($convertersForSource[$parentClass]); if ($converter !== null) { return $converter; } } $implementedInterface = class_implements($targetClass); /** @var array $implementedInterface */ $implementedInterface = $implementedInterface === false ? [] : $implementedInterface; $implementedInterface = array_keys($implementedInterface); $converters = $this->getConvertersForInterfaces($convertersForSource, $implementedInterface); $converter = $this->findEligibleConverterWithHighestPriority($converters); if ($converter !== null) { return $converter; } if (isset($convertersForSource['object'])) { return $this->findEligibleConverterWithHighestPriority($convertersForSource['object']); } return null; } /** * @param array $converters */ protected function findEligibleConverterWithHighestPriority(array $converters): ?TypeConverterInterface { if ($converters === []) { return null; } krsort($converters, SORT_NUMERIC); reset($converters); return current($converters); } /** * @param array> $convertersForSource * @param class-string[] $interfaceNames * * @return TypeConverterInterface[] * * @throws Exception\DuplicateTypeConverterException */ protected function getConvertersForInterfaces(array $convertersForSource, array $interfaceNames): array { $convertersForInterface = []; foreach ($interfaceNames as $implementedInterface) { if (isset($convertersForSource[$implementedInterface])) { foreach ($convertersForSource[$implementedInterface] as $priority => $converter) { if (isset($convertersForInterface[$priority])) { throw new Exception\DuplicateTypeConverterException( sprintf( 'There exist at least two converters which handle the conversion to an interface with priority "%d". %s and %s', $priority, get_class($convertersForInterface[$priority]), get_class($converter) ), 1297951338 ); } $convertersForInterface[$priority] = $converter; } } } return $convertersForInterface; } }