Skip to content

Commit 3ce3114

Browse files
Handle invalid version requirements in annotations by ignoring them and emitting a warning instead of crashing
1 parent f251c09 commit 3ce3114

File tree

5 files changed

+135
-14
lines changed

5 files changed

+135
-14
lines changed

src/Metadata/Parser/Annotation/DocBlock.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ private function __construct(string $docComment, array $symbolAnnotations, int $
120120
* string,
121121
* string|array{version: string, operator: string}|array{constraint: string}|array<int|string, string>
122122
* >
123+
*
124+
* @throws InvalidVersionRequirementException
123125
*/
124126
public function requirements(): array
125127
{

src/Metadata/Parser/AnnotationParser.php

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
use function method_exists;
1717
use function preg_replace;
1818
use function rtrim;
19+
use function sprintf;
1920
use function str_contains;
2021
use function str_starts_with;
2122
use function strlen;
2223
use function substr;
2324
use function trim;
25+
use PHPUnit\Event\Facade as EventFacade;
2426
use PHPUnit\Metadata\Annotation\Parser\Registry as AnnotationRegistry;
2527
use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException;
28+
use PHPUnit\Metadata\InvalidVersionRequirementException;
2629
use PHPUnit\Metadata\Metadata;
2730
use PHPUnit\Metadata\MetadataCollection;
2831
use PHPUnit\Metadata\ReflectionException;
@@ -147,13 +150,23 @@ public function forClass(string $className): MetadataCollection
147150
}
148151
}
149152

150-
$result = array_merge(
151-
$result,
152-
$this->parseRequirements(
153-
AnnotationRegistry::getInstance()->forClassName($className)->requirements(),
154-
'class',
155-
),
156-
);
153+
try {
154+
$result = array_merge(
155+
$result,
156+
$this->parseRequirements(
157+
AnnotationRegistry::getInstance()->forClassName($className)->requirements(),
158+
'class',
159+
),
160+
);
161+
} catch (InvalidVersionRequirementException $e) {
162+
EventFacade::emitter()->testRunnerTriggeredWarning(
163+
sprintf(
164+
'Class %s is annotated using an invalid version requirement: %s',
165+
$className,
166+
$e->getMessage(),
167+
),
168+
);
169+
}
157170

158171
return MetadataCollection::fromArray($result);
159172
}
@@ -364,13 +377,24 @@ public function forMethod(string $className, string $methodName): MetadataCollec
364377
}
365378

366379
if (method_exists($className, $methodName)) {
367-
$result = array_merge(
368-
$result,
369-
$this->parseRequirements(
370-
AnnotationRegistry::getInstance()->forMethod($className, $methodName)->requirements(),
371-
'method',
372-
),
373-
);
380+
try {
381+
$result = array_merge(
382+
$result,
383+
$this->parseRequirements(
384+
AnnotationRegistry::getInstance()->forMethod($className, $methodName)->requirements(),
385+
'method',
386+
),
387+
);
388+
} catch (InvalidVersionRequirementException $e) {
389+
EventFacade::emitter()->testRunnerTriggeredWarning(
390+
sprintf(
391+
'Method %s::%s is annotated using an invalid version requirement: %s',
392+
$className,
393+
$methodName,
394+
$e->getMessage(),
395+
),
396+
);
397+
}
374398
}
375399

376400
return MetadataCollection::fromArray($result);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TestFixture;
11+
12+
use PHPUnit\Framework\TestCase;
13+
14+
/**
15+
* @requires PHP ~~9.0
16+
*/
17+
final class InvalidRequirementsTest extends TestCase
18+
{
19+
/**
20+
* @requires PHP ~~9.0
21+
*/
22+
public function testInvalidVersionConstraint(): void
23+
{
24+
$this->assertTrue(true);
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
phpunit --no-configuration ../_files/InvalidRequirementsTest.php
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
require_once(__DIR__ . '/../../bootstrap.php');
6+
7+
$_SERVER['argv'][] = '--do-not-cache-result';
8+
$_SERVER['argv'][] = '--no-configuration';
9+
$_SERVER['argv'][] = \realpath(__DIR__ . '/../../_files/InvalidRequirementsTest.php');
10+
11+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
12+
--EXPECTF--
13+
PHPUnit %s by Sebastian Bergmann and contributors.
14+
15+
Runtime: %s
16+
17+
. 1 / 1 (100%)
18+
19+
Time: %s, Memory: %s
20+
21+
There were 2 PHPUnit test runner warnings:
22+
23+
1) Method PHPUnit\TestFixture\InvalidRequirementsTest::testInvalidVersionConstraint is annotated using an invalid version requirement: Version constraint ~~9.0 is not supported.
24+
25+
2) Class PHPUnit\TestFixture\InvalidRequirementsTest is annotated using an invalid version requirement: Version constraint ~~9.0 is not supported.
26+
27+
WARNINGS!
28+
Tests: 1, Assertions: 1, Warnings: 2.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Metadata\Annotation;
11+
12+
use Exception;
13+
use PHPUnit\Framework\Attributes\CoversClass;
14+
use PHPUnit\Framework\Attributes\Group;
15+
use PHPUnit\Framework\Attributes\Small;
16+
use PHPUnit\Framework\TestCase;
17+
use PHPUnit\Metadata\Annotation\Parser\DocBlock;
18+
use PHPUnit\Metadata\AnnotationsAreNotSupportedForInternalClassesException;
19+
use ReflectionClass;
20+
use ReflectionMethod;
21+
22+
#[CoversClass(DocBlock::class)]
23+
#[Small]
24+
#[Group('metadata')]
25+
#[Group('metadata/annotations')]
26+
final class DocBlockTest extends TestCase
27+
{
28+
public function testDoesNotSupportInternalClasses(): void
29+
{
30+
$this->expectException(AnnotationsAreNotSupportedForInternalClassesException::class);
31+
32+
DocBlock::ofClass(new ReflectionClass(Exception::class));
33+
}
34+
35+
public function testDoesNotSupportInternalMethods(): void
36+
{
37+
$this->expectException(AnnotationsAreNotSupportedForInternalClassesException::class);
38+
39+
DocBlock::ofMethod(new ReflectionMethod(Exception::class, 'getMessage'));
40+
}
41+
}

0 commit comments

Comments
 (0)