10
10
namespace PHPUnit \Metadata \Api ;
11
11
12
12
use function assert ;
13
+ use function class_exists ;
13
14
use function count ;
14
15
use function interface_exists ;
15
16
use function sprintf ;
28
29
use PHPUnit \Metadata \UsesFunction ;
29
30
use PHPUnit \Metadata \UsesMethod ;
30
31
use ReflectionClass ;
31
- use ReflectionException ;
32
32
use SebastianBergmann \CodeUnit \CodeUnitCollection ;
33
33
use SebastianBergmann \CodeUnit \Exception as CodeUnitException ;
34
34
use SebastianBergmann \CodeUnit \InvalidCodeUnitException ;
37
37
/**
38
38
* @internal This class is not covered by the backward compatibility promise for PHPUnit
39
39
*/
40
- final readonly class CodeCoverage
40
+ final class CodeCoverage
41
41
{
42
+ /**
43
+ * @psalm-var array<class-string, non-empty-list<class-string>>
44
+ */
45
+ private array $ withParents = [];
46
+
42
47
/**
43
48
* @psalm-param class-string $className
44
49
* @psalm-param non-empty-string $methodName
@@ -222,30 +227,7 @@ public function shouldCodeCoverageBeCollectedFor(string $className, string $meth
222
227
private function mapToCodeUnits (CoversClass |CoversFunction |CoversMethod |UsesClass |UsesFunction |UsesMethod $ metadata ): CodeUnitCollection
223
228
{
224
229
$ mapper = new Mapper ;
225
- $ names = [$ metadata ->asStringForCodeUnitMapper ()];
226
-
227
- if ($ metadata ->isCoversClass () || $ metadata ->isUsesClass ()) {
228
- try {
229
- $ reflector = new ReflectionClass ($ metadata ->asStringForCodeUnitMapper ());
230
- } catch (ReflectionException $ e ) {
231
- throw new InvalidCoversTargetException (
232
- sprintf (
233
- 'Class "%s" is not a valid target for code coverage ' ,
234
- $ metadata ->asStringForCodeUnitMapper (),
235
- ),
236
- $ e ->getCode (),
237
- $ e ,
238
- );
239
- }
240
-
241
- while ($ reflector = $ reflector ->getParentClass ()) {
242
- if (!$ reflector ->isUserDefined ()) {
243
- break ;
244
- }
245
-
246
- $ names [] = $ reflector ->getName ();
247
- }
248
- }
230
+ $ names = $ this ->names ($ metadata );
249
231
250
232
try {
251
233
if (count ($ names ) === 1 ) {
@@ -262,25 +244,65 @@ private function mapToCodeUnits(CoversClass|CoversFunction|CoversMethod|UsesClas
262
244
263
245
return $ codeUnits ;
264
246
} catch (CodeUnitException $ e ) {
265
- if ($ metadata ->isCoversClass () || $ metadata ->isUsesClass ()) {
266
- if (interface_exists ($ metadata ->className ())) {
267
- $ type = 'Interface ' ;
268
- } else {
269
- $ type = 'Class ' ;
270
- }
271
- } else {
272
- $ type = 'Function ' ;
273
- }
274
-
275
247
throw new InvalidCoversTargetException (
276
248
sprintf (
277
- '%s "%s" is not a valid target for code coverage ' ,
278
- $ type ,
249
+ '%s is not a valid target for code coverage ' ,
279
250
$ metadata ->asStringForCodeUnitMapper (),
280
251
),
281
252
$ e ->getCode (),
282
253
$ e ,
283
254
);
284
255
}
285
256
}
257
+
258
+ /**
259
+ * @psalm-return non-empty-list<non-empty-string>
260
+ *
261
+ * @throws InvalidCoversTargetException
262
+ */
263
+ private function names (CoversClass |CoversFunction |CoversMethod |UsesClass |UsesFunction |UsesMethod $ metadata ): array
264
+ {
265
+ $ name = $ metadata ->asStringForCodeUnitMapper ();
266
+ $ names = [$ name ];
267
+
268
+ if ($ metadata ->isCoversClass () || $ metadata ->isUsesClass ()) {
269
+ if (isset ($ this ->withParents [$ name ])) {
270
+ return $ this ->withParents [$ name ];
271
+ }
272
+
273
+ if (interface_exists ($ name )) {
274
+ throw new InvalidCoversTargetException (
275
+ sprintf (
276
+ 'Interface "%s" is not a valid target for code coverage ' ,
277
+ $ name ,
278
+ ),
279
+ );
280
+ }
281
+
282
+ if (!class_exists ($ name )) {
283
+ throw new InvalidCoversTargetException (
284
+ sprintf (
285
+ 'Class "%s" is not a valid target for code coverage ' ,
286
+ $ name ,
287
+ ),
288
+ );
289
+ }
290
+
291
+ assert (class_exists ($ names [0 ]));
292
+
293
+ $ reflector = new ReflectionClass ($ name );
294
+
295
+ while ($ reflector = $ reflector ->getParentClass ()) {
296
+ if (!$ reflector ->isUserDefined ()) {
297
+ break ;
298
+ }
299
+
300
+ $ names [] = $ reflector ->getName ();
301
+ }
302
+
303
+ $ this ->withParents [$ name ] = $ names ;
304
+ }
305
+
306
+ return $ names ;
307
+ }
286
308
}
0 commit comments