Skip to content

Commit 190be08

Browse files
authored
Check filtered attributes on instance type (#13)
1 parent 03d99ec commit 190be08

File tree

7 files changed

+107
-1
lines changed

7 files changed

+107
-1
lines changed

fixture_src/Fixtures.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,7 @@ public static function classOnlyAttributeSingleInterface() : ClassOnlyAttributeS
8282
return new ClassOnlyAttributeSingleInterfaceFixture();
8383
}
8484

85+
public static function targetAttributeInterface() : TargetAttributeInterfaceFixture {
86+
return new TargetAttributeInterfaceFixture();
87+
}
8588
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedTargetFixture;
4+
5+
#[\Attribute(\Attribute::TARGET_CLASS)]
6+
final class TargetAttributeImplementation implements TargetAttributeInterface {
7+
8+
public function __construct(public readonly string $value) {}
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedTargetFixture;
4+
5+
interface TargetAttributeInterface {
6+
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedTargetFixture\TargetAttributeInterface;
4+
5+
use Cspray\AnnotatedTargetFixture\TargetAttributeImplementation;
6+
7+
#[TargetAttributeImplementation('target-attr')]
8+
class TargetClass {
9+
10+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedTargetFixture;
4+
5+
use Cspray\AnnotatedTargetFixture\TargetAttributeInterface\TargetClass;
6+
use Cspray\Typiphy\ObjectType;
7+
use function Cspray\Typiphy\objectType;
8+
9+
final class TargetAttributeInterfaceFixture implements Fixture {
10+
11+
public function getPath() : string {
12+
return __DIR__ . '/TargetAttributeInterface';
13+
}
14+
15+
public function targetClass() : ObjectType {
16+
return objectType(TargetClass::class);
17+
}
18+
}

src/PhpParserAnnotatedTargetParser.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public function leaveNode(Node $node) {
8080
foreach ($node->attrGroups as $attrGroup) {
8181
foreach ($attrGroup->attrs as $attr) {
8282
$attrType = $attr->name->toString();
83-
if (!empty($this->filteredAttributes) && !in_array($attrType, $this->filteredAttributes)) {
83+
if (!empty($this->filteredAttributes) && !$this->isAttributeInstanceOfFilteredAttribute($attrType)) {
8484
continue;
8585
}
8686
if ($node instanceof Node\Stmt\Class_ || $node instanceof Node\Stmt\Interface_) {
@@ -106,6 +106,16 @@ public function leaveNode(Node $node) {
106106
}
107107
}
108108

109+
private function isAttributeInstanceOfFilteredAttribute(string $attrType) : bool {
110+
foreach ($this->filteredAttributes as $filteredAttribute) {
111+
if (is_a($attrType, $filteredAttribute, true)) {
112+
return true;
113+
}
114+
}
115+
116+
return false;
117+
}
118+
109119
private function getAnnotatedTargetFromClassNode(Node\Stmt\Class_|Node\Stmt\Interface_ $class, int $index) : AnnotatedTarget {
110120
$classType = $class->namespacedName->toString();
111121
return $this->getAnnotatedTarget(fn() => new ReflectionClass($classType), $index);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace Cspray\AnnotatedTarget\Unit;
4+
5+
use Cspray\AnnotatedTargetFixture\Fixtures;
6+
use Cspray\AnnotatedTargetFixture\TargetAttributeImplementation;
7+
use Cspray\AnnotatedTargetFixture\TargetAttributeInterface;
8+
use function Cspray\Typiphy\objectType;
9+
10+
uses(AnnotatedTargetParserTestCase::class);
11+
12+
beforeEach()->withFixtures(Fixtures::targetAttributeInterface())
13+
->withFilteredAttributes(objectType(TargetAttributeInterface::class));
14+
15+
it('counts parsed targets for single class')
16+
->expect(fn() => $this->getTargets())
17+
->toHaveCount(1);
18+
19+
it('ensures all targets are correct type')
20+
->expect(fn() => $this->getTargets())
21+
->toContainOnlyAnnotatedTargets();
22+
23+
it('ensures all targets share target reflection')
24+
->expect(fn() => $this->getTargets())
25+
->toShareTargetReflection();
26+
27+
it('ensures all targets share attribute reflection')
28+
->expect(fn() => $this->getTargets())
29+
->toShareAttributeReflection();
30+
31+
it('ensures all targets share attribute instance')
32+
->expect(fn() => $this->getTargets())
33+
->toShareAttributeInstance();
34+
35+
it('includes target reflection class')
36+
->expect(fn() => $this->getTargets())
37+
->toContainTargetClass(Fixtures::targetAttributeInterface()->targetClass());
38+
39+
it('includes attribute reflection class')
40+
->expect(fn() => $this->getTargets())
41+
->toContainTargetClassWithAttribute(Fixtures::targetAttributeInterface()->targetClass(), objectType(TargetAttributeImplementation::class));
42+
43+
it('includes attribute instance with correct value')
44+
->expect(fn() => $this->getTargets())
45+
->toContainTargetClassWithAttributeInstance(
46+
Fixtures::targetAttributeInterface()->targetClass(),
47+
objectType(TargetAttributeImplementation::class),
48+
fn(TargetAttributeImplementation $classOnly) => $classOnly->value === 'target-attr'
49+
);

0 commit comments

Comments
 (0)