voku/phpstan-agent-format
Composer 安装命令:
composer require voku/phpstan-agent-format
包简介
PHPStan custom error formatter optimized for coding-agent repair loops.
README 文档
README
🚮 Stop feeding AI agents console garbage. Give them structured repair envelopes instead.
voku/phpstan-agent-format adds a custom PHPStan formatter named agent that emits compact, deterministic repair envelopes for coding agents.
The default output uses TOON for token-efficient LLM repair loops.
⚡ Quick start
composer require --dev voku/phpstan-agent-format
Add the extension to your phpstan.neon:
includes: - vendor/voku/phpstan-agent-format/extension.neon parameters: level: max paths: - src
Run PHPStan with the agent formatter:
vendor/bin/phpstan analyse --error-format=agent
That is enough to get agent-ready TOON output.
🤖 Use as an agent skill
Paste this into your coding agent's system prompt or context:
You are fixing PHPStan issues in this repository. Run
vendor/bin/phpstan analyse --error-format=agent. Read the TOON envelope. Pick one cluster at a time. UserootCauseSummary,repairStrategySummary,symbolContext, snippets, andcontextTraceto understand the root cause. Make the smallest safe code change that removes the reported issue without changing unrelated behavior. Re-run PHPStan after each change and confirm the cluster disappears before moving on.
Recommended agent loop:
- Read
summary— understand total issue count and how many clusters were found. - Pick one cluster — use
kind,ruleIdentifier, andaffectedFilesto locate it. - Read the representative issue —
symbolContextand the code snippet show where to act. - Make the smallest safe change, then re-run PHPStan.
- Confirm the cluster is gone before picking the next one.
When an agent needs JSON instead of TOON (e.g. for structured tool output), set agentFormat.outputMode: json.
🚮 Why this exists
The slot machine
We take PHPStan output, dump it into CI, copy it into an AI prompt, and then act surprised when the agent starts fixing symptoms instead of the problem.
❌ bad:
vendor/bin/phpstan analyse
Then copy 500 lines of terminal output into an AI tool and ask:
please fix
Congratulations. You built a slot machine.
✅ better:
vendor/bin/phpstan analyse --error-format=agent
Then give the agent structured repair information:
Read the summary.
Pick one cluster.
Understand the root cause.
Make the smallest safe change.
Run PHPStan again.
This is not magic. This is basic tooling hygiene.
The problem with unstructured output
Static analysis output for humans is not automatically good input for agents.
Humans can read noisy CI logs and guess the hidden connection between ten errors. We can say: "Ah, this is probably one nullable value leaking through the service layer." Then we fix the source.
Agents are not there yet unless we give them better input.
Without structure, the agent sees this:
error
error
error
error
error
And then it does this:
change
change
change
change
break
That is not refactoring. That is automated panic.
What structured output looks like
❌ bad (three separate errors, same root cause, no context):
src/UserMailer.php:42 — Parameter #1 $email expects string, string|null given.
src/UserMailer.php:84 — Parameter #1 $email expects string, string|null given.
src/UserRepository.php:129 — Parameter #1 $id expects int, int|null given.
Agent reaction: Make everything nullable?
No. Bad agent. Sit.
✅ better (one cluster, two files, clear repair path):
kind: nullable-propagation
rootCauseSummary: Nullable value reaches a non-null expectation.
repairStrategySummary: Constrain nullability earlier or widen the target type if the domain allows it.
affectedFiles:
- src/UserMailer.php:42
- src/UserMailer.php:84
- src/UserRepository.php:129
Now we can work.
voku/phpstan-agent-format groups related findings, deduplicates repeated symptoms, and includes compact context traces — giving the agent something closer to a repair plan instead of a wall of terminal sadness.
And yes, this is still your job as a developer. The agent should not decide architecture. The agent should repair one cluster, with one small change, and then PHPStan should confirm that the problem disappeared.
Same rule as with legacy code: first think, then change, then check.
⚙️ Configure
includes: - vendor/voku/phpstan-agent-format/extension.neon parameters: level: max paths: - src agentFormat: outputMode: toon maxClusters: 30 maxIssuesPerCluster: 3 snippetLinesBefore: 2 snippetLinesAfter: 3 includeDocblock: false includeRelatedDefinition: true tokenBudget: 12000 redactPatterns: - '(?i)password\s*=\s*.+' - '(?i)api[_-]?key\s*=\s*.+' - '(?i)secret\s*=\s*.+'
Run:
vendor/bin/phpstan analyse --error-format=agent
Configuration options
| Option | Default | Description |
|---|---|---|
outputMode |
toon |
Serializer: toon, json, ndjson, markdown, compact |
maxClusters |
30 |
Maximum number of issue clusters in the report |
maxIssuesPerCluster |
3 |
Representative issues shown per cluster |
snippetLinesBefore |
2 |
Source context lines before the reported line |
snippetLinesAfter |
3 |
Source context lines after the reported line |
includeDocblock |
false |
Includes the nearest relevant PHPDoc block for the reported symbol when available. |
includeRelatedDefinition |
true |
Includes a compact declaration snippet for the related class, method, function, property, or constant when it can be resolved safely. |
tokenBudget |
12000 |
Cap report size; reduction is deterministic when exceeded |
redactPatterns |
[] |
Regex patterns to redact secrets from snippets |
Context-rich representative issue example
When includeDocblock and includeRelatedDefinition are enabled, representative issues can include compact symbol context without expanding entire source files:
{
"message": "Parameter #1 $email expects string, string|null given.",
"location": {
"file": "src/UserMailer.php",
"line": 42
},
"symbolContext": {
"className": "UserMailer",
"methodName": "send",
"parameterName": "email",
"expectedType": "string",
"inferredType": "string|null"
},
"docblock": "/** Sends an email to a verified recipient. */",
"relatedDefinition": {
"file": "src/UserMailer.php",
"line": 17,
"symbol": "UserMailer::send",
"kind": "method",
"snippet": [
"public function send(string $email): void"
]
}
}
If a docblock or related declaration cannot be resolved conservatively, the corresponding enabled field is emitted as null. Both fields respect redactPatterns and the token-budget reducer may remove them before shrinking root-cause and repair-strategy summaries.
📋 Output modes
| Mode | Alias | Best for |
|---|---|---|
agentToon |
toon |
Default — token-efficient agent repair loops |
agentJson |
json |
Structured tool output, JSON-consuming agents |
agentNdjson |
ndjson |
Streaming / line-delimited pipelines |
agentMarkdown |
markdown |
Chat-based flows, paste into Copilot/Claude/ChatGPT |
agentCompact |
compact |
Ultra-compact text for tight token budgets |
Switch the mode per workflow:
parameters: agentFormat: outputMode: markdown
vendor/bin/phpstan analyse --error-format=agent > phpstan-agent.md
Then hand the markdown report to your agent:
Read the phpstan-agent-format report.
Treat each cluster as one underlying problem.
Fix root causes instead of patching duplicates.
Keep changes minimal and preserve behavior.
Rerun PHPStan after each fix.
📐 Envelope shape (TOON)
tool: phpstan-agent-format
phpstanVersion: 2.1.50
summary:
totalIssues: 3
clusters: 1
suppressedDuplicates: 2
tokenStats:
estimatedTokens: 420
tokenBudget: 12000
wasReduced: false
clusters[1]{clusterId,kind,ruleIdentifier,rootCauseSummary,repairStrategySummary,confidence,affectedFiles,representativeIssues,suppressedDuplicateCount}:
6fdafecf6214,nullable-propagation,argument.type,Nullable value reaches a non-null expectation.,Constrain nullability earlier or widen the target type to accept null.,0.7,[3]: src/UserMailer.php:42 src/UserMailer.php:84 src/UserRepository.php:129,[0]:,2
📐 Envelope shape (JSON)
Representative issues include structured repair hints inside symbolContext, including the targeted parameter/property plus expected and inferred types when PHPStan exposes them.
{
"tool": "phpstan-agent-format",
"phpstanVersion": "2.1.50",
"summary": {
"totalIssues": 3,
"clusters": 1,
"suppressedDuplicates": 2,
"tokenStats": {
"estimatedTokens": 420,
"tokenBudget": 12000,
"wasReduced": false
}
},
"clusters": [
{
"clusterId": "6fdafecf6214",
"kind": "nullable-propagation",
"ruleIdentifier": "argument.type",
"rootCauseSummary": "Nullable value reaches a non-null expectation.",
"repairStrategySummary": "Constrain nullability earlier or widen the target type to accept null.",
"confidence": 0.7,
"affectedFiles": ["src/UserMailer.php:42", "src/UserMailer.php:84", "src/UserRepository.php:129"],
"representativeIssues": [
{
"id": "a1b2c3d4",
"message": "Parameter #1 $email expects string, string|null given.",
"ruleIdentifier": "argument.type",
"location": { "file": "src/UserMailer.php", "line": 42 },
"symbolContext": {
"className": "UserMailer",
"methodName": "send",
"parameterName": "$email",
"expectedType": "string",
"inferredType": "string|null"
}
}
],
"suppressedDuplicateCount": 2
}
]
}
🧩 Clustering strategy
First-pass clustering groups by:
- same PHPStan error-identifier family when available, otherwise same rule identifier
- same symbol context when detected
- same file + nearby line bucket
- same type-origin hint
Cluster kinds include:
- 🔴 nullable propagation
- 🟡 missing type declaration
- 🟠 generic/template drift
- 🟣 array shape drift
- 🔵 undefined member from inferred type
- ⚪ invalid offset access
- 🟤 stale ignore/baseline noise
- ⚫ fallback: repeated same-rule same-symbol
💰 Token budget reduction strategy
When estimated tokens exceed tokenBudget, degradation is deterministic and ordered:
- Remove verbose/secondary details (secondary locations)
- Shrink snippets to focused lines
- Reduce representative issues per cluster
- Keep root cause + repair strategy summaries last
🔒 Security and privacy
Snippets are redacted using configurable regex patterns before serialization.
parameters: agentFormat: redactPatterns: - '(?i)password\s*=\s*.+' - '(?i)api[_-]?key\s*=\s*.+'
📁 Example outputs
See /examples/:
agent-toon-example.toonagent-json-example.jsonagent-ndjson-example.ndjsonagent-markdown-example.mdagent-compact-example.txt
🔄 Reformat existing JSON exports
If you already produce phpstan --error-format=json, reformat that payload without rebuilding your pipeline:
$result = AgentErrorFormatter::formatPhpstanJsonExport($jsonString, $config);
The repository CI dogfoods both modes: PHPStan runs once with the default formatter, once with --error-format=agent, and again against committed fixture configs that exercise the agent envelope on real output.
voku/phpstan-agent-format 适用场景与选型建议
voku/phpstan-agent-format 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 20 次下载、GitHub Stars 达 2, 最近一次更新时间为 2026 年 04 月 28 日, 在 PHP 生态内属于活跃度较高的组件。
我们在过去多个企业项目中使用过 voku/phpstan-agent-format 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 voku/phpstan-agent-format 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
统计信息
- 总下载量: 20
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 2
- 点击次数: 32
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-04-28