GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( a6fddc...bed791 )
by SignpostMarv
02:44
created

TypeUtilities::EnsureArgumentIsString()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 2
nop 1
dl 0
loc 12
rs 10
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 3
1
<?php
2
/**
3
* @author SignpostMarv
4
*/
5
declare(strict_types=1);
6
7
namespace SignpostMarv\DaftObject;
8
9
use InvalidArgumentException;
10
use ReflectionException;
11
use ReflectionMethod;
12
13
class TypeUtilities
14
{
15
    const BOOL_EXPECTING_NON_PUBLIC_METHOD = false;
16
17
    const BOOL_EXPECTING_GETTER = false;
18
19
    const BOOL_DEFAULT_THROWIFNOTIMPLEMENTATION = false;
20
21
    const BOOL_DEFAULT_EXPECTING_NON_PUBLIC_METHOD = true;
22
23
    const BOOL_METHOD_IS_PUBLIC = true;
24
25
    const BOOL_METHOD_IS_NON_PUBLIC = false;
26
27
    const SUPPORTED_INVALID_LEADING_CHARACTERS = [
28
        '@',
29
    ];
30
31
    /**
32
    * @var array<string, array<string, bool>>
33
    */
34
    private static $Getters = [];
35
36
    /**
37
    * @var array<string, array<int, string>>
38
    */
39
    private static $publicSetters = [];
40
41 118
    public static function DaftObjectPublicGetters(string $class) : array
42
    {
43 118
        static::CachePublicGettersAndSetters($class);
44
45 118
        return array_keys(array_filter(self::$Getters[$class]));
46
    }
47
48 2
    public static function DaftObjectPublicOrProtectedGetters(string $class) : array
49
    {
50 2
        static::CachePublicGettersAndSetters($class);
51
52 2
        return array_keys(self::$Getters[$class]);
53
    }
54
55 186
    public static function DaftObjectPublicSetters(string $class) : array
56
    {
57 186
        static::CachePublicGettersAndSetters($class);
58
59 186
        return self::$publicSetters[$class];
60
    }
61
62 262
    public static function MethodNameFromProperty(string $prop, bool $SetNotGet = false) : string
63
    {
64 262
        if (TypeParanoia::MaybeInArray(mb_substr($prop, 0, 1), self::SUPPORTED_INVALID_LEADING_CHARACTERS)) {
65 2
            return ($SetNotGet ? 'Alter' : 'Obtain') . ucfirst(mb_substr($prop, 1));
66
        }
67
68 260
        return ($SetNotGet ? 'Set' : 'Get') . ucfirst($prop);
69
    }
70
71
    /**
72
    * Checks if a type correctly defines it's own id.
73
    *
74
    * @throws ClassDoesNotImplementClassException if $class is not an implementation of DefinesOwnIdPropertiesInterface
75
    * @throws ClassMethodReturnHasZeroArrayCountException if $class::DaftObjectIdProperties() does not contain at least one property
76
    * @throws ClassMethodReturnIsNotArrayOfStringsException if $class::DaftObjectIdProperties() is not string[]
77
    * @throws UndefinedPropertyException if an id property is not in $class::DaftObjectIdProperties()
78
    */
79 528
    public static function CheckTypeDefinesOwnIdProperties(
80
        string $class,
81
        bool $throwIfNotImplementation = self::BOOL_DEFAULT_THROWIFNOTIMPLEMENTATION
82
    ) : void {
83 528
        if (TypeParanoia::IsThingStrings($class, DefinesOwnIdPropertiesInterface::class)) {
84 314
            self::CheckTypeDefinesOwnIdPropertiesIsImplementation($class);
85 220
        } elseif ($throwIfNotImplementation) {
86 2
            throw new ClassDoesNotImplementClassException(
87 2
                $class,
88 2
                DefinesOwnIdPropertiesInterface::class
89
            );
90
        }
91 524
    }
92
93
    /**
94
    * @param mixed $value
95
    *
96
    * @return array<int, mixed> filtered $value
97
    */
98 14
    public static function MaybeThrowIfValueDoesNotMatchMultiTypedArray(
99
        bool $autoTrimStrings,
100
        bool $throwIfNotUnique,
101
        $value,
102
        string ...$types
103
    ) : array {
104 14
        if ( ! is_array($value)) {
105 2
            throw new InvalidArgumentException(
106
                'Argument 3 passed to ' .
107
                __METHOD__ .
108
                ' must be an array, ' .
109 2
                (is_object($value) ? get_class($value) : gettype($value)) .
110 2
                ' given!'
111
            );
112
        }
113
114 12
        return static::MaybeThrowIfValueDoesNotMatchMultiTypedArrayValueArray(
115 12
            $autoTrimStrings,
116 12
            $throwIfNotUnique,
117 12
            $value,
118 9
            ...$types
119
        );
120
    }
121
122 32
    private static function HasMethod(
123
        string $class,
124
        string $property,
125
        bool $SetNotGet,
126
        bool $pub = self::BOOL_DEFAULT_EXPECTING_NON_PUBLIC_METHOD
127
    ) : bool {
128 32
        $method = static::MethodNameFromProperty($property, $SetNotGet);
129
130
        try {
131 32
            $ref = new ReflectionMethod($class, $method);
132
133 32
            return ($pub ? $ref->isPublic() : $ref->isProtected()) && false === $ref->isStatic();
134 8
        } catch (ReflectionException $e) {
135 8
            return false;
136
        }
137
    }
138
139
    /**
140
    * @param mixed $maybe
141
    */
142 312
    private static function FilterMaybeArray($maybe, callable $filter) : array
143
    {
144 312
        return array_filter(
145 312
            TypeParanoia::EnsureArgumentIsArray($maybe, TypeParanoia::INDEX_FIRST_ARG, __METHOD__),
146 312
            $filter
147
        );
148
    }
149
150
    /**
151
    * @param mixed $maybe
152
    */
153 314
    private static function CountMaybeArray($maybe) : int
154
    {
155 314
        return count(TypeParanoia::EnsureArgumentIsArray(
156 314
            $maybe,
157 314
            TypeParanoia::INDEX_FIRST_ARG,
158 314
            __METHOD__
159
        ));
160
    }
161
162
    /**
163
    * @return array<int, mixed> filtered $value
164
    */
165 12
    private static function MaybeThrowIfNotArrayIntKeys(array $value) : array
166
    {
167 12
        $initialCount = count($value);
168
169
        /**
170
        * @var array<int, mixed>
171
        */
172 12
        $value = array_filter($value, 'is_int', ARRAY_FILTER_USE_KEY);
173
174 12
        if (count($value) !== $initialCount) {
175 2
            throw new InvalidArgumentException(
176
                'Argument 3 passed to ' .
177
                __METHOD__ .
178 2
                ' must be array<int, mixed>'
179
            );
180
        }
181
182 10
        return $value;
183
    }
184
185
    /**
186
    * @param array<int, mixed> $value
187
    *
188
    * @return array<int, mixed> filtered $value
189
    */
190 10
    private static function MaybeThrowIfValueArrayDoesNotMatchTypes(
191
        array $value,
192
        string ...$types
193
    ) : array {
194 10
        $initialCount = count($value);
195
196 10
        $value = array_filter(
197 10
            $value,
198
            /**
199
            * @param mixed $maybe
200
            */
201
            function ($maybe) use ($types) : bool {
202 10
                if (is_object($maybe)) {
203 8
                    foreach ($types as $maybeType) {
204 8
                        if (is_a($maybe, $maybeType)) {
205 8
                            return true;
206
                        }
207
                    }
208
209 2
                    return false;
210
                }
211
212 4
                return TypeParanoia::MaybeInArray(gettype($maybe), $types);
213 10
            }
214
        );
215
216 10
        if (count($value) !== $initialCount) {
217 2
            throw new InvalidArgumentException(
218
                'Argument 3 passed to ' .
219
                __METHOD__ .
220 2
                ' contained values that did not match the provided types!'
221
            );
222
        }
223
224 8
        return $value;
225
    }
226
227
    /**
228
    * @param array<int, mixed> $value
229
    *
230
    * @return array<int, mixed>
231
    */
232 8
    private static function MaybeRemapStringsToTrimmedStrings(
233
        array $value,
234
        bool $autoTrimStrings,
235
        string ...$types
236
    ) : array {
237 8
        if (TypeParanoia::MaybeInArray('string', $types) && $autoTrimStrings) {
238 2
            $value = array_map(
239
                /**
240
                * @param mixed $maybe
241
                *
242
                * @return mixed
243
                */
244
                function ($maybe) {
245 2
                    return is_string($maybe) ? trim($maybe) : $maybe;
246 2
                },
247 2
                $value
248
            );
249
        }
250
251 8
        return $value;
252
    }
253
254
    /**
255
    * @return array<int, mixed> filtered $value
256
    */
257 12
    private static function MaybeThrowIfValueDoesNotMatchMultiTypedArrayValueArray(
258
        bool $autoTrimStrings,
259
        bool $throwIfNotUnique,
260
        array $value,
261
        string ...$types
262
    ) : array {
263 12
        $value = static::MaybeThrowIfNotArrayIntKeys($value);
264 10
        $value = static::MaybeThrowIfValueArrayDoesNotMatchTypes($value, ...$types);
265 8
        $value = static::MaybeRemapStringsToTrimmedStrings($value, $autoTrimStrings, ...$types);
266
267 8
        $initialCount = count($value);
268
269 8
        $value = array_unique($value, SORT_REGULAR);
270
271 8
        if ($throwIfNotUnique && count($value) !== $initialCount) {
272 2
            throw new InvalidArgumentException(
273
                'Argument 3 passed to ' .
274
                __METHOD__ .
275 2
                ' contained non-unique values!'
276
            );
277
        }
278
279 6
        return array_values($value);
280
    }
281
282 236
    private static function CachePublicGettersAndSetters(string $class) : void
283
    {
284 236
        if (false === isset(self::$Getters[$class])) {
285 32
            self::$Getters[$class] = [];
286 32
            self::$publicSetters[$class] = [];
287
288 32
            if (TypeParanoia::IsThingStrings($class, DefinesOwnIdPropertiesInterface::class)) {
289 16
                self::$Getters[$class]['id'] = true;
290
            }
291
292 32
            self::CachePublicGettersAndSettersProperties($class);
293
        }
294 236
    }
295
296
    /**
297
    * @psalm-suppress InvalidStringClass
298
    */
299 32
    private static function CachePublicGettersAndSettersProperties(string $class) : void
300
    {
301
        /**
302
        * @var string[]
303
        */
304 32
        $props = $class::DaftObjectProperties();
305
306 32
        foreach ($props as $prop) {
307 32
            if (static::HasMethod($class, $prop, self::BOOL_EXPECTING_GETTER)) {
308 28
                self::$Getters[$class][$prop] = self::BOOL_METHOD_IS_PUBLIC;
309 6
            } elseif (static::HasMethod(
310 6
                $class,
311 6
                $prop,
312 6
                self::BOOL_EXPECTING_GETTER,
313 6
                self::BOOL_EXPECTING_NON_PUBLIC_METHOD
314
            )) {
315 2
                self::$Getters[$class][$prop] = self::BOOL_METHOD_IS_NON_PUBLIC;
316
            }
317
318 32
            if (static::HasMethod($class, $prop, true)) {
319 32
                self::$publicSetters[$class][] = $prop;
320
            }
321
        }
322 32
    }
323
324
    /**
325
    * @psalm-suppress InvalidStringClass
326
    */
327 314
    private static function CheckTypeDefinesOwnIdPropertiesIsImplementation(
328
        string $class
329
    ) : void {
330
        /**
331
        * @var scalar|array|object|null
332
        */
333 314
        $properties = $class::DaftObjectIdProperties();
334
335 314
        if (self::CountMaybeArray($properties) < 1) {
336 2
            throw new ClassMethodReturnHasZeroArrayCountException(
337 2
                $class,
338 2
                'DaftObjectIdProperties'
339
            );
340
        } elseif (
341 312
            self::CountMaybeArray($properties) !==
342 312
            count(self::FilterMaybeArray($properties, 'is_string'))
343
        ) {
344 2
            throw new ClassMethodReturnIsNotArrayOfStringsException(
345 2
                $class,
346 2
                'DaftObjectIdProperties'
347
            );
348
        }
349 310
    }
350
}
351