Skip to content

Commit de81d0e

Browse files
Merge branch '11.5'
2 parents 0f95acf + 340c6d9 commit de81d0e

File tree

6 files changed

+138
-6
lines changed

6 files changed

+138
-6
lines changed
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;
@@ -83,6 +84,7 @@
8384
use PHPUnit\Framework\Attributes\UsesNamespace;
8485
use PHPUnit\Framework\Attributes\UsesTrait;
8586
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
87+
use PHPUnit\Metadata\InvalidAttributeException;
8688
use PHPUnit\Metadata\Metadata;
8789
use PHPUnit\Metadata\MetadataCollection;
8890
use PHPUnit\Metadata\Version\ConstraintRequirement;
@@ -103,9 +105,10 @@ public function forClass(string $className): MetadataCollection
103105
{
104106
assert(class_exists($className));
105107

106-
$result = [];
108+
$reflector = new ReflectionClass($className);
109+
$result = [];
107110

108-
foreach ((new ReflectionClass($className))->getAttributes() as $attribute) {
111+
foreach ($reflector->getAttributes() as $attribute) {
109112
if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) {
110113
continue;
111114
}
@@ -114,7 +117,17 @@ public function forClass(string $className): MetadataCollection
114117
continue;
115118
}
116119

117-
$attributeInstance = $attribute->newInstance();
120+
try {
121+
$attributeInstance = $attribute->newInstance();
122+
} catch (Error $e) {
123+
throw new InvalidAttributeException(
124+
$attribute->getName(),
125+
'class ' . $className,
126+
$reflector->getFileName(),
127+
$reflector->getStartLine(),
128+
$e->getMessage(),
129+
);
130+
}
118131

119132
switch ($attribute->getName()) {
120133
case BackupGlobals::class:
@@ -449,9 +462,10 @@ public function forMethod(string $className, string $methodName): MetadataCollec
449462
assert(class_exists($className));
450463
assert(method_exists($className, $methodName));
451464

452-
$result = [];
465+
$reflector = new ReflectionMethod($className, $methodName);
466+
$result = [];
453467

454-
foreach ((new ReflectionMethod($className, $methodName))->getAttributes() as $attribute) {
468+
foreach ($reflector->getAttributes() as $attribute) {
455469
if (!str_starts_with($attribute->getName(), 'PHPUnit\\Framework\\Attributes\\')) {
456470
continue;
457471
}
@@ -460,7 +474,17 @@ public function forMethod(string $className, string $methodName): MetadataCollec
460474
continue;
461475
}
462476

463-
$attributeInstance = $attribute->newInstance();
477+
try {
478+
$attributeInstance = $attribute->newInstance();
479+
} catch (Error $e) {
480+
throw new InvalidAttributeException(
481+
$attribute->getName(),
482+
'method ' . $className . '::' . $methodName . '()',
483+
$reflector->getFileName(),
484+
$reflector->getStartLine(),
485+
$e->getMessage(),
486+
);
487+
}
464488

465489
switch ($attribute->getName()) {
466490
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
@@ -65,6 +65,7 @@
6565
use PHPUnit\Framework\Attributes\UsesTrait;
6666
use PHPUnit\Framework\Attributes\WithoutErrorHandler;
6767
use PHPUnit\Metadata\DisableReturnValueGenerationForTestDoubles;
68+
use PHPUnit\Metadata\InvalidAttributeException;
6869

6970
#[CoversClass(AttributeParser::class)]
7071
#[CoversClass(AfterClass::class)]
@@ -94,6 +95,7 @@
9495
#[CoversClass(ExcludeGlobalVariableFromBackup::class)]
9596
#[CoversClass(ExcludeStaticPropertyFromBackup::class)]
9697
#[CoversClass(Group::class)]
98+
#[CoversClass(InvalidAttributeException::class)]
9799
#[CoversClass(Large::class)]
98100
#[CoversClass(Medium::class)]
99101
#[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\RequiresEnvironmentVariable;
1819
use PHPUnit\Metadata\RequiresPhp;
1920
use PHPUnit\Metadata\RequiresPhpExtension;
@@ -30,6 +31,8 @@
3031
use PHPUnit\TestFixture\Metadata\Attribute\DependencyTest;
3132
use PHPUnit\TestFixture\Metadata\Attribute\DisableReturnValueGenerationForTestDoublesTest;
3233
use PHPUnit\TestFixture\Metadata\Attribute\DoesNotPerformAssertionsTest;
34+
use PHPUnit\TestFixture\Metadata\Attribute\DuplicateSmallAttributeTest;
35+
use PHPUnit\TestFixture\Metadata\Attribute\DuplicateTestAttributeTest;
3336
use PHPUnit\TestFixture\Metadata\Attribute\Example;
3437
use PHPUnit\TestFixture\Metadata\Attribute\ExampleTrait;
3538
use PHPUnit\TestFixture\Metadata\Attribute\GroupTest;
@@ -1138,5 +1141,19 @@ public function test_ignores_attributes_in_PHPUnit_namespace_that_do_not_exist()
11381141
$this->assertTrue($metadata->isEmpty());
11391142
}
11401143

1144+
public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_class(): void
1145+
{
1146+
$this->expectException(InvalidAttributeException::class);
1147+
1148+
$this->parser()->forClass(DuplicateSmallAttributeTest::class);
1149+
}
1150+
1151+
public function test_handles_ReflectionException_raised_when_instantiating_attribute_on_method(): void
1152+
{
1153+
$this->expectException(InvalidAttributeException::class);
1154+
1155+
$this->parser()->forMethod(DuplicateTestAttributeTest::class, 'testOne');
1156+
}
1157+
11411158
abstract protected function parser(): Parser;
11421159
}

0 commit comments

Comments
 (0)