You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First-class callables with parameter conditions (#418)
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 their _`AnyValue`_ and _`Flags`_ variants - 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 either `allowIn*` or `allowExceptIn*` zones when these are configured.
Previously, `allowParamsAnywhere` incorrectly allowed first-class callables because the null-args path in `hasAllowedParams` unconditionally returned `true`. `allowExceptParamsInAllowed` incorrectly disallowed first-class callables in allowed zones because the null-args path in `hasAllowedParamsInAllowed` treated it identically to `allowParamsInAllowed`. Both are fixed. A new section in `docs/allow-with-parameters.md` documents the complete behavior for all param condition directives.
Closes#415
Copy file name to clipboardExpand all lines: docs/allow-with-parameters.md
+8Lines changed: 8 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -208,6 +208,14 @@ parameters:
208
208
209
209
But because the "positional _or_ named" limitation described above applies here as well, I generally don't recommend using these shortcuts and instead recommend specifying both `position` and `name` keys.
210
210
211
+
### First-class callables
212
+
213
+
When a function or method is used as a [first-class callable](https://www.php.net/functions.first_class_callable_syntax) (`strlen(...)`), no arguments are present at the call site - the callable is invoked later with whatever arguments the caller passes. Because parameter conditions that restrict which calls are *allowed* (`allowParamsInAllowed`, `allowParamsInAllowedAnyValue`, `allowParamFlagsInAllowed`, `allowParamsAnywhere`, `allowParamsAnywhereAnyValue`, `allowParamFlagsAnywhere`) cannot be evaluated without arguments, any first-class callable is always reported when such a condition is configured, no matter where in the code it appears.
214
+
215
+
Conditions that restrict which calls are *disallowed* behave differently - the forbidden parameter condition cannot be triggered without arguments. For the `*Anywhere` variants (`allowExceptParamsAnywhere`, `allowExceptParamsAnyValue`, `allowExceptParamFlags`, `allowExceptCaseInsensitiveParams`, and their aliases), first-class callables are never reported. For the `*InAllowed` variants (`allowExceptParamsInAllowed`, `allowExceptParamFlagsInAllowed`, and their aliases), first-class callables are not reported inside the relevant zone; outside it, the zone rule alone determines whether the call is reported.
216
+
217
+
To allow a first-class callable of a disallowed function, use a zone-based directive without a parameter condition, for example `allowIn`, `allowInMethods`, or `allowInInstanceOf`. Alternatively, use an anonymous function that calls the function with the required argument: `fn($x) => hash('sha256', $x)` instead of `hash(...)`.
218
+
211
219
### PHPDoc type strings
212
220
213
221
Instead of the `value` directive, you can use the `typeString` directive which allows you to specify arrays, unions, and anything that can be expressed with PHPDoc:
0 commit comments