Repository navigation
Conversation
zonuexe
force-pushed
the
array-all-type-specifying
branch
from
August 9, 2026 08:35
bbb1116 to
ad88d61
Compare
zonuexe
marked this pull request as draft
September 14, 2026 05:43
zonuexe
force-pushed
the
array-all-type-specifying
branch
from
September 14, 2026 05:48
ad88d61 to
f4f7fb4
Compare
zonuexe
marked this pull request as ready for review
September 14, 2026 06:27
Collaborator
|
This pull request has been marked as ready for review. |
zonuexe
marked this pull request as draft
September 27, 2026 00:18
zonuexe
force-pushed
the
array-all-type-specifying
branch
from
September 27, 2026 00:22
f4f7fb4 to
566c432
Compare
zonuexe
marked this pull request as ready for review
September 27, 2026 00:23
Collaborator
|
This pull request has been marked as ready for review. |
zonuexe
marked this pull request as draft
October 5, 2026 12:09
zonuexe
force-pushed
the
array-all-type-specifying
branch
2 times, most recently
from
October 11, 2026 07:26
d0f9867 to
dd417b0
Compare
Supports arrow functions and single-return closures, e.g. array_all($arr, fn ($v, $k) => is_int($v) && is_string($k)). Partially resolves phpstan/phpstan#12939 Original work from phpstan#5134, squashed and rebased onto 2.2.x.
Rewrites the previous commit's extension on top of the callback handling
already used by array_filter(): element value/key types are assigned onto
a scope, which is then filtered by the predicate. Negated predicates,
bare truthiness and @phpstan-assert-if-true now narrow uniformly, and
first-class callables, constant-string callables and constant array
shapes are supported.
Callback normalization and per-element narrowing move into shared
services so ArrayFilterFunctionReturnTypeHelper and the new extensions
cannot drift apart. By-ref and variadic callback parameters no longer
narrow, since the callback may rewrite the elements.
Fixes unsound narrowing to never: an empty array makes array_all() true,
so a rejected element narrows the array to array{} and lets the scope
intersection decide whether that is impossible.
array_all() === false and array_any() === true imply a non-empty array,
which holds even when the callback cannot be analysed. array_any() is
otherwise the dual of array_all(), narrowing elements on its falsey side.
Co-authored-by: uekann <kantaueno0808@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The simple-downgrade tooling used by the 7.4/8.0/8.1 lint CI jobs cannot transform `match`, and it was the only real match expression left in src/.
Add array_all()/array_any() `=== false` test cases whose else branch is the truthy result of the call. This reaches the falsey-but-not-false type-specifier context that a plain `if (array_all(...))` never hits, so the `$context->false()` discriminator is now covered by mutation testing. Also make the array_all() impossible-check reproducer use a literal-false predicate: `is_string($key)` on a list key is inferred as `false` on current PHP but as `bool` on the downgraded 7.4/8.0 builds, which made the reported closure type - and thus the expected error message - unstable across the CI matrix. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The nsrt files carried `// lint >= 8.4`, so on PHP 8.3 the whole test was
skipped. The 8.3 mutation testing job therefore had no test left to kill
the mutants in the new extensions ("0 mutants were killed by Test
Framework") and failed the 100% MSI requirement.
Declare the functions for older runtimes behind a function_exists() guard,
the same way array-find.php does, and drop the lint line.
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
The fixtures use first-class callable syntax, so they need a `// lint >= 8.1` comment to be skipped on PHP 7.4/8.0. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
zonuexe
force-pushed
the
array-all-type-specifying
branch
from
November 2, 2026 15:50
5144f39 to
a633423
Compare
…arrowing The narrowing array_any() === true (non-empty array) and array_all() === true (per-element narrowing) is a consequence of the result, not proof of it. When the array type was already non-empty, or the predicate did not change the element types, the narrowed type equalled the argument type and the impossible-check rules reported "will always evaluate to true". Leave the call's result undecided via the specified types' root expression, except when the narrowed type cannot coexist with the argument's type: that call is still reported as always false. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds type narrowing for
array_all()/array_any()on top of the callback-handling machinery already used byarray_filter().array_all($arr, $cb) === truenarrows element value/key types by the predicate being truthy (an empty array still satisfies it);=== falseimplies the array is non-empty.array_any()is the dual:=== trueimplies non-empty,=== falsenarrows elements by the predicate being falsey.Callback normalization (arrow functions, single-return closures, first-class callables, constant-string callables) and per-element scope narrowing are extracted into shared services also used by
ArrayFilterFunctionReturnTypeHelper, so negated predicates, bare truthiness and custom@phpstan-assert-if-truepredicates all narrow uniformly, and constant array shapes are narrowed per offset.This also fixes an unsound narrowing to
never: an empty array still makesarray_all()true, so a rejected element narrows the array toarray{}rather thannever, letting the scope's own type intersection decide whether that is actually impossible for a known non-empty input.By-ref and variadic callback parameters no longer narrow, since the callback may rewrite the elements before the predicate is evaluated.
Partially resolves phpstan/phpstan#12939.
The first commit is a straight rebase/squash of the original work by @uekann in #5134, kept separate so the second commit's diff against it is easy to review. The second commit is the rewrite described above.