Completed
Push — master ( dca610...02a711 )
by Chris
03:06
created

MatchTester::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 1
rs 10
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php declare(strict_types = 1);
2
3
namespace DaveRandom\CallbackValidator;
4
5
final class MatchTester
6
{
7
    /**
8
     * Thou shalt not instantiate
9
     */
10
    private function __construct() { }
11
12
    /**
13
     * Lookup table of all built-in types
14
     */
15
    private static $builtInTypes = [
16
        BuiltInTypes::STRING   => true,
17
        BuiltInTypes::INT      => true,
18
        BuiltInTypes::FLOAT    => true,
19
        BuiltInTypes::BOOL     => true,
20
        BuiltInTypes::ARRAY    => true,
21
        BuiltInTypes::CALLABLE => true,
22
        BuiltInTypes::VOID     => true,
23
        BuiltInTypes::ITERABLE => true,
24
    ];
25
26
    /**
27
     * Lookup table of scalar types
28
     */
29
    private static $scalarTypes = [
30
        BuiltInTypes::STRING => true,
31
        BuiltInTypes::INT    => true,
32
        BuiltInTypes::FLOAT  => true,
33
        BuiltInTypes::BOOL   => true,
34
    ];
35
36
    /**
37
     * @param string|null $superTypeName
38
     * @param bool $superTypeNullable
39
     * @param string|null $subTypeName
40
     * @param bool $subTypeNullable
41
     * @param bool $weak
42
     * @return bool
43
     */
44 84
    public static function isMatch($superTypeName, $superTypeNullable, $subTypeName, $subTypeNullable, $weak)
45
    {
46
        // If the super type is unspecified, anything is a match
47 84
        if ($superTypeName === null) {
48
            return true;
49
        }
50
51
        // If the sub type is unspecified, nothing is a match
52 84
        if ($subTypeName === null) {
53
            return false;
54
        }
55
56 84
        $superTypeName = (string)$superTypeName;
57 84
        $subTypeName = (string)$subTypeName;
58
59
        // Sub type cannot be nullable unless the super type is as well
60 84
        if ($subTypeNullable && !$superTypeNullable) {
61
            // nullable void doesn't really make sense but for completeness...
62 24
            return $superTypeName === BuiltInTypes::VOID && $subTypeName === BuiltInTypes::VOID;
63
        }
64
65
        // If the string is an exact match it's definitely acceptable
66 64
        if ($superTypeName === $subTypeName) {
67 8
            return true;
68
        }
69
70
        // Check iterable
71 62 View Code Duplication
        if ($superTypeName === BuiltInTypes::ITERABLE) {
72 24
            return $subTypeName === BuiltInTypes::ARRAY
73 18
                || $subTypeName === \Traversable::class
74 24
                || \is_subclass_of($subTypeName, \Traversable::class);
75
        }
76
77
        // Check callable
78 44 View Code Duplication
        if ($superTypeName === BuiltInTypes::CALLABLE) {
79 24
            return $subTypeName === \Closure::class
80 18
                || \method_exists($subTypeName, '__invoke')
81 24
                || \is_subclass_of($subTypeName, \Closure::class);
82
        }
83
84
        // If the super type is built-in, check whether casting rules can succeed
85 26
        if (isset(self::$builtInTypes[$superTypeName])) {
86
            // Fail immediately in strict mode
87 26
            if (!$weak) {
88 13
                return false;
89
            }
90
91
            // Nothing else satisfies array, callable, void or iterable
92 13
            if (!isset(self::$scalarTypes[$superTypeName])) {
93 4
                return false;
94
            }
95
96
            // Scalars can all cast to each other
97 9
            if (isset(self::$scalarTypes[$subTypeName])) {
98 3
                return true;
99
            }
100
101
            // Classes with __toString() satisfy string
102 6
            if ($superTypeName === BuiltInTypes::STRING && \method_exists($subTypeName, '__toString')) {
103 3
                return true;
104
            }
105
106 3
            return false;
107
        }
108
109
        // We now know the super type is not built-in and there's no string match, sub type must not be built-in
110
        if (isset(self::$builtInTypes[$subTypeName])) {
111
            return false;
112
        }
113
114
        return \is_subclass_of($subTypeName, $superTypeName);
115
    }
116
}
117