First-class callables with parameter conditions#418
Conversation
7de490c to
1509fe4
Compare
There was a problem hiding this comment.
Pull request overview
Fixes how first-class callable syntax (func(...)) interacts with parameter-based allow/disallow conditions by treating “no args at call site” as an unsatisfiable param allow condition and an untriggerable param disallow condition. This aligns the rule behavior with PHPStan’s detection point for first-class callables and closes #415.
Changes:
- Adjusted
Allowedparam-check logic soargs === nullno longer incorrectly satisfies param conditions. - Added targeted first-class-callable fixtures + rule tests for
allowParamsAnywhere,allowParamsInAllowed, andallowExceptParamsInAllowedscenarios. - Documented first-class callable behavior in
docs/allow-with-parameters.mdand updated lint exclusions for PHP < 8.1.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/Allowed/Allowed.php | Fix null-args handling for param conditions and make allowed/disallowed zone behavior explicit via allowedByDefault. |
| docs/allow-with-parameters.md | Adds documentation section describing first-class callable behavior with param directives. |
| composer.json | Excludes new PHP 8.1-only fixtures from lint runs on PHP 7.4/8.0. |
| tests/src/RoyaleExceptFirstClassCallable.php | Fixture covering allowExceptInMethods + param directives with first-class callables. |
| tests/src/RoyaleAllowInFirstClassCallable.php | Fixture covering allowInMethods + param directives with first-class callables. |
| tests/src/FirstClassCallableParamsAnywhere.php | Fixture covering allowParamsAnywhere/allowExceptParamsAnywhere with first-class callables. |
| tests/Calls/FunctionFirstClassCallablesParamsAnywhereTest.php | New test asserting first-class callable behavior under allowParamsAnywhere/allowExceptParamsAnywhere. |
| tests/Calls/FunctionFirstClassCallablesAllowInMethodsWithParamsTest.php | New test asserting allowInMethods + allowParamsInAllowed / allowExceptParamsInAllowed. |
| tests/Calls/FunctionFirstClassCallablesAllowExceptInMethodsWithParamsTest.php | New test asserting allowExceptInMethods + allowParamsInAllowed / allowExceptParamsInAllowed. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
When a function or method is used as a first-class callable, no arguments are present at the detection point - they are supplied only when the callable is eventually invoked, so parameter conditions cannot be evaluated at the call site. Conditions that restrict which calls are allowed (`allowParamsAnywhere`, `allowParamsInAllowed`, and their `AnyValue` and `Flags` variants) require a matching param value. Because no args are present the condition can never be satisfied, so first-class callables are always reported when these directives are configured. Conditions that restrict which calls are disallowed (`allowExceptParamsAnywhere`, `allowExceptParamsInAllowed`, and their variants and aliases) require a matching param value to trigger the disallow. Because no args are present the forbidden condition can never be triggered, so first-class callables are never reported when these directives are configured.
1509fe4 to
aecdc11
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
PHPStan detects first-class callable syntax at the point of
func(...), where no arguments are present - they are supplied only when the callable is later invoked, which means parameter conditions configured on the disallowed call cannot be evaluated at the detection point.This PR corrects the behavior across all param condition directives. Conditions that require a matching param value to allow a call -
allowParamsAnywhere,allowParamsInAllowed, and theirAnyValueandFlagsvariants - can never be satisfied without arguments, so first-class callables are always reported when any of these is configured, regardless of zone. Conditions that require a matching param value to disallow a call -allowExceptParamsAnywhere,allowExceptParamsInAllowed, and their variants and aliases - can never be triggered without arguments, so first-class callables are never reported in eitherallowIn*orallowExceptIn*zones when these are configured.Previously,
allowParamsAnywhereincorrectly allowed first-class callables because the null-args path inhasAllowedParamsunconditionally returnedtrue.allowExceptParamsInAllowedincorrectly disallowed first-class callables in allowed zones because the null-args path inhasAllowedParamsInAllowedtreated it identically toallowParamsInAllowed. Both are fixed. A new section indocs/allow-with-parameters.mddocuments the complete behavior for all param condition directives.Closes #415