Skip to content

Commit 340c6d9

Browse files
Merge branch '10.5' into 11.5
2 parents 4b922b7 + e2a78f9 commit 340c6d9

File tree

7 files changed

+139
-6
lines changed

7 files changed

+139
-6
lines changed

ChangeLog-11.5.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ All notable changes of the PHPUnit 11.5 release series are documented in this fi
77
### Changed
88

99
* [#6117](https://github.com/sebastianbergmann/phpunit/issues/6117): Include source location information for issues triggered during test in `--debug` output
10+
* [#6119](https://github.com/sebastianbergmann/phpunit/issues/6119): Improve message for errors that occur while parsing attributes
1011

1112
## [11.5.6] - 2025-01-31
1213

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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;
11+
12+
use const PHP_EOL;
13+
use function sprintf;
14+
use PHPUnit\Exception;
15+
use RuntimeException;
16+
17+
/**
18+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
19+
*
20+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
21+
*/
22+
final class InvalidAttributeException extends RuntimeException implements Exception
23+
{
24+
/**
25+
* @param non-empty-string $attributeName
26+
* @param non-empty-string $target
27+
* @param non-empty-string $file
28+
* @param positive-int $line
29+
* @param non-empty-string $message
30+
*/
31+
public function __construct(string $attributeName, string $target, string $file, int $line, string $message)
32+
{
33+
parent::__construct(
34+
sprintf(
35+
'Invalid attribute %s for %s in %s:%d%s%s',
36+
$attributeName,
37+
$target,
38+
$file,
39+
$line,
40+
PHP_EOL,
41+
$message,
42+
),
43+
);
44+
}
45+
}

src/Metadata/Parser/AttributeParser.php

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use function str_starts_with;
1919
use function strtolower;
2020
use function trim;
21+
use Error;
2122
use PHPUnit\Event\Facade as EventFacade;
2223
use PHPUnit\Framework\Attributes\After;
2324
use PHPUnit\Framework\Attributes\AfterClass;
@@ -76,6 +77,7 @@
7677
use PHPUnit\Framework\Attributes\UsesMethod;
7778
use PHPUnit\Framework\Attributes\UsesTrait;
7879
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
80+
use PHPUnit\Metadata\InvalidAttributeException;
7981
use PHPUnit\Metadata\Metadata;
8082
use PHPUnit\Metadata\MetadataCollection;
8183
use PHPUnit\Metadata\Version\ConstraintRequirement;
@@ -96,9 +98,10 @@ public function forClass(string $className): MetadataCollection
9698
{
9799
assert(class_exists($className));
98100

99-
$result = [];
101+
$reflector = new ReflectionClass($className);
102+
$result = [];
100103

101-
foreach ((new ReflectionClass($className))->getAttributes() as $attribute) {
104+
foreach ($reflector->getAttributes() as $attribute) {
102105
if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) {
103106
continue;
104107
}
@@ -107,7 +110,17 @@ public function forClass(string $className): MetadataCollection
107110
continue;
108111
}
109112

110-
$attributeInstance = $attribute->newInstance();
113+
try {
114+
$attributeInstance = $attribute->newInstance();
115+
} catch (Error $e) {
116+
throw new InvalidAttributeException(
117+
$attribute->getName(),
118+
'class ' . $className,
119+
$reflector->getFileName(),
120+
$reflector->getStartLine(),
121+
$e->getMessage(),
122+
);
123+
}
111124

112125
switch ($attribute->getName()) {
113126
case BackupGlobals::class:
@@ -390,9 +403,10 @@ public function forMethod(string $className, string $methodName): MetadataCollec
390403
assert(class_exists($className));
391404
assert(method_exists($className, $methodName));
392405

393-
$result = [];
406+
$reflector = new ReflectionMethod($className, $methodName);
407+
$result = [];
394408

395-
foreach ((new ReflectionMethod($className, $methodName))->getAttributes() as $attribute) {
409+
foreach ($reflector->getAttributes() as $attribute) {
396410
if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) {
397411
continue;
398412
}
@@ -401,7 +415,17 @@ public function forMethod(string $className, string $methodName): MetadataCollec
401415
continue;
402416
}
403417

404-
$attributeInstance = $attribute->newInstance();
418+
try {
419+
$attributeInstance = $attribute->newInstance();
420+
} catch (Error $e) {
421+
throw new InvalidAttributeException(
422+
$attribute->getName(),
423+
'method ' . $className . '::' . $methodName . '()',
424+
$reflector->getFileName(),
425+
$reflector->getStartLine(),
426+
$e->getMessage(),
427+
);
428+
}
405429

406430
switch ($attribute->getName()) {
407431
case After::class:
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Metadata\Attribute;
11+
12+
use PHPUnit\Framework\Attributes\Small;
13+
use PHPUnit\Framework\TestCase;
14+
15+
#[Small]
16+
#[Small]
17+
final class DuplicateSmallAttributeTest extends TestCase
18+
{
19+
public function testOne(): void
20+
{
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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\Metadata\Attribute;
11+
12+
use PHPUnit\Framework\Attributes\Test;
13+
use PHPUnit\Framework\TestCase;
14+
15+
final class DuplicateTestAttributeTest extends TestCase
16+
{
17+
#[Test]
18+
#[Test]
19+
public function testOne(): void
20+
{
21+
}
22+
}

tests/unit/Metadata/Parser/AttributeParserTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
use PHPUnit\Framework\Attributes\UsesTrait;
6565
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
6666
use PHPUnit\Metadata\DisableReturnValueGenerationForTestDoubles;
67+
use PHPUnit\Metadata\InvalidAttributeException;
6768

6869
#[CoversClass(AttributeParser::class)]
6970
#[CoversClass(AfterClass::class)]
@@ -93,6 +94,7 @@
9394
#[CoversClass(ExcludeGlobalVariableFromBackup::class)]
9495
#[CoversClass(ExcludeStaticPropertyFromBackup::class)]
9596
#[CoversClass(Group::class)]
97+
#[CoversClass(InvalidAttributeException::class)]
9698
#[CoversClass(Large::class)]
9799
#[CoversClass(Medium::class)]
98100
#[CoversClass(PostCondition::class)]

tests/unit/Metadata/Parser/AttributeParserTestCase.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use PHPUnit\Metadata\DependsOnClass;
1616
use PHPUnit\Metadata\DependsOnMethod;
17+
use PHPUnit\Metadata\InvalidAttributeException;
1718
use PHPUnit\Metadata\RequiresPhp;
1819
use PHPUnit\Metadata\RequiresPhpExtension;
1920
use PHPUnit\Metadata\RequiresPhpunit;
@@ -28,6 +29,8 @@
2829
use PHPUnit\TestFixture\Metadata\Attribute\DependencyTest;
2930
use PHPUnit\TestFixture\Metadata\Attribute\DisableReturnValueGenerationForTestDoublesTest;
3031
use PHPUnit\TestFixture\Metadata\Attribute\DoesNotPerformAssertionsTest;
32+
use PHPUnit\TestFixture\Metadata\Attribute\DuplicateSmallAttributeTest;
33+
use PHPUnit\TestFixture\Metadata\Attribute\DuplicateTestAttributeTest;
3134
use PHPUnit\TestFixture\Metadata\Attribute\Example;
3235
use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait;
3336
use PHPUnit\TestFixture\Metadata\Attribute\GroupTest;
@@ -1039,5 +1042,19 @@ public function test_ignores_attributes_in_PHPUnit_namespace_that_do_not_exist()
10391042
$this->assertTrue($metadata->isEmpty());
10401043
}
10411044

1045+
public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_class(): void
1046+
{
1047+
$this->expectException(InvalidAttributeException::class);
1048+
1049+
$this->parser()->forClass(DuplicateSmallAttributeTest::class);
1050+
}
1051+
1052+
public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_method(): void
1053+
{
1054+
$this->expectException(InvalidAttributeException::class);
1055+
1056+
$this->parser()->forMethod(DuplicateTestAttributeTest::class, 'testOne');
1057+
}
1058+
10421059
abstract protected function parser(): Parser;
10431060
}

0 commit comments

Comments
 (0)