Most developers associate PHP with Laravel, WordPress, or writing controllers and views. But deep inside, PHP offers something powerful yet underused: Abstract Syntax Trees (AST).
In this post, we’ll explore what AST is, why it’s important, and how to leverage PHP’s AST for static analysis, code transformation, and custom tooling — something that can truly elevate you as a PHP backend engineer.
What Is an Abstract Syntax Tree (AST)?
An AST is a tree representation of the abstract syntactic structure of code. Every node in the tree denotes a construct in the source code.
Example:
$sum = $a + $b;In AST, this is interpreted as:
- Assignment (
=)
- Variable
$sum- Expression: Binary Operation (
+)
- Left:
$a- Right:
$bThis structure helps us understand code semantically, not just as raw text. It’s the same idea used in compilers, linters, and static analyzers.
Why Should a PHP Developer Care?
PHP added AST support starting from PHP 7 via the php-ast extension. You can now analyze PHP code in a structured way without needing to reimplement a parser yourself.
Use cases include:
- Static analysis (like PHPStan or Psalm)
- Automatic code refactoring
- Code cleanup / transformation
- Custom linters for project-specific rules
- Test coverage mapping
Getting Started with php-ast
1. Install the extension
pecl install ast
2. Parse PHP code
Use nikic/php-parser, a library that converts PHP code into AST nodes you can walk through.
use PhpParser\ParserFactory;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$ast = $parser->parse('<?php $sum = $a + $b;');
Now you can recursively loop through $ast and inspect node types, like Expr_Assign, Expr_BinaryOp_Plus, etc.
Now lets dive deeper with this Extension
Most PHP developers know about tools like PHP_CodeSniffer or PHPStan, but few realize what’s happening under the hood — these tools rely on something powerful and underutilized: the Abstract Syntax Tree (AST).
In this article, we’ll use AST to build a simple complexity analyzer that counts how “complex” a PHP function is — not by line count, but by its control structures: if, for, while, switch, etc.
What is Code Complexity?
Code complexity, particularly cyclomatic complexity, measures the number of independent execution paths through a function. More branches = more test cases = harder to maintain.
For example:
function check($x) {
if ($x > 0) {
echo "Positive";
} else {
echo "Not positive";
}
}
This function has 2 paths = complexity 3 (1 base + 2 if).
Using nikic/php-parser, we’ll walk through PHP code and count:
if,elseif,elsefor,foreach,whilecase,defaultinswitch&&,||in conditions (optional)
Step-by-Step: Count Complexity with PHP AST
1. Install the parser:
composer require nikic/php-parser
Create an analyzer script:
use PhpParser\ParserFactory;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\Node;
require 'vendor/autoload.php';
$code = <<<'CODE'
<?php
function example($x) {
if ($x > 0) {
for ($i = 0; $i < $x; $i++) {
echo $i;
}
} else if ($x < 0) {
echo "Negative";
} else {
echo "Zero";
}
}
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
$ast = $parser->parse($code);
$traverser = new NodeTraverser();
$visitor = new class extends NodeVisitorAbstract {
public $complexity = 1; // base complexity
public function enterNode(Node $node) {
if ($node instanceof Node\Stmt\If_
|| $node instanceof Node\Stmt\ElseIf_
|| $node instanceof Node\Stmt\For_
|| $node instanceof Node\Stmt\Foreach_
|| $node instanceof Node\Stmt\While_
|| $node instanceof Node\Stmt\Do_
|| $node instanceof Node\Stmt\Case_
|| $node instanceof Node\Expr\BinaryOp\BooleanAnd
|| $node instanceof Node\Expr\BinaryOp\BooleanOr) {
$this->complexity++;
}
}
};
$traverser->addVisitor($visitor);
$traverser->traverse($ast);
echo "Complexity: " . $visitor->complexity . PHP_EOL;
Output:
Complexity: 5
Perfect! This shows your function has 5 paths — and might be due for refactoring if it gets higher.