SKILL.md
PHP Structured Edit
Use php-ast-edit for PHP writes while this skill is active. The tool parses snippets, changes the AST, and prints the result. After rejection, correct the cause; do not fall back to text mutation.
First use
Resolve the executable once: repository bin/php-ast-edit, project vendor/bin/php-ast-edit, installed command, or this skill's scripts/php-ast-edit wrapper. Use help if needed; install missing engine dependencies before retrying.
Auto mode uses format-preserving printing unless applicable configuration enables canonical printing. Explicit printer choices override auto mode. Normalization is optional; doctor diagnoses canonical setup. Read the formatting reference when configuring formatting.
Workflow
- Find the relevant code with normal search or an LSP.
- Prefer named targets:
{"select":"method:Checkout::submit"}. Other selectors:
class:, interface:, trait:, enum:, function:, property:Foo::$items, const:Foo::LIMIT. Ambiguous names are refused. Use inspect for unnamed targets; retain its ref and sha256.
- Batch related edits across files in one
apply. Includesha256when relying on a
read snapshot. After STALE_SOURCE, reread and reassess; never drop the guard. Use "report":"compact" to receive each verification result once.
- Supply compact valid snippets. Import or qualify external types in namespaced PHP,
e.g. \\DateTimeImmutable in JSON. The printer handles indentation.
- Review returned
effects,diff, allwarnings, andvalidation.parsedand legacy
valid mean parser success. Follow checkIds to top-level verify in compact reports. Failed checks need repair; skipped or unrun checks remain outstanding where required.
- A passed configured command satisfies that same check on unchanged inputs. Repeat it
after relevant changes, or run additional checks required by the task. Use supplied exact-byte evidence for its stated scope instead of rereading solely to reconfirm it. Neither passing tests nor byte preservation proves reference completeness. Report changed symbols and checks actually run; distinguish declarations from call sites. An intended edit leaves a Git diff. Avoid repository-wide format for a local edit.
Minimal transaction:
{"report":"compact","files":[{"path":"src/Registry.php","edits":[{"target":{"select":"class:Registry"},"operation":"add_member","php":"public function register(string $name): void {}"}]}]}
php-ast-edit apply --input edits.json
"report":"full" (the default) retains per-file verification. Report mode changes presentation only; checksPassed: null means no checks ran.
Choose the narrow operation
- Empty list:
insertintowithpropertyandposition; class members:addmember. - Replace any node:
replacenode; change a slot:replacechild. - New file:
mode: create, full PHP including<?php, defaultexpectAbsentguard.
Delete: mode: delete with the snapshot hash.
- Local rename:
rename_variableon the enclosing function-like scope withfrom/to.
Binding collisions are rejected; dynamic variables remain limited.
- Method rename:
rename_methodwithto; assess unresolved receivers and inheritance
limits. Cross-file resolution requires project-aware tools. Use set_name for an intentionally declaration-only change.
- SQL/HTML/JSON inside a PHP literal:
setstringon itsScalarStringnode.
Use contexts --operation <name> before guessing unfamiliar arguments. Parser and host lint passes do not prove application behavior.
References, when needed
- [Operations](references/operations.md): full schema, selectors, guards,
parseAs, reports. - [Formatting](references/formatting-contract.md): preserving layout, optional normalization.
- [Enforcement](references/enforcement.md): optional hook and its limits.