Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.37% covered (warning)
85.37%
35 / 41
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConverterUtils
85.37% covered (warning)
85.37%
35 / 41
40.00% covered (danger)
40.00%
2 / 5
21.25
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
1
 toReflectionClass
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
6.84
 toReflectionType
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 dynamicCast
72.73% covered (warning)
72.73%
8 / 11
0.00% covered (danger)
0.00%
0 / 1
9.30
 getInstance
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2namespace Apie\Core\Utils;
3
4use Apie\Core\TypeConverters\ArrayToDoctrineCollection;
5use Apie\Core\TypeConverters\DoctrineCollectionToArray;
6use Apie\Core\TypeConverters\IntToAutoincrementIntegerConverter;
7use Apie\Core\TypeConverters\ReflectionClassToReflectionTypeConverter;
8use Apie\Core\TypeConverters\ReflectionMethodToReflectionClassConverter;
9use Apie\Core\TypeConverters\ReflectionPropertyToReflectionClassConverter;
10use Apie\Core\TypeConverters\ReflectionTypeToReflectionClassConverter;
11use Apie\Core\TypeConverters\ReflectionUnionTypeToReflectionClassConverter;
12use Apie\Core\TypeConverters\StringToReflectionClassConverter;
13use Apie\StorageMetadataBuilder\Interfaces\MixedStorageInterface;
14use Apie\TypeConverter\Converters\ObjectToObjectConverter;
15use Apie\TypeConverter\DefaultConvertersFactory;
16use Apie\TypeConverter\TypeConverter;
17use ReflectionClass;
18use ReflectionMethod;
19use ReflectionNamedType;
20use ReflectionProperty;
21use ReflectionType;
22
23final class ConverterUtils
24{
25    private static self $instance;
26
27    private TypeConverter $typeConverter;
28
29    private function __construct()
30    {
31        $converters = [
32            new ArrayToDoctrineCollection(),
33            new DoctrineCollectionToArray(),
34            new IntToAutoincrementIntegerConverter(),
35            new StringToReflectionClassConverter(),
36            new ReflectionMethodToReflectionClassConverter(),
37            new ReflectionPropertyToReflectionClassConverter(),
38            new ReflectionTypeToReflectionClassConverter(),
39            new ReflectionUnionTypeToReflectionClassConverter(),
40            new ReflectionClassToReflectionTypeConverter(),
41        ];
42        $this->typeConverter = new TypeConverter(
43            new ObjectToObjectConverter(),
44            ...DefaultConvertersFactory::create(
45                ...$converters
46            )
47        );
48    }
49
50    /**
51     * @template T of object
52     * @param string|ReflectionClass<T>|ReflectionProperty|ReflectionType|ReflectionMethod|null $input
53     * @return ReflectionClass<T>|null
54     */
55    public static function toReflectionClass(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod|null $input, bool $strict = false): ?ReflectionClass
56    {
57        if ($input === null) {
58            return null;
59        }
60        if ($input instanceof ReflectionClass) {
61            return $input;
62        }
63        if (is_string($input) && !class_exists($input)) {
64            return null;
65        }
66        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionClass' : '?ReflectionClass');
67    }
68
69    /**
70     * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input
71     */
72    public static function toReflectionType(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input, bool $strict = false): ?ReflectionType
73    {
74        if ($input instanceof ReflectionType) {
75            return $input;
76        }
77        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionType' : '?ReflectionType');
78    }
79
80    public static function dynamicCast(mixed $input, ReflectionType $wantedType): mixed
81    {
82        if ($input instanceof MixedStorageInterface) {
83            $input = $input->toOriginalObject();
84        }
85        if ($input === null && $wantedType->allowsNull()) {
86            return null;
87        }
88        if (is_object($input)) {
89            $class = self::toReflectionClass($wantedType);
90            if ($class->isInstance($input)) {
91                return $input;
92            }
93        } elseif ($wantedType instanceof ReflectionNamedType && $wantedType->getName() === get_debug_type($input)) {
94            return $input;
95        }
96        return self::getInstance()->typeConverter->convertTo($input, $wantedType);
97    }
98
99    private static function getInstance(): self
100    {
101        if (!isset(self::$instance)) {
102            self::$instance = new self();
103        }
104
105        return self::$instance;
106    }
107}