StreamUseAfterClose
error native on by defaultGroup: Probable bugs
Detect a stream operation requiring an open resource on an identity closed by a successful fclose on every incoming path.
Example
php
<?php
function bad($path) { $h = fopen($path, 'wb'); if ($h === false) { return; } fclose($h); fwrite($h, 'next'); }Reported:
- line 2: Use an open stream for this operation.
Detection
- Report a stream operation requiring an open resource on an identity closed by a successful fclose on every incoming path.
- Resolve builtin calls and named arguments before checking the contract. Project-local callees are considered only when their bodies or immutable summaries establish the required facts. The default is enabled, with positive proof required.
- For flow-dependent conditions, require a reachable source-to-use path with no intervening invalidation. Unknown calls, ambiguous aliases, recursion or analysis-budget exhaustion cannot establish definite facts.
Exceptions (no report)
- Fresh reopened identity, guarded failed close, unknown handles and escaped aliases invalidate proof.
- A closure created anywhere in the same scope that binds the handle variable by reference (
use (&$h)) invalidates proof, whether it is created before or after the close: calling it can store a newly opened stream in the variable. A by-value capture, a by-reference capture of another variable, and a capture written inside a nested function body do not. - No report where the operation runs only when the reaching
fclosereturned false, since a refused close leaves the stream open. The result may be tested directly or through an unchanged local holding it, by negation, a comparison with a boolean literal,and/or, a ternary, anif/elsebranch, or an early exit of the success branch. An operation reached only when the close returned true is still reported, as is one guarded by a local that was reassigned after the close. - Unresolved targets, incomplete required facts, suppressed findings and PHP versions lacking the referenced API or language feature produce no report. User declarations shadowing builtin names are not builtin contracts.
- A close whose stream argument comes from an unpacked list (
fclose(...$handles)) may close the stream or not: the use is not reported as following a close.
Fix
No automatic fix: the correct operation order or reopening policy is unknown.
PHP versions
Apply only on PHP versions supporting the referenced APIs and syntax (within PHP 5.3–8.5). Builtin behavior and argument contracts follow the configured PHP version. Extension-specific calls require resolved extension API symbols; do not treat same-named application functions as extension calls.
Options
None. Enable or disable this rule through normal rule configuration.
Configure
In custos.json:
json
{
"rules": {
"StreamUseAfterClose": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
php
// @custos-ignore StreamUseAfterClose