open-code-modeling/php-code-ast

Open Code Modeling PHP 代码抽象语法树


README

基于AST的PHP代码生成。

安装

$ composer require open-code-modeling/php-code-ast

使用

请参阅tests文件夹中的单元测试以获取全面示例。

让我们从一个简单的例子开始,使用ClassBuilder生成一个类

<?php

$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::ONLY_PHP7);
$printer = new PhpParser\PrettyPrinter\Standard(['shortArraySyntax' => true]);

$ast = $parser->parse('');

$classBuilder = OpenCodeModeling\CodeAst\Builder\ClassBuilder::fromScratch('TestClass', 'My\\Awesome\\Service');
$classBuilder
    ->setFinal(true)
    ->setExtends('BaseClass')
    ->setNamespaceImports('Foo\\Bar')
    ->setImplements('\\Iterator', 'Bar');

$nodeTraverser = new PhpParser\NodeTraverser();

$classBuilder->injectVisitors($nodeTraverser, $parser);

print_r($printer->prettyPrintFile($nodeTraverser->traverse($ast)));

将打印以下输出

<?php

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

use Foo\Bar;
final class TestClass extends BaseClass implements \Iterator, Bar
{
}

所有这些操作都是通过PHP AST完成的,并且它会检查AST令牌是否已定义。因此,它不会覆盖您的代码。

您还可以使用代码和PHP AST访问者类通过低级API生成代码。要向上面的类添加一个toInt()方法,您可以通过ClassMethod访问者添加它。

<?php

$method = new OpenCodeModeling\CodeAst\Code\MethodGenerator(
    'toInt',
    [],
    OpenCodeModeling\CodeAst\Code\MethodGenerator::FLAG_PUBLIC,
    new OpenCodeModeling\CodeAst\Code\BodyGenerator($this->parser, 'return $this->myValue;')
);
$method->setReturnType('int');

$nodeTraverser->addVisitor(new OpenCodeModeling\CodeAst\NodeVisitor\ClassMethod($method));

print_r($printer->prettyPrintFile($nodeTraverser->traverse($ast)));

这将打印以下输出。

<?php

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

use Foo\Bar;
final class TestClass extends BaseClass implements \Iterator, Bar
{
    public function toInt() : int
    {
        return $this->myValue;
    }
}

现在,将toInt()方法的主体更改为其他内容。您会看到您的更改将不会被覆盖。

反向使用

还可以从解析的PHP AST创建一个工厂类。您可以通过调用OpenCodeModeling\CodeAst\Builder\ClassBuilder::fromNodes()来创建OpenCodeModeling\CodeAst\Builder\ClassBuilder的一个实例。

<?php
$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::ONLY_PHP7);
$printer = new PhpParser\PrettyPrinter\Standard(['shortArraySyntax' => true]);

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

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

use Foo\Bar;
final class TestClass extends BaseClass implements \Iterator, Bar
{
    private const PRIV = 'private';
}
EOF;


$ast = $parser->parse($expected);

$classBuilder = OpenCodeModeling\CodeAst\Builder\ClassBuilder::fromNodes(...$ast);

$classBuilder->getName(); // TestClass
$classBuilder->getExtends(); // BaseClass
$classBuilder->isFinal(); // true
$classBuilder->isStrict(); // true
$classBuilder->isAbstract(); // false