> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP AST

> 讲解 PHP AST（抽象语法树）的基础、PHP 内部 AST 的定位，以及基于 nikic/PHP-Parser 的实用代码解析与转换模式。

## 什么是 PHP AST

AST（Abstract Syntax Tree，抽象语法树）是将源码分解为「具有语义结构」的树。

在你编写代码生成工具、静态分析工具、自动重构工具时，AST 几乎是必备基础。用字符串替换容易出错的转换，用 AST 就能按「function call」「class declaration」「`use` statement」等语法单元安全处理。

```mermaid theme={null}
graph TD
    A["<?php<br>echo add(1, 2);"] --> B["Stmt\\Expression"]
    B --> C["Expr\\FuncCall: add"]
    C --> D["Arg: 1"]
    C --> E["Arg: 2"]
```

## PHP 的内部 AST

PHP 7 以后的 Zend Engine 并不直接把 PHP 代码转换为 opcode，而是先转换为内部 AST 再编译。

* 用 `php -r` 执行的一行代码，内部也走完全相同的编译管线
* OPcache 会缓存最终生成的 opcode 供复用
* 在通常的应用开发中，几乎没有机会直接操作这份内部 AST

也就是说，当你想「用 AST 编辑代码」时，实用的做法不是操作 PHP 运行时内部 AST，而是使用用户态可用的 AST 库。

## nikic/PHP-Parser 包

[nikic/PHP-Parser](https://github.com/nikic/PHP-Parser) 是将 PHP 代码解析、遍历、再生成为 AST 的标准库。README 与官方文档介绍的基本流程分为 3 步。

### 安装

```bash theme={null}
composer require nikic/php-parser
```

### 基本解析示例（代码转 AST）

```php theme={null}
<?php

use PhpParser\Error;
use PhpParser\ParserFactory;

$code = <<<'CODE'
<?php
function greet(string $name): void {
    echo "Hello, {$name}";
}
CODE;

$parser = (new ParserFactory())->createForNewestSupportedVersion();

try {
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}\n";
    return;
}
```

### 用 NodeVisitor 模式遍历、修改 AST

```php theme={null}
<?php

use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;

$traverser = new NodeTraverser();

$traverser->addVisitor(new class extends NodeVisitorAbstract {
    public function leaveNode(Node $node)
    {
        if ($node instanceof Node\Scalar\Int_) {
            return new Node\Scalar\String_((string) $node->value);
        }
    }
});

$modifiedAst = $traverser->traverse($ast);
```

继承 `NodeVisitorAbstract` 后只需实现所需的钩子（`enterNode` / `leaveNode`）。复杂的转换建议在 `enterNode` 收集信息，在 `leaveNode` 做替换。

### 用 Pretty Printer 再生成代码

```php theme={null}
<?php

use PhpParser\PrettyPrinter;

$prettyPrinter = new PrettyPrinter\Standard();
$newCode = $prettyPrinter->prettyPrintFile($modifiedAst);

echo $newCode;
```

采用这套流程，你的转换对象就从「作为字符串的代码」变成了「作为语法树的代码」。

## Laravel/Chisel 中的使用示例

[laravel/chisel](/zh/blog/chisel-introduction) 是用于对 Starter Kit 做后处理裁剪的库。它在 `composer.json` 中依赖 `nikic/php-parser` 的 5.x 系列。

Chisel 中的 `Laravel\Chisel\Ast\Source` 按下列顺序进行 AST 编辑：

1. 用 `ParserFactory::createForNewestSupportedVersion()` 解析代码
2. 向 `NodeTraverser` 添加多个 Visitor（如 `RemoveImportVisitor`）执行转换
3. 用 `PhpParser\PrettyPrinter\Standard` 的 `printFormatPreserving()` 保留原格式回写

通过这种设计，`use` 语句、trait、interface 的移除都可以基于语法而非文本替换来安全执行。

## 用例

使用 AST 可以让以下开发更好推进：

* 代码生成 CLI（生成模板后按语法级别编辑）
* 静态分析工具（检测特定语法、发现违规）
* 自动重构（API 迁移、命名调整的半自动化）
* 项目模板的后处理（如 Chisel 的功能移除/替换）

## 小结

在一般的 Laravel 应用开发中，PHP AST 并非日常都会用到的功能。

不过，若你要做工具或包开发，AST 会成为实现「不易出错、可复现代码变更」的强大基础。先用 `nikic/php-parser` 的 Parser、Visitor、Pretty Printer 三件套在一个小型 CLI 上尝试即可。


## Related topics

- [Laravel Chisel — 启动套件的安装后脚本库](/zh/blog/chisel-introduction.md)
- [PHP 属性](/zh/advanced/php-attributes.md)
- [PHP FFI](/zh/advanced/ffi.md)
- [VOICEVOX Core for PHP](/zh/packages/voicevox-core-php/index.md)
- [PHP Reflection API](/zh/advanced/php-reflection.md)
