feat(php): consolidated PHP tooling (php, artisan, phpunit, phpstan, pest, paratest, ecs, pint)#1649
feat(php): consolidated PHP tooling (php, artisan, phpunit, phpstan, pest, paratest, ecs, pint)#1649iliaal wants to merge 7 commits into
Conversation
📊 Automated PR Analysis
SummaryAdds eight new rtk subcommands for PHP tooling (php, artisan, phpunit, phpstan, pest, paratest, ecs, pint) with structured output parsers and compression. Consolidates work from three prior PRs by different authors into a single module, including shared test output filtering, Composer custom-bin-dir support, and registry normalization. Review Checklist
Linked issues: #1503, #1246, #874, #1110 Analyzed automatically by wshm · This is an automated analysis, not a human review. |
|
Hi, |
Can be added maybe as a follow-up, PR is already quite big, but 100% a good idea. |
|
Added f9e139a on top of this branch: swaps the Companion fix at the recovery layer: #1696 — same head-bias bug in |
|
Tested this branch on a production Symfony monorepo (27 packages, PHP 8.4/8.5, Pest 4.6.3 + PHPStan level 9), built from source and wired through the Claude Code Works well across the toolchain:
Coverage matches our PHP stack exactly and the filtered output stays faithful to each tool's native format. Would love to see this land — happy to help test further. Thanks for the thorough work here. |
|
Really looking forward to this landing as part of rtk. It's needed to get real benefits from rtk for those of us on PHP monoliths at the moment :) Pretty please? |
…pest, paratest, ecs, pint) Consolidates the PHP-tooling work from three upstream PRs plus a new Pint module, leaving phpt to its own PR (rtk-ai#1503). - rtk php / rtk artisan: syntax check (-l) and Laravel artisan wrapper. - rtk phpunit: structured-state parser, aggregate counts, bounded failure list. Uses runner::run_filtered. - rtk phpstan: typed serde::Deserialize parser for --error-format=json, groups errors by file, sorts by count desc. Utility commands (--version, list, clear-result-cache) pass through unchanged. - rtk pest / rtk paratest: shared test_output helper. - rtk ecs / rtk pint: code-style fixers; pint uses --format=json for structured per-file rule counts. Composer custom-bin-dir detection: composer_bin_dirs() reads COMPOSER_BIN_DIR and composer.json config.bin-dir, so tools/bin/phpunit classifies identically to vendor/bin/phpunit. registry.rs normalizes tool paths before matching. Sources: - rtk-ai#1246 (aaronflorey, self-closed): php, artisan, ecs, pest, paratest, test_output, utils, composer_bin_dirs, registry normalization. - rtk-ai#874 (Beninho, open): phpunit state-machine parser. - rtk-ai#1110 (LucianoVandi, open): phpstan typed parser. - New: pint_cmd.rs. Tests: discover::registry 253 pass; cmds::php 36 pass; cargo build --release 0 errors; cargo fmt --check clean.
The make filter used max_lines=50, implemented as head-only truncation in toml_filter. For commands like 'make test' running PHPT (~17000 progress lines followed by a pass/fail summary), the cap discarded exactly the useful part — the tail. Switch to head_lines=10 + tail_lines=40 so both the build prologue and the final summary survive, with a '... (N lines omitted)' marker in the middle. Token savings on long outputs remain >60%; existing inline tests still pass and a new tail-preservation test guards the behaviour.
Upstream develop changed `rewrite_command` to take 3 args (cmd, excluded, transparent_prefixes). The PHP tooling tests that don't care about transparent prefixes now use the existing `rewrite_command_no_prefixes` helper instead.
|
Hi @iliaal, Since you're aggregating contributions from others in this PR, please ensure you include the "Co-authored-by:" trailer in the commit message to properly credit them. Example: This helps maintain proper attribution for all contributors. Thanks! |
The PHP runners (phpunit, pest/paratest, ecs, phpstan, pint) only
applied their compact filters when rtk itself launched the process.
Output produced elsewhere — most commonly a tool run inside a Docker
container and piped back to the host — bypassed them entirely, since
the visible command is `docker ...`, not the PHP tool.
Wire the existing filter functions into `rtk pipe`:
- resolve_filter: phpunit, pest|paratest|php-test, ecs, phpstan, pint
- phpstan/pint pipe wrappers sniff JSON-vs-text by content, since the
runners force --format=json but piped output may be either
- auto_detect_filter: route the "by Sebastian Bergmann" banner to the
phpunit filter (no -f needed)
- bump the four backing fns to pub(crate)
Also fix filter_phpstan_text matching the summary line case-sensitively
("found"), which missed phpstan's actual "[ERROR] Found N errors".
Tests: phpunit banner auto-detect, phpstan case-insensitive summary.
feat(pipe): expose PHP tool filters as stdin pipe filters
|
Tried this branch locally against current PHP tool versions (Pint 1.27.1, PHPStan 2.1.40, ECS 13.0.4) — great work, the savings are substantial. I hit two parser bugs where the expected schema no longer matches what current tooling emits. Both stem from test fixtures using an older schema, so the suite stays green. Details + fixes below. 1.
{"result":"fail","files":[{"path":"app/Foo.php","fixers":["concat_space"]}]}The fields are required with no aliases, so serde rejects it and #[serde(alias = "path")]
name: String,
#[serde(rename = "appliedFixers", alias = "fixers")]
applied_fixers: Vec<String>,2.
if phpstan.totals.file_errors == 0 && phpstan.totals.errors == 0 { return "phpstan: ok".to_string(); }
// and report phpstan.totals.file_errors in the summary lineBoth fixtures set |
pint: Pint >=1.14 renamed JSON keys name->path and appliedFixers->fixers. The struct fields were required with no aliases, so serde rejected current output and the filter fell back to raw (no compression). Add backward- compatible aliases so both schemas parse. phpstan: the "ok" gate and summary line read totals.errors, which counts only non-file-specific (global) errors. A normal failing run reports errors=0 with the count in file_errors, so runs with real errors were reported as "phpstan: ok", silently hiding failures. Gate on both counts and report file_errors in the summary. Both regressions slipped past the suite because the fixtures set errors == file_errors (phpstan) and used the old key names (pint). Added regression tests using the current-version schemas. Reported by @evaldnet (verified against Pint 1.27.1, PHPStan 2.1.40). Co-authored-by: Aaron Florey <azza@jcaks.net> Co-authored-by: Eli White <1153183+EliW@users.noreply.github.com> Co-authored-by: Benjamin LETELLIER <bletellier@audencia.com> Co-authored-by: Luciano <vandi.luciano@gmail.com>
|
Thanks for the precise report, @evaldnet — both confirmed and fixed on this branch (184ced3).
Both ship with regression tests on the current-version schemas — the old fixtures set |
|
@EliW No. Both old and current tool output parse; I checked each schema against the new code. pint: the fix adds serde aliases, not a rename. The struct accepts both the old keys ( phpstan: no new version dependency. Safety net under both: if the JSON ever fails to parse, rtk falls back to raw tool output. You lose the compression for that run, not the result. So anyone stuck on an older phpstan or pint won't get broken output; worst case is unfiltered passthrough. |
|
Rebuilt on 184ced3 and re-ran against current versions. Both fixes hold:
One separate thing I noticed while testing: the leading
Since |
…ecs/pint `./vendor/bin/<tool>` is the common Laravel invocation form. classify_command normalizes the leading `./`, so these classify as supported, but the rewrite strips literal `rewrite_prefixes` from the raw command and the five rules only carried `vendor/bin/<tool>` and bare `<tool>`. So `./vendor/bin/pint` ran raw with no compression while `vendor/bin/pint` rewrote to `rtk pint`. phpstan already carried `./vendor/bin/phpstan`. Add the `./vendor/bin/` prefix to the other five, with a regression test. Reported by @evaldnet (verified against pint 1.29.1, phpstan 2.1.40). Co-authored-by: Aaron Florey <azza@jcaks.net> Co-authored-by: Eli White <1153183+EliW@users.noreply.github.com> Co-authored-by: Benjamin LETELLIER <bletellier@audencia.com> Co-authored-by: Luciano <vandi.luciano@gmail.com>
|
Fixed on the branch (bd91048). One correction on the mechanism: the regex isn't the blocker. |
Summary
Consolidates the open and closed PHP-tooling work into a single PR against
develop, omitting.phpt(which has its own PR #1503).This PR adds eight
rtksubcommands for PHP test runners and code tooling:php,artisan,phpunit,phpstan,pest,paratest,ecs,pint. Output compression in the 60% to 95% range per tool, structured parsers where possible (PHPUnit state machine, PHPStan typed JSON, Pint per-file rule counts).Credits to original authors
php,artisan,ecs,pest,paratest,test_output,utils,composer_bin_dirs, registry normalization for Composer custom-bin-dir layouts.phpunitstate-machine parser. I ported it into the consolidated module, switched it torunner::run_filtered, and stripped emoji from output.phpstantyped serde::Deserialize parser for--error-format=json. Groups errors by file, sorts by count descending; passes utility commands (--version,list,clear-result-cache) through unchanged. I stripped emoji from the success output.pint(Laravel Pint code-style fixer) using--format=jsonfor structured per-file rule counts.If the maintainers prefer this consolidation, close the three separate PRs in favor of this one. If they prefer the originals, close this PR and let the per-tool PRs continue independently. Either is fine with me.
Composer custom-bin-dir support
composer_bin_dirs()readsCOMPOSER_BIN_DIRandcomposer.json'sconfig.bin-dir, sotools/bin/phpunitclassifies identically tovendor/bin/phpunit.registry.rsnormalizes tool paths before matching, so a single rule covers the standard Composer layouts.