nikic/php-parser
Parse PHP code into an Abstract Syntax Tree (AST) for static analysis, manipulation, and code generation. Supports PHP 5.x to 8.4, handles errors gracefully, and preserves formatting during AST-to-code conversion. Easily traverse, modify, and convert ASTs back to PHP, with JSON serialization support...
Installation:
composer require nikic/php-parser
Use nikic/php-parser:^5.0 for PHP 7.4+ or nikic/php-parser:^4.0 for PHP 5.2-8.3 compatibility.
First Use Case: Parse a PHP file into an AST and dump it:
use PhpParser\ParserFactory;
$parser = (new ParserFactory())->createForNewestSupportedVersion();
$ast = $parser->parse(file_get_contents('MyClass.php'));
print_r($ast);
Where to Look First:
AST Traversal & Modification:
Use NodeTraverser with custom NodeVisitor to modify nodes:
$traverser = new NodeTraverser();
$traverser->addVisitor(new class extends NodeVisitorAbstract {
public function enterNode(Node $node) {
if ($node instanceof Stmt_ClassMethod) {
$node->returnType = new Name('void');
}
}
});
$ast = $traverser->traverse($ast);
Code Generation:
Use BuilderFactory to construct nodes programmatically:
$builder = (new BuilderFactory())->create();
$method = $builder->method('foo')
->makePublic()
->addParam($builder->param('bar'))
->addStmt($builder->expr($builder->funcCall('echo', ['bar'])));
Name Resolution: Resolve fully qualified names in the AST:
$resolver = new NameResolver();
$resolver->resolve(new NodeFinder(), $ast);
Pretty Printing: Convert AST back to PHP with formatting:
$printer = new PrettyPrinter\Standard();
$code = $printer->prettyPrintFile($ast);
file_put_contents('output.php', $code);
Laravel Service Provider: Bind the parser and printer as singletons:
$this->app->singleton(ParserFactory::class, fn() => new ParserFactory());
$this->app->singleton(PrettyPrinterAbstract::class, fn() => new PrettyPrinter\Standard());
Artisan Commands: Create a command to parse/modify files:
class ParseCommand extends Command {
protected $signature = 'parse {file}';
public function handle() {
$parser = app(ParserFactory::class)->createForNewestSupportedVersion();
$ast = $parser->parse(file_get_contents($this->argument('file')));
$this->info($ast[0]->getDocComment() ?? 'No docblock');
}
}
Event Listeners:
Hook into Laravel events (e.g., Illuminate\Foundation\Bootstrap\LoadConfiguration) to parse config files dynamically.
PHP Version Mismatch:
createForOldestSupportedVersion() or createForNewestSupportedVersion() explicitly.Cyclic References:
weakReferences in NodeConnectingVisitor to avoid memory leaks:
$visitor = new NodeConnectingVisitor();
$visitor->setWeakReferences(true);
Formatting Preservation:
PrettyPrinter\Standard with preserveWhitespace: true for partial modifications:
$printer = new PrettyPrinter\Standard(['preserveWhitespace' => true]);
Attribute Handling:
attrGroups. Ensure compatibility with target PHP version (e.g., PHP 7.4+ for multi-line attributes).Error Recovery:
ParserFactory::PARSER_EARLY_STOP_ON_ERROR:
$parser = (new ParserFactory())->create(
ParserFactory::PARSER_EARLY_STOP_ON_ERROR
);
Node Dumping:
Use NodeDumper for debugging:
$dumper = new NodeDumper();
$dumper->setDumpNodeAnnotations(true); // Show node positions
echo $dumper->dump($ast);
Xdebug Overhead: Disable Xdebug during parsing for performance:
if (extension_loaded('xdebug')) {
ini_set('xdebug.mode', 'off');
}
Custom Pretty Printer:
Extend PrettyPrinterAbstract to modify formatting:
class CustomPrinter extends PrettyPrinterAbstract {
public function pMethod(Stmt_ClassMethod $node) {
return '/* custom */ ' . parent::pMethod($node);
}
}
Custom Node Visitors:
Implement NodeVisitor for complex transformations:
class AddDocblockVisitor extends NodeVisitorAbstract {
public function enterNode(Node $node) {
if ($node instanceof Stmt_Class) {
$node->setAttribute('docComment', '/** @deprecated */');
}
}
}
Lexer Hooks:
Extend Lexer for custom token handling (e.g., custom PHPDoc tags):
$lexer = new Lexer(['usedAttributes' => ['customTag']]);
Constant Evaluation:
Use Evaluator for runtime evaluation of constant expressions:
$evaluator = new Evaluator();
$value = $evaluator->evaluate($ast[0]->expr, []);
Blade Template Parsing:
Parse Blade files by stripping @ directives first:
$code = preg_replace('/@\w+\(.*?\)/', '', file_get_contents('view.blade.php'));
Migration File Analysis: Use the parser to validate migrations before execution:
$parser = app(ParserFactory::class)->createForNewestSupportedVersion();
try {
$ast = $parser->parse(file_get_contents('database/migrations/...'));
// Validate AST structure (e.g., no raw SQL in `up()`)
} catch (Error $e) {
throw new RuntimeException("Invalid migration syntax: " . $e->getMessage());
}
Dynamic Configuration: Parse and merge config files at runtime:
$parser = app(ParserFactory::class)->createForOldestSupportedVersion();
$configAst = $parser->parse(file_get_contents('config/custom.php'));
// Merge with default config using AST traversal
How can I help you explore Laravel packages today?