go-php-parser

command module
v0.0.0-...-0d612bf Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 10 Imported by: 0

README

Go PHP Parser

A PHP parser and code style checker written in Go that generates an Abstract Syntax Tree (AST) from PHP source code and applies style rules to generate a report.

Project Target

The long-term target is a production-grade, full PHP static analyser with cold full-project performance comparable to Mago, without sacrificing semantic coverage or diagnostic quality. See Full Static Analyser and Mago-Class Performance Target for the benchmark contract, architecture, milestones, and acceptance gates.

Features

Language Support
  • PHP 8+ syntax
  • Function declarations with parameters
  • Variable declarations and assignments
  • Control structures (if, elseif, else)
  • String literals (single and double quoted)
  • String interpolation
  • Integer and float literals
  • Boolean literals (true, false)
  • Null literal
  • Comments (single-line and doc comments)
  • Basic expressions and operators
AST Features
  • Detailed position tracking (line, column, offset)
  • Hierarchical node structure
  • Support for:
    • Function nodes
    • Variable nodes
    • Parameter nodes
    • Assignment nodes
    • Expression nodes
    • Control structure nodes
    • Comment nodes
    • Literal nodes (string, integer, float, boolean, null)

Installation

git clone https://github.com/yourusername/go-php-parser.git
cd go-php-parser
go mod download

Usage

Option 1

To use the style checker against your codebase, first build a the project

make build

This will generate a binary named go-phpcs

  • Copy this binary, together with config.yaml file in this repository into your project.
  • Modify config.yaml file to target the directory you need PHP style checks.
  • Run the style checker
./go-phpcs

Optionally export the report into a file

./go-phpcs -o report.log
Option 2

Clone your project into a folder within this project.

Update config.yaml with your folder name.

Run the style checks:

make run
Listing All Style Rules

You can list all available style rule codes supported by this tool using the list-style-rules command. This is useful for discovering which rules you can enable or disable in your config.yaml.

Run the following command:

./go-phpcs list-style-rules

This will print a list of all registered style rule codes, for example:

Available style rule codes:
PSR12.Files.EndFileNoTrailingWhitespace
PSR12.Files.EndFileNewline
PSR12.Files.NoMultipleStatementsPerLine
PSR12.Files.NoSpaceBeforeSemicolon
PSR12.Files.NoBlankLineAfterPHPOpeningTag
PSR12.Classes.OpenBraceOnOwnLine
PSR12.Methods.VisibilityDeclared
PSR1.Classes.ClassDeclaration.PascalCase
PSR12.Classes.ClosingBraceOnOwnLine
...

You can then copy any of these codes into your config.yaml under the rules: section to customize which checks are performed.

PSR-12 Style Checks

This parser implements several PSR-12 style checks, including:

  • No trailing whitespace (PSR12.Files.EndFileNoTrailingWhitespace): Disallows trailing whitespace at the end of lines.
  • File must end with a single blank line (PSR12.Files.EndFileNewline): Ensures files end with exactly one blank line.
  • No multiple statements per line (PSR12.Files.NoMultipleStatementsPerLine): Disallows more than one statement (semicolon) per line.
  • No space before semicolon (PSR12.Files.NoSpaceBeforeSemicolon): Disallows any space or tab before a semicolon at the end of a statement.
  • No blank line after opening <?php tag (PSR12.Files.NoBlankLineAfterPHPOpeningTag): Disallows blank lines immediately after the opening PHP tag.
  • Class opening brace on its own line (PSR12.Classes.OpenBraceOnOwnLine): Requires that the opening brace for a class, interface, trait, or enum must appear on its own line, with no leading or trailing whitespace.
  • Method visibility must be declared (PSR12.Methods.VisibilityDeclared): Requires that every class method explicitly declares its visibility (public, protected, or private).

Style issues are reported per file and line, and can be extended by adding new checkers in the style/psr12 package.

Available Style Rules

You can enable or disable specific code style rules using the rules: key in your config.yaml. If no rules are specified, all available rules are run.

List of Available Rules:

  • PSR12.Files.EndFileNoTrailingWhitespace
  • PSR12.Files.EndFileNewline
  • PSR12.Files.NoMultipleStatementsPerLine
  • PSR12.Files.NoSpaceBeforeSemicolon
  • PSR12.Files.NoBlankLineAfterPHPOpeningTag
  • PSR12.Classes.OpenBraceOnOwnLine
  • PSR12.Methods.VisibilityDeclared
  • PSR12.Classes.ClosingBraceOnOwnLine
Rule Code Description
PSR12.Files.EndFileNoTrailingWhitespace Enforces no trailing whitespace on lines
PSR12.Files.EndFileNewline File must end with a single blank line
PSR12.Files.NoMultipleStatementsPerLine Disallows more than one statement (semicolon) per line
PSR12.Files.NoSpaceBeforeSemicolon Disallows any space or tab before a semicolon at the end of a statement
PSR12.Files.NoBlankLineAfterPHPOpeningTag Disallows blank lines after the opening <?php tag
PSR1.Classes.ClassDeclaration.PascalCase Enforces PascalCase for class names
PSR12.Classes.ClosingBraceOnOwnLine Closing brace must be on its own line, and not followed by code or comments. Reports a syntax error if the file contains only a closing brace

Example config.yaml:

path: ./src
extensions:
  - php
ignore:
   - vendor
rules:
  - PSR12.Files.EndFileNoTrailingWhitespace
  - PSR12.Files.EndFileNewline
  - PSR12.Files.NoMultipleStatementsPerLine
  - PSR12.Files.NoSpaceBeforeSemicolon
  - PSR12.Files.NoBlankLineAfterPHPOpeningTag
  - PSR1.Classes.ClassDeclaration.PascalCase
  - PSR12.Classes.ClosingBraceOnOwnLine

Add or remove rule codes under rules: to control which checks are performed. If you don't specify rules it will execute all rules available.

Basic Usage
go run main.go examples/test.php

This will parse the PHP file and output the AST in a tree-like structure.

Directory Scanning & Parallelism

You can scan all PHP files in a directory as defined in config.yaml:

go run main.go

To control parallelism (number of concurrent workers), use the -p flag. By default, the number of workers is set to the number of CPU cores on your machine:

go run main.go -p 4   # Use 4 workers in parallel
Compatibility Metrics

First, fetch the pinned corpora (not committed to this repository — see test_projects/manifest.json):

go run ./cmd/fetch-test-projects

To track parser compatibility progress across the checked-in corpus under test_projects, run:

make compat-metrics

This prints overall file compatibility, per-project compatibility, total parse errors, and a small sample of the first failing files per project.

You can also emit a machine-readable snapshot for tracking over time:

go run ./cmd/compat-metrics -json -output compatibility-report.json

Useful flags:

  • -root to scan a different corpus root
  • -workers to control parallelism
  • -top to control how many failing-file examples are shown per project
Full-Analyser Benchmark

To measure the analysis engine itself (not the style checker) against the checked-in test_projects corpus — index-only, process-cold full analysis, and warm-loop full analysis, with timing, RSS, and diagnostic counts per the full-static-analyser benchmark contract:

go run ./cmd/benchmark --root test_projects/symfony --json --output benchmark-report.json

Or a human-readable summary:

go run ./cmd/benchmark --root test_projects/phpunit

For a selected-path workload, pass the same source/include boundary used by the reference analyser. Paths are relative to --root; missing paths fail instead of silently shrinking the corpus:

go run ./cmd/benchmark \
  --root test_projects/wordpress-develop \
  --paths src,tests,vendor \
  --excludes src/js \
  --json

Cold-full-analysis runs each re-exec the binary as a fresh subprocess (10 by default) so no in-process cache state leaks between measured runs. The parent times the entire child lifetime, including startup, discovery, reads, parsing, indexing, analysis, reduction, and result serialization. Warm-full-analysis loops the indexed analysis pipeline in a single process after one unmeasured warmup iteration. Incremental-edit timing is reported as unsupported — the engine has no incremental invalidation API yet.

Pinned Benchmark Corpora

test_projects/* (other than manifest.json) are fetched on demand, not committed — each is large (tens to hundreds of MB) and Git has no reliable way to pin an external directory's exact revision without either committing its full content or a real submodule. go run ./cmd/fetch-test-projects reads test_projects/manifest.json and checks out each project's exact pinned commit (a shallow, single-commit fetch, not a full clone) into test_projects/<name>, skipping projects already at the pinned commit. The manifest records the Mago benchmark's three required workloads (php-standard-library, wordpress-develop, magento2) alongside this project's own representative framework corpora (Composer, Drupal, Laravel, PHPUnit, Symfony), each with its exact commit per the comparable-performance contract.

go run ./cmd/fetch-test-projects                       # fetch everything in the manifest
go run ./cmd/fetch-test-projects --only psl,magento2    # fetch a subset
go run ./cmd/fetch-test-projects --force                # re-fetch even if already at the pinned commit

To re-pin a project to a newer revision, update its commit (and ref, for readability) in test_projects/manifest.json and re-run with --force.

Useful flags:

  • --root corpus root to scan
  • --paths comma-separated paths within the root to scan
  • --excludes comma-separated paths within the root to exclude
  • --level analysis rule level filter (-1 = run every registered rule)
  • --cold-runs number of measured process-cold runs (contract minimum is 10)
  • --warm-iterations in-process warm-loop iterations, including the unmeasured warmup
  • --skip-cold skip the process-cold subprocess runs for a quick check
  • --cpuprofile/--memprofile write a go tool pprof-compatible CPU or heap profile from a single in-process full-analysis run (bypasses the cold/warm harness so the profiler attaches directly to the profiled work); pair with --profile-iterations to profile several in-process passes at once

After scanning, the tool will print performance statistics:

Scan completed in 1.55 seconds
Total lines scanned: 1653877
Lines per second: 1063784.86
Total parsing errors: 0
HeapAlloc: 148.56 MB
Sys: 298.92 MB
Configuration

File scanning is controlled by config.yaml:

path: ./demo_project
extensions:
  - php
ignore:
  # - vendor
  • path: Directory to scan
  • extensions: File extensions to include
  • ignore: Directories to skip (uncomment to enable)
Programmatic Usage
package main

import (
    "go-php-parser/lexer"
    "go-php-parser/parser"
    "go-php-parser/ast"
)

func main() {
    // Read PHP file
    input := `<?php
    function test($param) {
        echo "Hello, $param!";
    }`

    // Create lexer
    l := lexer.New(input)

    // Create parser
    p := parser.New(l)

    // Parse the input
    nodes := p.Parse()

    // Check for errors
    if len(p.Errors()) > 0 {
        fmt.Println("Parsing errors:")
        for _, err := range p.Errors() {
            fmt.Printf("\t%s\n", err)
        }
        return
    }

    // Print AST
    ast.PrintAST(nodes, 0)
}

Project Structure

go-php-parser/
├── ast/         # AST node definitions
├── lexer/       # Tokenizer implementation
├── parser/      # Parser implementation
├── token/       # Token type definitions
├── examples/    # Example PHP files
└── main.go      # Main entry point

AST Node Types

Core Nodes
  • Node - Base interface for all AST nodes
  • Position - Line/column/offset information
Expression Nodes
  • Identifier - Variable or function names
  • VariableNode - PHP variables ($var)
  • StringLiteral - String literals
  • InterpolatedStringLiteral - Strings with variable interpolation
  • IntegerLiteral - Integer literals
  • FloatLiteral - Floating-point literals
  • BooleanLiteral - Boolean literals (true/false)
  • NullLiteral - Null literal
  • BinaryExpr - Binary expressions
  • FunctionCall - Function calls
Statement Nodes
  • FunctionNode - Function declarations
  • ParameterNode - Function parameters
  • AssignmentNode - Variable assignments
  • ExpressionStmt - Expression statements
  • ReturnNode - Return statements
  • IfNode - If statements
  • ElseIfNode - Elseif clauses
  • ElseNode - Else clauses
  • WhileNode - While loops
  • CommentNode - Comments

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cmd
benchmark command
Command benchmark is the checked-in cold/full/incremental benchmark harness required by docs/full-static-analyser-target.md's M0 exit criteria ("Add the external three-project benchmark harness ...
Command benchmark is the checked-in cold/full/incremental benchmark harness required by docs/full-static-analyser-target.md's M0 exit criteria ("Add the external three-project benchmark harness ...
compat-metrics command
diagnostic-diff command
fetch-test-projects command
Command fetch-test-projects clones or verifies the pinned corpora listed in test_projects/manifest.json into test_projects/<name>.
Command fetch-test-projects clones or verifies the pinned corpora listed in test_projects/manifest.json into test_projects/<name>.
PSR12.Classes.ClosingBraceOnOwnLine
PSR12.Classes.ClosingBraceOnOwnLine

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL