Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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('<?php');

$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('<?php');

$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)));
}
}