Skip to content

Commit 10dd6b3

Browse files
authored
Merge pull request #2 from open-code-modeling/fix/class-constant-visitor
Fix existing check in class constant node visitor
2 parents 636e7b4 + cc5f96a commit 10dd6b3

File tree

3 files changed

+192
-37
lines changed

3 files changed

+192
-37
lines changed

.github/workflows/integration.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ jobs:
4545
run: |
4646
composer install --no-progress --prefer-dist --optimize-autoloader
4747
48-
#- name: Run Tests
49-
# run: php vendor/bin/phpunit --coverage-text
48+
- name: Run Tests
49+
run: php vendor/bin/phpunit --coverage-text
5050

5151
coding-standard:
5252
name: Coding Standard

src/NodeVisitor/ClassConstant.php

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010

1111
namespace OpenCodeModeling\CodeAst\NodeVisitor;
1212

13+
use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
1314
use OpenCodeModeling\CodeAst\Code\IdentifierGenerator;
1415
use PhpParser\Node;
1516
use PhpParser\Node\Stmt\Class_;
17+
use PhpParser\Node\Stmt\Namespace_;
1618
use PhpParser\NodeVisitorAbstract;
1719

1820
final class ClassConstant extends NodeVisitorAbstract
@@ -27,53 +29,62 @@ public function __construct(IdentifierGenerator $lineGenerator)
2729
$this->lineGenerator = $lineGenerator;
2830
}
2931

30-
public function enterNode(Node $node)
32+
public static function forClassConstant(
33+
string $constantName,
34+
string $constantValue,
35+
int $flags = ClassConstGenerator::FLAG_PUBLIC
36+
): ClassConstant {
37+
return new self(
38+
new IdentifierGenerator(
39+
$constantName,
40+
new ClassConstGenerator($constantName, $constantValue, $flags)
41+
)
42+
);
43+
}
44+
45+
public function afterTraverse(array $nodes): ?array
3146
{
32-
if ($node instanceof Class_) {
33-
if ($definitions = $this->constant($node)) {
47+
$newNodes = [];
48+
49+
foreach ($nodes as $node) {
50+
$newNodes[] = $node;
51+
52+
if ($node instanceof Namespace_) {
53+
foreach ($node->stmts as $stmt) {
54+
if ($stmt instanceof Class_) {
55+
if ($this->checkConstantExists($stmt)) {
56+
return null;
57+
}
58+
$stmt->stmts = \array_merge(
59+
$this->lineGenerator->generate(),
60+
$stmt->stmts
61+
);
62+
}
63+
}
64+
} elseif ($node instanceof Class_) {
65+
if ($this->checkConstantExists($node)) {
66+
return null;
67+
}
3468
$node->stmts = \array_merge(
35-
$definitions,
69+
$this->lineGenerator->generate(),
3670
$node->stmts
3771
);
38-
39-
return $node;
4072
}
4173
}
4274

43-
return null;
75+
return $newNodes;
4476
}
4577

46-
private function isAlreadyDefined(
47-
string $lineIdentifier,
48-
Class_ $node
49-
): bool {
50-
$alreadyDefined = false;
51-
78+
private function checkConstantExists(Class_ $node): bool
79+
{
5280
foreach ($node->stmts as $stmt) {
53-
if (! $stmt instanceof Node\Stmt\ClassConst) {
54-
continue;
55-
}
56-
57-
if ($lineIdentifier === $stmt->consts[0]->name->name) {
58-
$alreadyDefined = true;
59-
break;
81+
if ($stmt instanceof Node\Stmt\ClassConst
82+
&& $stmt->consts[0]->name->name === $this->lineGenerator->getIdentifier()
83+
) {
84+
return true;
6085
}
6186
}
6287

63-
return $alreadyDefined;
64-
}
65-
66-
private function constant(Class_ $node): ?array
67-
{
68-
$isAlreadyDefined = $this->isAlreadyDefined(
69-
$this->lineGenerator->getIdentifier(),
70-
$node
71-
);
72-
73-
if ($isAlreadyDefined === false) {
74-
return $this->lineGenerator->generate();
75-
}
76-
77-
return null;
88+
return false;
7889
}
7990
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenCodeModelingTest\CodeAst\NodeVisitor;
6+
7+
use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
8+
use OpenCodeModeling\CodeAst\Code\ClassGenerator;
9+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassConstant;
10+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassFile;
11+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassNamespace;
12+
use OpenCodeModeling\CodeAst\NodeVisitor\StrictType;
13+
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\BooleanFactory;
14+
use PhpParser\NodeTraverser;
15+
use PhpParser\Parser;
16+
use PhpParser\ParserFactory;
17+
use PhpParser\PrettyPrinter\Standard;
18+
use PHPUnit\Framework\TestCase;
19+
20+
final class ClassConstantTest extends TestCase
21+
{
22+
/**
23+
* @var Parser
24+
*/
25+
private $parser;
26+
27+
/**
28+
* @var Standard
29+
*/
30+
private $printer;
31+
32+
public function setUp(): void
33+
{
34+
$this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
35+
$this->printer = new Standard(['shortArraySyntax' => true]);
36+
}
37+
38+
/**
39+
* @test
40+
*/
41+
public function it_generates_constant_for_class_for_empty_file(): void
42+
{
43+
$ast = $this->parser->parse('');
44+
45+
$nodeTraverser = new NodeTraverser();
46+
$nodeTraverser->addVisitor(new StrictType());
47+
$nodeTraverser->addVisitor(new ClassFile(new ClassGenerator('TestClass')));
48+
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string'));
49+
50+
$expected = <<<'EOF'
51+
<?php
52+
53+
declare (strict_types=1);
54+
class TestClass
55+
{
56+
public const TYPE_STRING = 'string';
57+
}
58+
EOF;
59+
60+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
61+
}
62+
63+
/**
64+
* @test
65+
*/
66+
public function it_generates_constant_for_class_for_existing_file(): void
67+
{
68+
$ast = $this->parser->parse('<?php class TestClass {}');
69+
70+
$nodeTraverser = new NodeTraverser();
71+
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string', ClassConstGenerator::FLAG_PRIVATE));
72+
73+
$expected = <<<'EOF'
74+
<?php
75+
76+
class TestClass
77+
{
78+
private const TYPE_STRING = 'string';
79+
}
80+
EOF;
81+
82+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
83+
}
84+
85+
/**
86+
* @test
87+
*/
88+
public function it_generates_constant_for_class_with_namespace_for_empty_file(): void
89+
{
90+
$ast = $this->parser->parse('');
91+
92+
$nodeTraverser = new NodeTraverser();
93+
$nodeTraverser->addVisitor(new StrictType());
94+
$nodeTraverser->addVisitor(new ClassNamespace('My\\Awesome\\Service'));
95+
$nodeTraverser->addVisitor(new ClassFile(new ClassGenerator('TestClass')));
96+
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string'));
97+
98+
$expected = <<<'EOF'
99+
<?php
100+
101+
declare (strict_types=1);
102+
namespace My\Awesome\Service;
103+
104+
class TestClass
105+
{
106+
public const TYPE_STRING = 'string';
107+
}
108+
EOF;
109+
110+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
111+
}
112+
113+
/**
114+
* @test
115+
*/
116+
public function it_generates_constant_for_class_with_namespace_for_existing_file(): void
117+
{
118+
$code = <<<'PHP'
119+
<?php
120+
121+
namespace My\Awesome\Service;
122+
123+
class TestClass {}
124+
PHP;
125+
126+
$ast = $this->parser->parse($code);
127+
128+
$nodeTraverser = new NodeTraverser();
129+
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string', ClassConstGenerator::FLAG_PRIVATE));
130+
131+
$expected = <<<'EOF'
132+
<?php
133+
134+
namespace My\Awesome\Service;
135+
136+
class TestClass
137+
{
138+
private const TYPE_STRING = 'string';
139+
}
140+
EOF;
141+
142+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
143+
}
144+
}

0 commit comments

Comments
 (0)