PHP AST(Abstract Syntax Tree)入門
ASTとは
AST(抽象構文木)は、PHPコードを「文字列」ではなく「構造を持ったツリー」として表現したものです。
コードは一度、字句解析でトークンに分解され、構文解析でASTに組み立てられます。
つまり、ASTを理解するということは、PHPがコードをどう解釈しているかを見ることと同義です。
PHPに存在する2種類のAST
PHPの世界には二系統のASTがあります。
-
Zend AST
PHP本体やCレベルの拡張(php-ast)が使うAST。高速で内部向け。 -
nikic/php-parser AST
Composerで導入できるライブラリ。PHPだけでASTを扱えるようにしたもの。
学習・解析・コード操作をする場合、こちらを使うのが一般的。
今から学ぶのは nikic/php-parser のASTです。
nikic/php-parser ASTの基本構造
このASTでは、すべてのノードが PhpParser\Node を継承しています。
ノードは大きく分けて2種類に分類されます。
Node
├ Stmt(Statement、文)
└ Expr(Expression、式)
Stmtは構造や制御を表し、Exprは値や演算を表します。
例:StmtとExprの違いをコードで見る
if ($x > 0) {
return $x + 1;
}
if自体は Stmt_If
return も Stmt_Return
しかし $x + 1 は Expr_BinaryOp_Plus という式です
つまり「文の中に式が含まれる」という構造になっています。
実際のAST構造イメージ
function add($x, $y) {
return $x + $y;
}
これをASTで見ると概念的にはこうなります。
Stmt_Function(name: add)
├ Param(x)
├ Param(y)
└ Stmt_Return
└ Expr_BinaryOp_Plus
├ Expr_Variable(x)
└ Expr_Variable(y)
こうして階層構造として理解するのがASTです。
よく出てくるExprノード
Expr_Variable → $var
Expr_Assign → $a = $b
Expr_MethodCall → $obj->call()
Expr_StaticCall → Class::call()
Expr_New → new A()
Expr_FuncCall → f()
Expr_ArrayDimFetch → $arr[0]
Expr_BinaryOp_* → +, -, ., && などの演算系
よく出てくるStmtノード
Stmt_Function → function foo() {}
Stmt_Class → class Foo {}
Stmt_Interface → interface i {}
Stmt_ClassMethod → public function bar() {}
Stmt_If → if (...) {}
Stmt_Return → return ...;
Stmt_Foreach / Stmt_For → ループ構造
nikic/php-parserを使ってASTを確認する方法
Composerでインストールします。
composer require nikic/php-parser
最小のAST表示コード例
use PhpParser\ParserFactory;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;
require 'vendor/autoload.php';
$code = <<<'CODE'
<?php
function add($x, $y) {
return $x + $y;
}
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$ast = $parser->parse($code);
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
public function enterNode(Node $node) {
echo $node->getType(), PHP_EOL;
}
});
$traverser->traverse($ast);
これを実行すると、ASTノードの種類が1行ずつ出力されます。
これが「コードを木として見る」第一ステップです。
ASTを理解するための軸
コードを構文ツリーとして見る
StmtとExprを区別する
Node名が表す構文を覚える
ASTをダンプして目で見ることで構造が身体で覚えられる
Discussion