Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
81.67% covered (warning)
81.67%
49 / 60
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConverterUtils
81.67% covered (warning)
81.67%
49 / 60
33.33% covered (danger)
33.33%
2 / 6
38.31
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
 isStaticOrSelf
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
8.05
 toReflectionClass
46.67% covered (danger)
46.67%
7 / 15
0.00% covered (danger)
0.00%
0 / 1
25.17
 toReflectionType
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
3.33
 dynamicCast
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
8.05
 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     * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod|null $input
52     */
53    public static function isStaticOrSelf(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod|null $input): bool
54    {
55        if (is_string($input)) {
56            return $input === 'self' || $input === 'static';
57        }
58        if ($input instanceof ReflectionProperty) {
59            return self::isStaticOrSelf($input->getType());
60        }
61        if ($input instanceof ReflectionNamedType) {
62            return self::isStaticOrSelf($input->getName());
63        }
64        if ($input instanceof \ReflectionIntersectionType || $input instanceof \ReflectionUnionType) {
65            return false;
66        }
67        if ($input instanceof ReflectionType) {
68            return self::isStaticOrSelf((string) $input);
69        }
70        return false;
71    }
72
73    /**
74     * @template T of object
75     * @param string|ReflectionClass<T>|ReflectionProperty|ReflectionType|ReflectionMethod|null $input
76     * @param ReflectionMethod|ReflectionClass<T>|null $context
77     * @return ReflectionClass<T>|null
78     */
79    public static function toReflectionClass(
80        string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod|null $input,
81        bool $strict = false,
82        ReflectionMethod|ReflectionClass|null $context = null
83    ): ?ReflectionClass {
84        if ($input === null) {
85            return null;
86        }
87        if ($input instanceof ReflectionClass) {
88            return $input;
89        }
90        if (self::isStaticOrSelf($input)) {
91            if ($context instanceof ReflectionMethod) {
92                return $context->getDeclaringClass();
93            }
94            if ($context instanceof ReflectionClass) {
95                return $context;
96            }
97            if ($input instanceof ReflectionMethod) {
98                return $input->getDeclaringClass();
99            }
100            throw new \LogicException('I can not handle static without a proper context of usage');
101        }
102        if (is_string($input) && !class_exists($input)) {
103            return null;
104        }
105        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionClass' : '?ReflectionClass');
106    }
107
108    /**
109     * @param string|ReflectionClass<object>|ReflectionProperty|ReflectionType|ReflectionMethod $input
110     */
111    public static function toReflectionType(string|ReflectionClass|ReflectionProperty|ReflectionType|ReflectionMethod $input, bool $strict = false): ?ReflectionType
112    {
113        if ($input instanceof ReflectionType) {
114            return $input;
115        }
116        return self::getInstance()->typeConverter->convertTo($input, $strict ? 'ReflectionType' : '?ReflectionType');
117    }
118
119    public static function dynamicCast(mixed $input, ReflectionType $wantedType): mixed
120    {
121        if ($input instanceof MixedStorageInterface) {
122            $input = $input->toOriginalObject();
123        }
124        if ($input === null && $wantedType->allowsNull()) {
125            return null;
126        }
127        if (is_object($input)) {
128            $class = self::toReflectionClass($wantedType);
129            if ($class->isInstance($input)) {
130                return $input;
131            }
132        } elseif ($wantedType instanceof ReflectionNamedType && $wantedType->getName() === get_debug_type($input)) {
133            return $input;
134        }
135        return self::getInstance()->typeConverter->convertTo($input, $wantedType);
136    }
137
138    private static function getInstance(): self
139    {
140        if (!isset(self::$instance)) {
141            self::$instance = new self();
142        }
143
144        return self::$instance;
145    }
146}