Skip to content

Add generic type signatures for array_first, array_last, array_any and array_all#11883

Open
delolmo wants to merge 3 commits into
vimeo:6.xfrom
delolmo:generic-array-first-last-any-all
Open

Add generic type signatures for array_first, array_last, array_any and array_all#11883
delolmo wants to merge 3 commits into
vimeo:6.xfrom
delolmo:generic-array-first-last-any-all

Conversation

@delolmo

@delolmo delolmo commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

PHP 8.4's array_any/array_all and PHP 8.5's array_first/array_last are currently only described by the autogenerated CallMap, which gives them non-generic signatures:

  • array_first(array): mixed and array_last(array): mixed — the element type is lost, callers get mixed.
  • array_any(array, callable): bool and array_all(array, callable): bool — the callback parameters are untyped callable, so the value/key types are not checked inside the predicate.

This adds templated stubs to stubs/CoreGenericFunctions.phpstub, directly mirroring the existing array_key_first/array_key_last entries that sit a few lines above:

  • array_first/array_last return value-of<TArray>, with the same emptiness-aware conditional as array_key_first (null for a statically-empty array, the value type for a non-empty-array, otherwise value-of<TArray>|null).
  • array_any/array_all keep their bool return but gain a typed callable(TValue, TKey): bool predicate so the callback body is checked against the array's value and key types. They are marked @psalm-pure: unlike usort/uasort (which mutate their by-reference array), these functions do not mutate, so the call is pure when given a pure callback and Psalm propagates the callback's purity. This also matches Psalm's own source, which calls array_any from mutation-free contexts (e.g. Type\Atomic).

Tests

Adds cases to tests/ArrayFunctionCallTest.php covering the general, non-empty-array and empty-array branches for array_first/array_last, plus typed-callback bool results for array_any/array_all.

Note

array_first/array_last/array_any/array_all already exist in the version CallMaps (CallMap_84/CallMap_85); placing the generic signatures in CoreGenericFunctions.phpstub matches where array_key_first/array_key_last already live so the improved types apply regardless of the PHP version Psalm itself runs on. Happy to move them behind version gating if you'd prefer.

@delolmo delolmo marked this pull request as ready for review June 18, 2026 10:12
Comment thread stubs/CoreGenericFunctions.phpstub
@delolmo

delolmo commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

Heads up for reviewers: the failing ci/circleci: test-with-real-projects check is not related to this change. It fails at the Analyse PHPUnit step on a pre-existing baseline drift (psalm dev reporting ImpureFunctionCall/ImpureMethodCall on get_loaded_extensions(), Duration::fromSecondsAndNanoseconds, TestMethod::hasDataFromDataProvider, etc., plus unused-baseline entries) — none of which touch array_first/array_last/array_any/array_all. The same step is currently red on unrelated PRs (e.g. #11879, #11878). All substantive checks here — the GitHub Actions build (Psalm self-analysis), the full unit-test matrix including PHP 8.5, code style and backward-compatibility — pass.

@delolmo delolmo marked this pull request as draft June 18, 2026 10:26
@delolmo delolmo marked this pull request as ready for review June 18, 2026 10:26

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 375524f. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant