Repository navigation
Fix the search filter example, which is a PHP fatal error - #603
Merged
Merged
Conversation
The CountryFilter sample on this page, the first thing an extension author copies to add a filter, does not load. It declares:
public function filter(SearchState $state, string $filterValue, bool $negate)
while FilterInterface declares `string|array $value` and a `void` return. PHP does not permit an implementation to narrow a parameter type, so this is a fatal error, not a style difference: "Declaration of CountryFilter::filter(...) must be compatible with FilterInterface::filter(SearchState $state, array|string $value, bool $negate): void". The signature now matches, the body normalises the array form (which is what arrives when a filter key is repeated), and a note explains why the signature cannot be trimmed.
The mutator registration referred to OnlySameCountryFilterMutator while the class above it is OnlySameCountrySearchMutator, so following the example produced a class-not-found error. Registration now uses the name that was defined.
Two classes presented as complete also lacked imports for types in their own signatures. AcmeSearcher used User and Builder with neither imported; AbstractAcmeSearcher used FilterManager, User, SearchCriteria and SearchResults with none of the four imported. Both now import what they reference, matching core's own UserSearcher.
AcmeSearcher additionally scopes its base query with whereVisibleTo, as every core searcher does, since a searcher that does not scope returns rows the actor may not see. A line notes that this comes from ScopeVisibilityTrait and links Model Visibility, so it is not mistaken for something every model has.
The gambits half of the page was checked and needed nothing: BooleanGambit and KeyValueGambit are exported where documented, the UnreadGambit sample matches core's real one, and the eight IGambit members listed under advanced gambits match the interface exactly.
imorland
approved these changes
Oct 3, 2026
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.
The first worked example on this page does not load. Verified by running the exact declaration against the real interface.
CountryFilteris a PHP fatal errorThe page declares:
FilterInterfacedeclaresstring|array $valueand avoidreturn. PHP does not permit an implementation to narrow a parameter type, so this is a fatal error rather than a stylistic difference:Anyone following the page to add a filter, which is what this section is for, gets that and no filter.
The signature now matches the interface, and the body normalises the array form, which is what arrives when the same filter key is supplied more than once. A short caution explains why the signature cannot be trimmed, since
stringlooks like the obvious type to write and the reason it is not is non-obvious.The mutator registration names a class that does not exist
The example defines
OnlySameCountrySearchMutator, then registersOnlySameCountryFilterMutator. Following the page produces a class-not-found error. Registration now uses the name that was defined.Two "complete" classes were missing imports
AcmeSearcherusesUserandBuilderin its signature with neither imported, so both resolve into the example's own namespace.AbstractAcmeSearcherusesFilterManager,User,SearchCriteriaandSearchResultswith none of the four imported. Both now import what they reference, matching core's ownUserSearcher.While there:
AcmeSearchernow scopes its base query withwhereVisibleTo, as every core searcher does. A searcher that does not scope its query returns rows the actor is not permitted to see, which is a worse failure than a missing feature. I added a line noting thatwhereVisibleTocomes fromScopeVisibilityTraitand linking Model Visibility, so it is not mistaken for something every model has.The gambits half needed nothing
Worth recording, since 2.0 moved gambits to the frontend and that is exactly where stale samples tend to survive:
BooleanGambitandKeyValueGambitare both exported fromflarum/common/query/IGambitas documented, withIGambitas the default export.UnreadGambitsample matches core's real one (core also definesenabled(), which is optional).type,pattern,toFilter,filterKey,fromFilter,suggestion,predicates,enabled) match theIGambitinterface exactly, in order.Verification
The corrected filter was run against a stand-in of the real interface and loads and executes for both the string and array forms.
npx docusaurus build --locale encompletes with no errors and no anchors broken on this page. This branch touches onlysearch.md, which no other open PR of mine edits.