DefinitionAssistant::ObtainExpectedProperties()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 1
dl 0
loc 18
ccs 10
cts 10
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
* @author SignpostMarv
4
*/
5
declare(strict_types=1);
6
7
namespace SignpostMarv\DaftMagicPropertyAnalysis;
8
9
use Closure;
10
use InvalidArgumentException;
11
12
/**
13
* @template T as object
14
*/
15
class DefinitionAssistant
16
{
17
    const ARG_INDEX_CLOSURE_GETTER = 2;
18
19
    const ARG_INDEX_CLOSURE_SETTER = 3;
20
21
    const IN_ARRAY_STRICT_MODE = true;
22
23
    const COUNT_EXPECTED_REQUIRED_PARAMETERS = 1;
24
25
    const PARAM_INDEX_FIRST = 0;
26
27
    const BOOL_IS_PARAM = true;
28
29
    const BOOL_IS_RETURN = false;
30
31
    /**
32
    * @var array<string, array<int, string>>
33
    *
34
    * @psalm-var array<class-string<T>, array<int, string>>
35
    */
36
    protected static $properties = [];
37
38
    /**
39
    * @var array<string, Closure>
40
    *
41
    * @psalm-var array<class-string<T>, Closure(string):?string>
42
    */
43
    protected static $getters = [];
44
45
    /**
46
    * @var array<string, Closure>
47
    *
48
    * @psalm-var array<class-string<T>, Closure(string):?string>
49
    */
50
    protected static $setters = [];
51
52
    /**
53
    * @psalm-param class-string<T> $type
54
    */
55 24
    public static function IsTypeUnregistered(string $type) : bool
56
    {
57 24
        return ! isset(static::$properties[$type]);
58
    }
59
60
    /**
61
    * @psalm-param class-string<T> $type
62
    * @psalm-param null|Closure(string):?string $getter
63
    * @psalm-param null|Closure(string):?string $setter
64
    */
65 24
    public static function RegisterType(
66
        string $type,
67
        ? Closure $getter,
68
        ? Closure $setter,
69
        string $property,
70
        string ...$properties
71
    ) : void {
72 24
        if ( ! self::IsTypeUnregistered($type)) {
73 4
            throw new InvalidArgumentException(
74
                'Argument 1 passed to ' .
75
                __METHOD__ .
76 4
                '() has already been registered!'
77
            );
78 24
        } elseif (is_null($getter) && is_null($setter)) {
79 4
            throw new InvalidArgumentException(
80 4
                'One or both of arguments 2 and 3 must be specified!'
81
            );
82
        }
83
84 20
        array_unshift($properties, $property);
85
86 20
        static::MaybeRegisterTypeGetter($type, $getter);
87 16
        static::MaybeRegisterTypeSetter($type, $setter);
0 ignored issues
show
Bug introduced by
$type of type object is incompatible with the type string expected by parameter $type of SignpostMarv\DaftMagicPr...ybeRegisterTypeSetter(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
        static::MaybeRegisterTypeSetter(/** @scrutinizer ignore-type */ $type, $setter);
Loading history...
88
89 12
        static::$properties[$type] = $properties;
90 12
    }
91
92
    /**
93
    * @psalm-param class-string<T> $type
94
    */
95 8
    public static function GetterMethodName(string $type, string $property) : ? string
96
    {
97
        if (
98 8
            in_array($property, static::$properties[$type] ?? [], self::IN_ARRAY_STRICT_MODE) &&
99 8
            isset(static::$getters[$type])
100
        ) {
101 8
            return static::$getters[$type]($property);
102
        }
103
104 8
        return self::CheckOtherTypes(self::$getters, $type, $property);
105
    }
106
107
    /**
108
    * @psalm-param class-string<T> $type
109
    */
110 8
    public static function SetterMethodName(string $type, string $property) : ? string
111
    {
112
        if (
113 8
            in_array($property, static::$properties[$type] ?? [], self::IN_ARRAY_STRICT_MODE) &&
114 8
            isset(static::$setters[$type])
115
        ) {
116 8
            return static::$setters[$type]($property);
117
        }
118
119 8
        return self::CheckOtherTypes(self::$setters, $type, $property);
120
    }
121
122
    /**
123
    * @param string|object $maybe
124
    *
125
    * @psalm-param class-string<T>|T $maybe
126
    *
127
    * @return array<int, string>
128
    */
129 12
    public static function ObtainExpectedProperties($maybe) : array
130
    {
131
        /**
132
        * @var array<int, string>
133
        */
134 12
        $out = array_values(array_unique(array_reduce(
135 9
            array_filter(
136 9
                static::$properties,
137
                function (string $type) use ($maybe) : bool {
138 12
                    return is_a($maybe, $type, is_string($maybe));
139 12
                },
140 12
                ARRAY_FILTER_USE_KEY
141
            ),
142 12
            'array_merge',
143 12
            []
144
        )));
145
146 12
        return $out;
147
    }
148
149
    /**
150
    * @param array<string, Closure> $otherTypes
151
    *
152
    * @psalm-param array<class-string<T>, Closure(string):?string> $otherTypes
153
    * @psalm-param class-string<T> $type
154
    */
155 12
    protected static function CheckOtherTypes(
156
        array $otherTypes,
157
        string $type,
158
        string $property
159
    ) : ? string {
160 12
        foreach ($otherTypes as $otherType => $getter) {
161
            if (
162 12
                $otherType !== $type &&
163 11
                isset(self::$properties[$otherType]) &&
164 11
                in_array($property, self::$properties[$otherType], self::IN_ARRAY_STRICT_MODE)
165
            ) {
166 11
                return $getter($property);
167
            }
168
        }
169
170 8
        return null;
171
    }
172
173
    /**
174
    * @psalm-param class-string<T> $type
175
    * @psalm-param null|Closure(string):?string $getter
176
    */
177 20
    private static function MaybeRegisterTypeGetter(string $type, ? Closure $getter) : void
178
    {
179 20
        if ( ! is_null($getter)) {
180 16
            if ( ! method_exists($type, '__get')) {
181 4
                throw new InvalidArgumentException(
182
                    'Argument 1 passed to ' .
183
                    __CLASS__ .
184 4
                    '::RegisterType() must declare __get() !'
185
                );
186
            }
187
188 12
            self::$getters[$type] = $getter;
189
        }
190 16
    }
191
192
    /**
193
    * @psalm-param class-string<T> $type
194
    * @psalm-param null|Closure(string):?string $setter
195
    */
196 16
    private static function MaybeRegisterTypeSetter(string $type, ? Closure $setter) : void
197
    {
198 16
        if ( ! is_null($setter)) {
199 16
            if ( ! method_exists($type, '__set')) {
200 4
                throw new InvalidArgumentException(
201
                    'Argument 1 passed to ' .
202
                    __CLASS__ .
203 4
                    '::RegisterType() must declare __set() !'
204
                );
205
            }
206
207 12
            self::$setters[$type] = $setter;
208
        }
209 12
    }
210
}
211