[PHP 8.6] Partial Function Application (PFA) - #229
lisachenko wants to merge 5 commits into
Conversation
PHP 8.6 introduces Partial Function Application (PFA): `foo(1, ?)` turns a call into a Closure. nikic/php-parser 5.8.0 (latest, 2026-06-04) has no grammar for the `?` argument placeholder, so such sources cannot be analyzed at all. Pin down the interim contract of issue #224: - `tests/Stub/FileWithPartialFunctionApplication86.php` - PFA placeholders in function, method and closure bodies. Valid PHP 8.6, invalid PHP 8.5, therefore never included and deliberately kept out of `AbstractTestCase::getFilesToAnalyze()`, mirroring how `FileWithFunctionsFcc.php` is handled. - `tests/Stub/FileWithFccInBodies.php` - the PFA-adjacent syntax that is already parseable: `foo(...)` inside function-like bodies. - `tests/Php86PartialFunctionApplicationTest.php` - asserts that PFA sources surface a catchable `PhpParser\Error` (the engine does not wrap parser errors) instead of a truncated AST, that a failed parse does not poison the engine cache, that first-class callables in bodies still reflect cleanly with their `VariadicPlaceholder` node intact, and that `NodeExpressionResolver` degrades into `ReflectionException` for placeholder arguments and unhandled node types. Refs #224 Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
|
@claude can you have a look here for feature request to nikic/php-parser or create it there? |
|
Checked upstream — a feature request already exists and is actively moving, so I did not create a duplicate:
So the follow-up here is exactly what this PR's body anticipates: once #1159 ships in a PHP-Parser release, bump the Worth subscribing to #1159 for release timing — I could not watch it from this session (repository outside my access scope). Generated by Claude Code |
|
Upstream update: nikic/PHP-Parser#1159 (partial function application support, No tagged release contains it yet — latest is still 5.8.0. My recurring check keeps watching; as soon as a release ships PFA parsing, I'll do the full follow-up on this branch (constraint bump, flip the parse-error assertions to positive reflection tests, Generated by Claude Code |
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
…r ^5.9 nikic/php-parser 5.9.0 parses the PFA `?` placeholder into ArgPlaceholder nodes, so PFA-containing sources now reflect cleanly on every supported host runtime: - bump the nikic/php-parser constraint to ^5.9 - flip the interim parse-error tests to positive reflection assertions: engine and ReflectionFile reflect the PFA stub, every placeholder position (incl. named placeholders) parses, method bodies keep their ArgPlaceholder nodes and isPartialFunctionApplication() flag - keep the resolver contract: a placeholder argument in a constant-expression position degrades into ReflectionException (message generalized to cover ArgPlaceholder and VariadicPlaceholder alike) - pin cache hygiene with a genuinely broken source now that PFA parses - guarded PHP 8.6 test executes the stub and checks native behavior Note: a first-class callable reports isPartialFunctionApplication() true in php-parser 5.9 by design; the FCC regression test documents that. Refs #224 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
… 5.9 Rector's bootstrap preloads its own unprefixed php-parser copy whenever PHPUnit >= 12 is running, shadowing the project's php-parser. Rector 2.6.7 is the first release bundling php-parser ^5.9, so older versions would break the PFA tests in the lowest-dependencies CI job. Refs #224 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn
|
php-parser 5.9.0 shipped PFA support on 2026-09-13, so this PR is now the full implementation — the "On Hold" reason is resolved. Pushed in two commits (
Local gate green: 13,810 tests on 8.5 / 15,041 on 8.6 (PFA class 20/20 on 8.6, partial application evaluates to Generated by Claude Code |
Refs #224 (part of epic #219)
Full PHP 8.6 Partial Function Application support
PFA lets any call use a
?placeholder for a single open argument, evaluating to aClosure:This PR started as interim behavior-documenting coverage while parsing was blocked upstream. nikic/PHP-Parser#1159 merged on 2026-09-05 and shipped in php-parser 5.9.0 on 2026-09-13 (
ArgPlaceholdernode), so the follow-up planned in this PR's history is now implemented here:composer.json:nikic/php-parserbumped^5.4→^5.9;rector/rectorbumped^2.0→^2.6.7(see below).ReflectionFileentry point reflect the PFA stub (functions, classes, signatures) on every host runtime — confirmingParserFactory::createForNewestSupportedVersion()picks the 5.9 grammar up on a PHP 8.5 host with no engine change; every placeholder position parses (trailing, leading, multiple, namedf(name: ?), method/static/newcontexts) with the expectedArgPlaceholdercount; method bodies keep theirArgPlaceholdernodes withisPartialFunctionApplication()true.ReflectionException— the same contract user-defined first-class callables already have. The message is generalized ("Cannot statically resolve a placeholder argument in a function/constructor call") since the existinginstanceof Argguard coversArgPlaceholderandVariadicPlaceholderalike; dedicated tests pin all three shapes.Closurereturned,('a b') → 'a-b').isPartialFunctionApplication() === trueforfoo(...)), which the test documents.AbstractTestCase::getFilesToAnalyze()— it now parses everywhere but is still a compile error when included on PHP 8.5.Why the rector floor bump is needed
Rector's package
bootstrap.phppreloads its bundled, unprefixed php-parser copy whenever PHPUnit ≥ 12 is running — which the PHPUnit 13 bump (#236) activated, silently shadowing the project's own php-parser in every phpunit run. Rector 2.6.7 (2026-09-13) is the first release bundling php-parser^5.9; with an older rector, the lowest-dependencies CI job would run the suite against a shadowed pre-PFA parser and fail. Worth keeping in mind generally: under PHPUnit ≥ 12, the test suite exercises rector's bundled php-parser build, not the lockednikic/php-parserpackage.Local validation
vendor/bin/phpunit(PHP 8.5.9, php-parser 5.9.0, PHPUnit 13.3.4)php8.6 vendor/bin/phpunit(PHP 8.6.0beta1)vendor/bin/phpstan analyse src --no-progress(level 10)[OK] No errors🤖 Generated with Claude Code
https://claude.ai/code/session_01Sy8BcM8ivUEpu8uVm7wADn