Skip to content

Commit 1eaf5e0

Browse files
Add RequireExtends and RequireImplements attributes
1 parent 1028781 commit 1eaf5e0

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"require": {
2626
"php": ">=8.0",
2727
"nikic/php-parser": "^4 || ^5",
28-
"php-static-analysis/attributes": "^0.1.13 || dev-main"
28+
"php-static-analysis/attributes": "^0.1.14 || dev-main"
2929
},
3030
"require-dev": {
3131
"php-static-analysis/phpstan-extension": "dev-main",

src/AttributeNodeVisitor.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
use PhpStaticAnalysis\Attributes\Property;
2222
use PhpStaticAnalysis\Attributes\PropertyRead;
2323
use PhpStaticAnalysis\Attributes\PropertyWrite;
24+
use PhpStaticAnalysis\Attributes\RequireExtends;
25+
use PhpStaticAnalysis\Attributes\RequireImplements;
2426
use PhpStaticAnalysis\Attributes\Returns;
2527
use PhpStaticAnalysis\Attributes\SelfOut;
2628
use PhpStaticAnalysis\Attributes\Template;
@@ -41,6 +43,7 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
4143
private const ARGS_MANY_IN_USE = "many in use";
4244
private const ARGS_MANY_WITH_NAME = "many with name";
4345
private const ARGS_MANY_WITHOUT_NAME = "many without name";
46+
private const ARGS_MANY_WITHOUT_NAME_AND_PREFIX = "many without name and prexif";
4447

4548
private const ALLOWED_NODE_TYPES = [
4649
Stmt\Class_::class,
@@ -119,6 +122,8 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
119122
Property::class,
120123
PropertyRead::class,
121124
PropertyWrite::class,
125+
RequireExtends::class,
126+
RequireImplements::class,
122127
Template::class,
123128
TemplateContravariant::class,
124129
TemplateCovariant::class,
@@ -136,6 +141,8 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
136141
'Property' => Property::class,
137142
'PropertyRead' => PropertyRead::class,
138143
'PropertyWrite' => PropertyWrite::class,
144+
'RequireExtends' => RequireExtends::class,
145+
'RequireImplements' => RequireImplements::class,
139146
'Returns' => Returns::class,
140147
'SelfOut' => SelfOut::class,
141148
'Template' => Template::class,
@@ -179,6 +186,12 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
179186
PropertyWrite::class => [
180187
'all' => 'property-write',
181188
],
189+
RequireExtends::class => [
190+
'all' => 'require-extends',
191+
],
192+
RequireImplements::class => [
193+
'all' => 'require-implements',
194+
],
182195
Returns::class => [
183196
'all' => 'return',
184197
],
@@ -243,6 +256,12 @@ class AttributeNodeVisitor extends NodeVisitorAbstract
243256
PropertyWrite::class => [
244257
'all' => self::ARGS_MANY_WITH_NAME,
245258
],
259+
RequireExtends::class => [
260+
'all' => self::ARGS_ONE_WITH_PREFIX,
261+
],
262+
RequireImplements::class => [
263+
'all' => self::ARGS_MANY_WITHOUT_NAME_AND_PREFIX,
264+
],
246265
Returns::class => [
247266
'all' => self::ARGS_ONE,
248267
],
@@ -365,6 +384,12 @@ public function enterNode(Node $node)
365384
$tagCreated = true;
366385
}
367386
break;
387+
case self::ARGS_MANY_WITHOUT_NAME_AND_PREFIX:
388+
foreach ($args as $arg) {
389+
$tagsToAdd[] = $this->createTag($nodeType, $attributeName, $arg, prefix: $this->toolType);
390+
$tagCreated = true;
391+
}
392+
break;
368393
case self::ARGS_MANY_IN_USE:
369394
foreach ($args as $arg) {
370395
if ($arg->value instanceof String_) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\NodeVisitor;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Attribute;
7+
use PhpParser\Node\AttributeGroup;
8+
use PhpParser\Node\Name\FullyQualified;
9+
use PhpStaticAnalysis\Attributes\RequireExtends;
10+
11+
class RequireExtendsAttributeNodeVisitorTest extends AttributeNodeVisitorTestBase
12+
{
13+
public function testAddsRequireExtendsPHPDoc(): void
14+
{
15+
$node = new Node\Stmt\Trait_('Test');
16+
$this->addRequireExtendsAttributeToNode($node);
17+
$this->nodeVisitor->enterNode($node);
18+
$docText = $this->getDocText($node);
19+
$this->assertEquals("/**\n * @require-extends RequireClass\n */", $docText);
20+
}
21+
22+
private function addRequireExtendsAttributeToNode(Node\Stmt\Trait_ $node): void
23+
{
24+
$args = [
25+
new Node\Arg(new Node\Scalar\String_('RequireClass'))
26+
];
27+
$attributeName = new FullyQualified(RequireExtends::class);
28+
$attribute = new Attribute($attributeName, $args);
29+
$node->attrGroups = array_merge($node->attrGroups, [new AttributeGroup([$attribute])]);
30+
}
31+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
namespace test\PhpStaticAnalysis\NodeVisitor;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Attribute;
7+
use PhpParser\Node\AttributeGroup;
8+
use PhpParser\Node\Name\FullyQualified;
9+
use PhpStaticAnalysis\Attributes\RequireImplements;
10+
11+
class RequireImplementsAttributeNodeVisitorTest extends AttributeNodeVisitorTestBase
12+
{
13+
public function testAddsRequireImplementsPHPDoc(): void
14+
{
15+
$node = new Node\Stmt\Trait_('Test');
16+
$this->addRequireImplementsAttributesToNode($node);
17+
$this->nodeVisitor->enterNode($node);
18+
$docText = $this->getDocText($node);
19+
$this->assertEquals("/**\n * @require-implements RequireInterface\n */", $docText);
20+
}
21+
22+
public function testAddsSeveralRequireImplementsPHPDocs(): void
23+
{
24+
$node = new Node\Stmt\Trait_('Test');
25+
$this->addRequireImplementsAttributesToNode($node, 2);
26+
$this->nodeVisitor->enterNode($node);
27+
$docText = $this->getDocText($node);
28+
$this->assertEquals("/**\n * @require-implements RequireInterface\n * @require-implements RequireInterface\n */", $docText);
29+
}
30+
31+
public function testAddsMultipleRequireImplementsPHPDocs(): void
32+
{
33+
$node = new Node\Stmt\Trait_('Test');
34+
$this->addRequireImplementsAttributesToNode($node);
35+
$this->addRequireImplementsAttributesToNode($node);
36+
$this->nodeVisitor->enterNode($node);
37+
$docText = $this->getDocText($node);
38+
$this->assertEquals("/**\n * @require-implements RequireInterface\n * @require-implements RequireInterface\n */", $docText);
39+
}
40+
41+
private function addRequireImplementsAttributesToNode(Node\Stmt\Trait_ $node, int $num = 1): void
42+
{
43+
$value = new Node\Scalar\String_('RequireInterface');
44+
$args = [];
45+
for ($i = 0; $i < $num; $i++) {
46+
$args[] = new Node\Arg($value);
47+
}
48+
$attributeName = new FullyQualified(RequireImplements::class);
49+
$attribute = new Attribute($attributeName, $args);
50+
$node->attrGroups = array_merge($node->attrGroups, [new AttributeGroup([$attribute])]);
51+
}
52+
}

tests/TemplateExtendsAttributeNodeVisitorTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace test\PhpStaticAnalysis\NodeVisitor;
44

5-
use Exception;
65
use PhpParser\Node;
76
use PhpParser\Node\Attribute;
87
use PhpParser\Node\AttributeGroup;

0 commit comments

Comments
 (0)