Skip to content

Fix existing check in class constant node visitor #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ jobs:
run: |
composer install --no-progress --prefer-dist --optimize-autoloader

#- name: Run Tests
# run: php vendor/bin/phpunit --coverage-text
- name: Run Tests
run: php vendor/bin/phpunit --coverage-text

coding-standard:
name: Coding Standard
Expand Down
81 changes: 46 additions & 35 deletions src/NodeVisitor/ClassConstant.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

namespace OpenCodeModeling\CodeAst\NodeVisitor;

use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
use OpenCodeModeling\CodeAst\Code\IdentifierGenerator;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeVisitorAbstract;

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

public function enterNode(Node $node)
public static function forClassConstant(
string $constantName,
string $constantValue,
int $flags = ClassConstGenerator::FLAG_PUBLIC
): ClassConstant {
return new self(
new IdentifierGenerator(
$constantName,
new ClassConstGenerator($constantName, $constantValue, $flags)
)
);
}

public function afterTraverse(array $nodes): ?array
{
if ($node instanceof Class_) {
if ($definitions = $this->constant($node)) {
$newNodes = [];

foreach ($nodes as $node) {
$newNodes[] = $node;

if ($node instanceof Namespace_) {
foreach ($node->stmts as $stmt) {
if ($stmt instanceof Class_) {
if ($this->checkConstantExists($stmt)) {
return null;
}
$stmt->stmts = \array_merge(
$this->lineGenerator->generate(),
$stmt->stmts
);
}
}
} elseif ($node instanceof Class_) {
if ($this->checkConstantExists($node)) {
return null;
}
$node->stmts = \array_merge(
$definitions,
$this->lineGenerator->generate(),
$node->stmts
);

return $node;
}
}

return null;
return $newNodes;
}

private function isAlreadyDefined(
string $lineIdentifier,
Class_ $node
): bool {
$alreadyDefined = false;

private function checkConstantExists(Class_ $node): bool
{
foreach ($node->stmts as $stmt) {
if (! $stmt instanceof Node\Stmt\ClassConst) {
continue;
}

if ($lineIdentifier === $stmt->consts[0]->name->name) {
$alreadyDefined = true;
break;
if ($stmt instanceof Node\Stmt\ClassConst
&& $stmt->consts[0]->name->name === $this->lineGenerator->getIdentifier()
) {
return true;
}
}

return $alreadyDefined;
}

private function constant(Class_ $node): ?array
{
$isAlreadyDefined = $this->isAlreadyDefined(
$this->lineGenerator->getIdentifier(),
$node
);

if ($isAlreadyDefined === false) {
return $this->lineGenerator->generate();
}

return null;
return false;
}
}
144 changes: 144 additions & 0 deletions tests/NodeVisitor/ClassConstantTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php

declare(strict_types=1);

namespace OpenCodeModelingTest\CodeAst\NodeVisitor;

use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
use OpenCodeModeling\CodeAst\Code\ClassGenerator;
use OpenCodeModeling\CodeAst\NodeVisitor\ClassConstant;
use OpenCodeModeling\CodeAst\NodeVisitor\ClassFile;
use OpenCodeModeling\CodeAst\NodeVisitor\ClassNamespace;
use OpenCodeModeling\CodeAst\NodeVisitor\StrictType;
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\BooleanFactory;
use PhpParser\NodeTraverser;
use PhpParser\Parser;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter\Standard;
use PHPUnit\Framework\TestCase;

final class ClassConstantTest extends TestCase
{
/**
* @var Parser
*/
private $parser;

/**
* @var Standard
*/
private $printer;

public function setUp(): void
{
$this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
$this->printer = new Standard(['shortArraySyntax' => true]);
}

/**
* @test
*/
public function it_generates_constant_for_class_for_empty_file(): void
{
$ast = $this->parser->parse('');

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new StrictType());
$nodeTraverser->addVisitor(new ClassFile(new ClassGenerator('TestClass')));
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string'));

$expected = <<<'EOF'
<?php

declare (strict_types=1);
class TestClass
{
public const TYPE_STRING = 'string';
}
EOF;

$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
}

/**
* @test
*/
public function it_generates_constant_for_class_for_existing_file(): void
{
$ast = $this->parser->parse('<?php class TestClass {}');

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string', ClassConstGenerator::FLAG_PRIVATE));

$expected = <<<'EOF'
<?php

class TestClass
{
private const TYPE_STRING = 'string';
}
EOF;

$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
}

/**
* @test
*/
public function it_generates_constant_for_class_with_namespace_for_empty_file(): void
{
$ast = $this->parser->parse('');

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(new StrictType());
$nodeTraverser->addVisitor(new ClassNamespace('My\\Awesome\\Service'));
$nodeTraverser->addVisitor(new ClassFile(new ClassGenerator('TestClass')));
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string'));

$expected = <<<'EOF'
<?php

declare (strict_types=1);
namespace My\Awesome\Service;

class TestClass
{
public const TYPE_STRING = 'string';
}
EOF;

$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
}

/**
* @test
*/
public function it_generates_constant_for_class_with_namespace_for_existing_file(): void
{
$code = <<<'PHP'
<?php

namespace My\Awesome\Service;

class TestClass {}
PHP;

$ast = $this->parser->parse($code);

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor(ClassConstant::forClassConstant('TYPE_STRING', 'string', ClassConstGenerator::FLAG_PRIVATE));

$expected = <<<'EOF'
<?php

namespace My\Awesome\Service;

class TestClass
{
private const TYPE_STRING = 'string';
}
EOF;

$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
}
}