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

tempestphp / tempest-framework / 14024978163

23 Mar 2025 05:55PM UTC coverage: 79.391% (-0.05%) from 79.441%
14024978163

push

github

web-flow
feat(view): cache Blade and Twig templates in internal storage (#1061)

2 of 2 new or added lines in 2 files covered. (100.0%)

912 existing lines in 110 files now uncovered.

10478 of 13198 relevant lines covered (79.39%)

91.09 hits per line

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

89.36
/src/Tempest/Reflection/src/TypeReflector.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\Reflection;
6

7
use Exception;
8
use Generator;
9
use ReflectionClass as PHPReflectionClass;
10
use ReflectionIntersectionType as PHPReflectionIntersectionType;
11
use ReflectionNamedType as PHPReflectionNamedType;
12
use ReflectionParameter as PHPReflectionParameter;
13
use ReflectionProperty as PHPReflectionProperty;
14
use ReflectionType as PHPReflectionType;
15
use ReflectionUnionType as PHPReflectionUnionType;
16
use Reflector as PHPReflector;
17

18
final readonly class TypeReflector implements Reflector
19
{
20
    private const array BUILTIN_VALIDATION = [
21
        'array' => 'is_array',
22
        'bool' => 'is_bool',
23
        'callable' => 'is_callable',
24
        'float' => 'is_float',
25
        'int' => 'is_int',
26
        'null' => 'is_null',
27
        'object' => 'is_object',
28
        'resource' => 'is_resource',
29
        'string' => 'is_string',
30
        // these are handled explicitly
31
        'false' => null,
32
        'mixed' => null,
33
        'never' => null,
34
        'true' => null,
35
        'void' => null,
36
    ];
37

38
    private const array SCALAR_TYPES = [
39
        'bool',
40
        'string',
41
        'int',
42
        'float',
43
    ];
44

45
    private string $definition;
46

47
    private string $cleanDefinition;
48

49
    private bool $isNullable;
50

51
    public function __construct(
740✔
52
        private PHPReflector|PHPReflectionType|string $reflector,
53
    ) {
54
        $this->definition = $this->resolveDefinition($this->reflector);
740✔
55
        $this->isNullable = $this->resolveIsNullable($this->reflector);
740✔
56
        $this->cleanDefinition = str_replace('?', '', $this->definition);
740✔
57
    }
58

59
    public function asClass(): ClassReflector
117✔
60
    {
61
        return new ClassReflector($this->cleanDefinition);
117✔
62
    }
63

64
    public function equals(string|TypeReflector $type): bool
71✔
65
    {
66
        if (is_string($type)) {
71✔
67
            $type = new TypeReflector($type);
60✔
68
        }
69

70
        return $this->definition === $type->definition;
71✔
71
    }
72

73
    public function accepts(mixed $input): bool
680✔
74
    {
75
        if ($this->isNullable && $input === null) {
680✔
76
            return true;
1✔
77
        }
78

79
        if ($this->isBuiltIn()) {
679✔
80
            return match ($this->cleanDefinition) {
227✔
UNCOV
81
                'false' => $input === false,
×
UNCOV
82
                'mixed' => true,
×
UNCOV
83
                'never' => false,
×
UNCOV
84
                'true' => $input === true,
×
UNCOV
85
                'void' => false,
×
86
                default => self::BUILTIN_VALIDATION[$this->cleanDefinition]($input),
227✔
87
            };
227✔
88
        }
89

90
        if ($this->isClass()) {
675✔
91
            if (is_string($input)) {
674✔
92
                return $this->matches($input);
3✔
93
            }
94

95
            $cleanDefinition = $this->cleanDefinition;
674✔
96

97
            return $input instanceof $cleanDefinition;
674✔
98
        }
99

100
        if ($this->isIterable()) {
660✔
UNCOV
101
            return is_iterable($input);
×
102
        }
103

104
        if (str_contains($this->definition, '|')) {
660✔
105
            return array_any($this->split(), static fn ($type) => $type->accepts($input));
1✔
106
        }
107

108
        if (str_contains($this->definition, '&')) {
659✔
109
            return array_all($this->split(), static fn ($type) => $type->accepts($input));
×
110
        }
111

112
        return false;
659✔
113
    }
114

115
    public function matches(string $className): bool
695✔
116
    {
117
        return is_a($this->cleanDefinition, $className, true);
695✔
118
    }
119

120
    public function getName(): string
710✔
121
    {
122
        return $this->definition;
710✔
123
    }
124

125
    public function getShortName(): string
666✔
126
    {
127
        $parts = explode('\\', $this->definition);
666✔
128

129
        return $parts[array_key_last($parts)];
666✔
130
    }
131

132
    public function isBuiltIn(): bool
687✔
133
    {
134
        return isset(self::BUILTIN_VALIDATION[$this->cleanDefinition]);
687✔
135
    }
136

137
    public function isScalar(): bool
19✔
138
    {
139
        return in_array($this->cleanDefinition, self::SCALAR_TYPES, strict: true);
19✔
140
    }
141

142
    public function isClass(): bool
683✔
143
    {
144
        return class_exists($this->cleanDefinition);
683✔
145
    }
146

147
    public function isInterface(): bool
×
148
    {
149
        return interface_exists($this->cleanDefinition);
×
150
    }
151

152
    public function isIterable(): bool
674✔
153
    {
154
        return in_array(
674✔
155
            $this->cleanDefinition,
674✔
156
            [
674✔
157
                'array',
674✔
158
                'iterable',
674✔
159
                Generator::class,
674✔
160
            ],
674✔
161
            strict: true,
674✔
162
        );
674✔
163
    }
164

165
    public function isNullable(): bool
19✔
166
    {
167
        return $this->isNullable;
19✔
168
    }
169

170
    /** @return self[] */
171
    public function split(): array
683✔
172
    {
173
        return array_map(
683✔
174
            fn (string $part) => new self($part),
683✔
175
            preg_split('/[&|]/', $this->definition),
683✔
176
        );
683✔
177
    }
178

179
    private function resolveDefinition(PHPReflector|PHPReflectionType|string $reflector): string
740✔
180
    {
181
        if (is_string($reflector)) {
740✔
182
            return $reflector;
694✔
183
        }
184

185
        if ($reflector instanceof PHPReflectionParameter || $reflector instanceof PHPReflectionProperty) {
731✔
186
            return $this->resolveDefinition($reflector->getType());
712✔
187
        }
188

189
        if ($reflector instanceof PHPReflectionClass) {
731✔
190
            return $reflector->getName();
699✔
191
        }
192

193
        if ($reflector instanceof PHPReflectionNamedType) {
720✔
194
            return $reflector->getName();
720✔
195
        }
196

197
        if ($reflector instanceof PHPReflectionUnionType) {
662✔
198
            return implode('|', array_map(
661✔
199
                fn (PHPReflectionType $reflectionType) => $this->resolveDefinition($reflectionType),
661✔
200
                $reflector->getTypes(),
661✔
201
            ));
661✔
202
        }
203

204
        if ($reflector instanceof PHPReflectionIntersectionType) {
1✔
205
            return implode('&', array_map(
1✔
206
                fn (PHPReflectionType $reflectionType) => $this->resolveDefinition($reflectionType),
1✔
207
                $reflector->getTypes(),
1✔
208
            ));
1✔
209
        }
210

UNCOV
211
        throw new Exception('Could not resolve type');
×
212
    }
213

214
    private function resolveIsNullable(PHPReflectionType|PHPReflector|string $reflector): bool
740✔
215
    {
216
        if (is_string($reflector)) {
740✔
217
            return str_contains($this->definition, '?') || str_contains($this->definition, 'null');
694✔
218
        }
219

220
        if ($reflector instanceof PHPReflectionParameter || $reflector instanceof PHPReflectionProperty) {
731✔
221
            return $reflector->getType()->allowsNull();
712✔
222
        }
223

224
        if ($reflector instanceof PHPReflectionType) {
699✔
225
            return $reflector->allowsNull();
669✔
226
        }
227

228
        return false;
699✔
229
    }
230
}
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