PAO: Zero-Config JSON Output for PHP Tools That Cuts AI Token Usage by 99.8%
PAO (PHP Agent Output) is a zero-configuration Composer package that automatically detects AI coding agents like Claude Code and Cursor, then switches PHP testing and static analysis tools (Pest, PHPUnit, PHPStan, Rector) to emit compact structured JSON instead of verbose human-readable output, reducing token consumption by up to 99.8% while preserving human terminal experience.
Background: AI Agents Cannot Parse Human-Oriented Output
With the rise of AI coding assistants such as Claude Code, Cursor, Devin, and Gemini CLI, development teams increasingly delegate test execution, static analysis, and refactoring to AI agents. However, the output of standard PHP tools was never designed for machine consumption. For a project with 1,000 test cases, running Pest produces hundreds of lines of progress dots, timing, and memory information — noise that forces the agent to burn tokens on irrelevant data while the actual result (pass/fail) is buried at the end. This output grows linearly with test count, making token waste severe in large projects.
What Is PAO?
PAO (PHP Agent Output) is a framework-agnostic PHP package maintained by the Laravel team under the laravel/ namespace but with zero dependency on Laravel or any framework . Its entire logic is a single rule: detect an AI agent environment → emit structured JSON; otherwise leave output untouched so human experience is unaffected.
Supported tools and versions:
Pest 4–5
PHPUnit 12–13
Paratest (any version)
PHPStan (any version)
Rector (any version)
Supported project types: Laravel, Symfony, Webman, Laminas, Slim, vanilla PHP — any Composer-based PHP project. Detected AI agents: Claude Code, Cursor, Devin, Gemini CLI, and other mainstream assistants.
Installation
One line, no configuration, no code changes: composer require laravel/pao --dev PAO hooks into Composer's files autoload mechanism, so the detection logic runs automatically on every PHP process startup. No phpunit.xml, pest.config.php, custom reporters, or service providers are required.
Effect Comparison
Pest / PHPUnit
Before PAO (agent environment): The agent receives a verbose text stream including progress dots, per-test timings, and a final summary. For 107 tests (1 failure) the output spans dozens of lines.
After PAO (agent environment): The agent receives a compact JSON object:
{
"tool": "pest",
"result": "failed",
"tests": 107,
"passed": 106,
"failed": 1,
"duration_ms": 14080,
"failures": [
{
"test": "Tests\\Feature\\AuthControllerTest > it returns 401 when token expires",
"file": "/var/www/app/tests/Feature/AuthControllerTest.php",
"line": 64,
"message": "Expected response status 401 but received 500.",
"trace": [
"/var/www/app/tests/Feature/AuthControllerTest.php:64"
]
}
]
}Token consumption drops from hundreds of lines to a few dozen; the agent can directly read file and line to pinpoint the failure.
PHPStan Static Analysis
JSON output includes errors count and error_details grouped by file, each with line, message, and identifier (e.g., return.type).
Pest with Coverage Plugin
When --coverage is used, ANSI color codes are stripped and the raw coverage lines are placed in a raw array inside the JSON.
JSON Field Reference
tool(string) — Tool name: pest, phpunit, phpstan, rector,
paratest result(string) — "passed" or
"failed" tests(int) — Total test count passed (int) — Passed count failed (int) — Failed count (present on failure) duration_ms (int) — Execution time in milliseconds failures (array) — Failure details with test, file, line, message,
trace errors(int) — Error count (PHPStan only) error_details (object) — Error details grouped by file (PHPStan only) raw (array) — Extra plugin output (Coverage, Profile, etc.)
Environment Variables
PAO_FORCE=true— Force JSON mode regardless of agent detection PAO_DISABLE=true — Force human-readable output (local debugging only; must not be written into CI, Makefile, or scripts)
Example:
PAO_FORCE=true vendor/bin/pest tests/Feature/TaskControllerTest.phpproduces JSON even in a manual terminal.
Comparison with Traditional Approaches
Raw Pest/PHPUnit — Config cost: None; Output size: ∝ test count; Failure location precision: Medium; Agent friendliness: ⭐
--format=junit XML — Config cost: Requires config; Output size: Large; Failure location precision: High; Agent friendliness: ⭐⭐
Custom Reporter — Config cost: High; Output size: Controllable; Failure location precision: High; Agent friendliness: ⭐⭐⭐
laravel/pao — Config cost: Zero ; Output size: Constant ; Failure location precision: Exact line ; Agent friendliness: ⭐⭐⭐⭐⭐
PAO's key property: output size is constant — 100 tests and 10,000 tests produce nearly identical JSON payloads; only failure details add bytes.
Caveats
PHP version: Requires PHP 8.3+ (uses readonly properties and other modern syntax).
Process interruption: PAO emits JSON in a shutdown_function; if the process is killed (non-graceful exit), output may be incomplete.
Paratest parallelism: PAO includes special handling to correctly merge worker-process output.
Namespace: The laravel/ prefix denotes the maintainer, not a Laravel framework requirement .
CI environments: CI usually lacks agent detection; use PAO_FORCE=true if JSON is needed downstream, but verify the consumer can parse JSON.
Summary
laravel/paois a pragmatic answer for the PHP ecosystem in the AI coding era. It changes no tool behavior, invades no project code, and silently switches output format only when an AI agent is present — transparent to humans, friendly to AI. One-line install, permanent effect, framework-agnostic, zero intrusion.
composer require laravel/pao --devSigned-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
