FailedClosureBindingInvoked
error native on by default PHP ≥ 5.4Group: Probable bugs
Check closure binding before invoking it.
Example
Finding:
php
<?php
$f = static function () {}; $bound = $f->bindTo(new stdClass()); $bound();Reported:
- line 2: Check closure binding before invoking it.
Detection
- Track an explicitly static closure bound using Closure::bindTo or builtin Closure::bind with a proven non-null object. Report invocation of the failed binding result on the same reachable local path, including an immediately invoked result. The object is proven only when every alternative of its inferred type is a class instance: a nullable, untyped, mixed or otherwise partly non-class type proves nothing, because binding null to a static closure succeeds.
- Emit one finding per violating operation. Flow facts require a reachable path with no intervening mutation, escaped alias, unknown call involving the tracked value or analysis-budget exhaustion. Uncertain facts cannot prove a violation.
Exceptions (no report)
- Null object bindings, nonstatic closures, binding results guarded against null and unknown closure provenance are excluded. Binding failure alone is not reported here.
- Exclude shadowed builtin symbols, unresolved required types, incomplete syntax and unsupported PHP versions.
- A result is guarded when the invocation can only run with a non-null value of the same tracked result:
- it sits in an
if/elseifbody whose condition proves it, in anelseif/elsebranch reached only after a condition whose failure proves it, in the matching arm of a ternary, or in the right operand of&&/and(left operand proves it when true) or||/or(left operand proves it when false); - or an earlier statement of the same or an enclosing statement list is an
ifwhose body always leaves the path and whose condition proves it when false (itselseif/elsebranches, when they continue, are reached with that condition false too). - A condition proves the result when true if it is the result itself (truthiness),
!== null/!= nullin either operand order,instanceof,isset(...), builtinis_callable(...)/is_object(...), the negation of a condition that proves it when false, or a conjunction with such an operand. It proves the result when false if it is=== null/== null,empty(...), builtinis_null(...), the negation of a condition that proves it when true, or a disjunction with such an operand. A comparison of such a condition withtrueorfalseproves what the condition proves for that outcome (is_callable($b) === true); a comparison with any other value proves nothing. - A result held in a plain variable is also guarded when the flow facts of a followed scope exclude null at the invocation, for instance a result assigned inside the condition that tests it, a test stored in a boolean flag, or a loop condition. An arm of
match (true)is such a fact too: its own single condition held, and every condition of the earlier arms failed. A result held in a property or static property, or invoked in a scope the flow does not follow (one that evaluates or includes code), relies on the forms above, read wherever they guard the invocation; a write to the property between the test and the invocation voids the test.
- it sits in an
Fix
No automatic fix. Choosing a repair requires runtime information, error-handling policy or a semantic decision.
All edits use exact byte ranges, preserve comments and evaluation order, and require syntax supported by the target PHP version. A fix must never add an evaluation or silently change unrelated arguments.
PHP versions
Minimum PHP 5.4.
Configure
In custos.json:
json
{
"rules": {
"FailedClosureBindingInvoked": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
php
// @custos-ignore FailedClosureBindingInvoked