• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

voku / Arrayy / 24917960344

25 Apr 2026 12:27AM UTC coverage: 89.25% (-0.09%) from 89.336%
24917960344

push

github

web-flow
Merge pull request #159 from voku/copilot/add-native-properties-type-checking

Fix PHP 8.0 intersection-type CI failure, strengthen type-check coverage, and refresh PHP 8.0+ docs/CI matrix

143 of 164 new or added lines in 6 files covered. (87.2%)

12 existing lines in 1 file now uncovered.

2607 of 2921 relevant lines covered (89.25%)

238.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.86
/src/TypeCheck/AbstractTypeCheck.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Arrayy\TypeCheck;
6

7
abstract class AbstractTypeCheck implements TypeCheckInterface
8
{
9
    /**
10
     * @var bool
11
     */
12
    protected $isNullable = false;
13

14
    /**
15
     * @var string[]
16
     */
17
    protected $types = [];
18

19
    /**
20
     * @var array<string, string>
21
     */
22
    private static $typeMapping = [
23
        'int'   => 'integer',
24
        'bool'  => 'boolean',
25
        'float' => 'double',
26
    ];
27

28
    /**
29
     * @return string[]
30
     */
31
    public function getTypes(): array
32
    {
33
        return $this->types;
126✔
34
    }
35

36
    /**
37
     * @param mixed $value
38
     *
39
     * @return bool
40
     */
41
    public function checkType(&$value): bool
42
    {
43
        if ($this->isNullable && $value === null) {
1,162✔
44
            return true;
245✔
45
        }
46

47
        if (
48
            \is_array($value)
1,162✔
49
            &&
50
            \count($this->types) > 1
1,162✔
51
            &&
52
            $this->allTypesAreGenericArrays()
1,162✔
53
        ) {
54
            if ($this->assertTypeEquals(\implode('|', $this->types), $value)) {
14✔
55
                return true;
14✔
56
            }
57
        }
58

59
        foreach ($this->types as $currentType) {
1,155✔
60
            $isValidType = $this->assertTypeEquals($currentType, $value);
1,155✔
61

62
            if ($isValidType) {
1,155✔
63
                return true;
1,106✔
64
            }
65
        }
66

67
        $type = \gettype($value);
406✔
68

69
        $expectedTypes = \implode('|', $this->types);
406✔
70

71
        $this->throwException($expectedTypes, $value, $type);
406✔
72

73
        return false;
×
74
    }
75

76
    /**
77
     * @param string $type
78
     * @param mixed  $value
79
     *
80
     * @return bool
81
     */
82
    protected function assertTypeEquals(string $type, &$value): bool
83
    {
84
        $type = \trim($type);
1,162✔
85

86
        if (\strpos($type, '[]') !== false) {
1,162✔
87
            return $this->isValidGenericCollection($type, $value);
329✔
88
        }
89

90
        if (\strpos($type, '&') !== false) {
1,155✔
91
            foreach (\explode('&', $type) as $subType) {
7✔
92
                if ($this->assertTypeEquals(\trim($subType), $value) === false) {
7✔
NEW
93
                    return false;
×
94
                }
95
            }
96

97
            return true;
7✔
98
        }
99

100
        if (\strpos($type, '|') !== false) {
1,155✔
101
            foreach (\explode('|', $type) as $subType) {
21✔
102
                if ($this->assertTypeEquals(\trim($subType), $value) === true) {
21✔
103
                    return true;
21✔
104
                }
105
            }
106

NEW
107
            return false;
×
108
        }
109

110
        if ($type === 'mixed' && $value !== null) {
1,155✔
111
            return true;
42✔
112
        }
113

114
        return $value instanceof $type
1,148✔
115
               ||
1,148✔
116
               \gettype($value) === (self::$typeMapping[$type] ?? $type)
1,148✔
117
               ||
1,148✔
118
               (
1,148✔
119
                   $type === 'scalar'
1,148✔
120
                    &&
1,148✔
121
                    \is_scalar($value)
1,148✔
122
               )
1,148✔
123
               ||
1,148✔
124
               (
1,148✔
125
                   $type === 'callable'
1,148✔
126
                   &&
1,148✔
127
                   \is_callable($value)
1,148✔
128
               )
1,148✔
129
               ||
1,148✔
130
               (
1,148✔
131
                   $type === 'numeric'
1,148✔
132
                   &&
1,148✔
133
                   (
1,148✔
134
                       \is_float($value)
1,148✔
135
                       ||
1,148✔
136
                       \is_int($value)
1,148✔
137
                   )
1,148✔
138
               )
1,148✔
139
               ||
1,148✔
140
               (
1,148✔
141
                   $type === 'resource'
1,148✔
142
                   &&
1,148✔
143
                   \is_resource($value)
1,148✔
144
               );
1,148✔
145
    }
146

147
    /**
148
     * @param mixed $value
149
     *
150
     * @return string
151
     */
152
    protected function valueToString($value): string
153
    {
154
        // null
155
        if ($value === null) {
140✔
156
            return 'NULL';
7✔
157
        }
158

159
        // bool
160
        if (\is_bool($value)) {
133✔
161
            return $value ? 'TRUE' : 'FALSE';
×
162
        }
163

164
        // array
165
        if (\is_array($value)) {
133✔
166
            return 'Array';
77✔
167
        }
168

169
        // scalar types (integer, float, string)
170
        if (\is_scalar($value)) {
56✔
171
            return (string) $value;
49✔
172
        }
173

174
        // resource
175
        if (\is_resource($value)) {
7✔
176
            return \get_resource_type($value) . ' resource #' . (int) $value;
×
177
        }
178

179
        if (\is_object($value)) {
7✔
180
            return \get_class($value) . ' Object';
7✔
181
        }
182

183
        return '';
×
184
    }
185

186
    /**
187
     * @param string $type
188
     * @param mixed  $collection
189
     *
190
     * @return bool
191
     */
192
    private function isValidGenericCollection(string $type, &$collection): bool
193
    {
194
        if (!\is_array($collection)) {
329✔
195
            return false;
7✔
196
        }
197

198
        $valueType = \str_replace('[]', '', $type);
322✔
199

200
        foreach ($collection as $value) {
322✔
201
            // A typed collection is only valid when every element matches the declared element type.
202
            if (!$this->assertTypeEquals($valueType, $value)) {
301✔
203
                return false;
105✔
204
            }
205
        }
206

207
        return true;
217✔
208
    }
209

210
    private function allTypesAreGenericArrays(): bool
211
    {
212
        foreach ($this->types as $type) {
14✔
213
            if (\substr($type, -2) !== '[]') {
14✔
NEW
214
                return false;
×
215
            }
216
        }
217

218
        return true;
14✔
219
    }
220
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc