Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.18% covered (warning)
87.18%
34 / 39
40.00% covered (danger)
40.00%
2 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConverterUtils
87.18% covered (warning)
87.18%
34 / 39
40.00% covered (danger)
40.00%
2 / 5
19.76
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
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
5.20
 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 $input
53     * @return ReflectionClass<T>
54     */
55    public static function toReflectionClass(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input, bool $strict = false): ?ReflectionClass
56    {
57        if ($input instanceof ReflectionClass) {
58            return $input;
59        }
60        if (is_string($input) && !class_exists($input)) {
61            return null;
62        }
63        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionClass' : '?ReflectionClass');
64    }
65
66    /**
67     * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input
68     */
69    public static function toReflectionType(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input, bool $strict = false): ?ReflectionType
70    {
71        if ($input instanceof ReflectionType) {
72            return $input;
73        }
74        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionType' : '?ReflectionType');
75    }
76
77    public static function dynamicCast(mixed $input, ReflectionType $wantedType): mixed
78    {
79        if ($input instanceof MixedStorageInterface) {
80            $input = $input->toOriginalObject();
81        }
82        if ($input === null && $wantedType->allowsNull()) {
83            return null;
84        }
85        if (is_object($input)) {
86            $class = self::toReflectionClass($wantedType);
87            if ($class->isInstance($input)) {
88                return $input;
89            }
90        } elseif ($wantedType instanceof ReflectionNamedType && $wantedType->getName() === get_debug_type($input)) {
91            return $input;
92        }
93        return self::getInstance()->typeConverter->convertTo($input, $wantedType);
94    }
95
96    private static function getInstance(): self
97    {
98        if (!isset(self::$instance)) {
99            self::$instance = new self();
100        }
101
102        return self::$instance;
103    }
104}