前言
这几天初步了解了一下 PHP-Parser,它是一个由 PHP 语言编写的 PHP 解析器,支持将残缺的 PHP 代码转换为 AST、对 AST 进行操作以及将 AST 转换为格式友好的 PHP 源代码。功能可以说是十分强大且灵活。前文提到的 AST 即抽象语法树,是源代码语法结构的一种抽象表示,可以认为 AST 与其对应的 PHP 源代码是等价的。
项目地址:nikic/PHP-Parser,十分感谢 nikic 大佬以及其他 Contributors 为我们提供了如此方便强大的库。
使用 composer 安装:
php composer.phar require nikic/php-parser
生成 AST
我们需要利用 AST 便利的特性,当然就需要先把原生 PHP 代码转换为一棵 AST。
use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;
$code = <<<'CODE'
<?php
function test() {
var_dump(0xaa);
}
test();
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
$dumper = new NodeDumper;
echo $dumper->dump($ast) . "\n";
这就是一个最简单的例子,它可以生成 $code
中代码所对应的 AST,同时通过 NodeDumper
,我们可以轻松直观地看到它的结构。
操作 AST
PHP-Parser 提供了 NodeVisitor
和 NodeFinder
两种遍历节点的接口,后者是前者的一种简洁用法,本质上它们都需要使用 NodeTraverser
。这里我们以适用范围更广的 NodeVisitor
举例。
AST 就像是一把精巧的手术刀,许多大规模改动源代码的问题都可以通过它简便地解决。操作 AST 时就像是在对源代码动手术,以节点为单位批量解决许多看似难以解决的问题。
例如,我们需要把代码中所有的十六进制数字换成十进制书写,便于维护,在源代码很大时这个工作将会很困难,但利用 PHP-Parser 即可轻松处理。我们用 NodeVisitor
处理一下:
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Node\Scalar\LNumber;
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
public function leaveNode(Node $node) {
if ($node instanceof LNumber) { // 十六进制数字修复为十进制
return new LNumber($node->value,array('kind'=>LNumber::KIND_DEC));
}
}
});
$ast = $traverser->traverse($ast);
对于源代码中的数字,PHP-Parser 将其解析为 LNumber
节点,我们重新创建一个 kind
为十进制、值与原先相同的 LNumber
节点即可。
leaveNode
方法将在 NodeVisitor
离开节点时执行,返回值将改变该节点的值。对于 Expression
节点,返回常量 NodeTraverser::REMOVE_NODE
可以移除该节点。
生成代码
PHP-Parser 在操作完毕后,一般需要将抽象的数据结构 AST
转换回具体的源代码,此时可以使用 PrettyPrinter
。经过 PrettyPrinter
处理后,可以得到美化(当然也可以是自定义格式)后的源代码。
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
$newCode = $prettyPrinter->prettyPrintFile($ast);
版权声明:本文是原创文章,版权归 无限UCW 所有。
本文链接:https://ucw.moe/archives/php-parser-ast.html
本站所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由地转载和修改,但请务必注明文章来源并且不可用于商业目的。