Skip to content

ReaddirFalsyFilenameLoss ​

warning native on by default quick-fix

Group: Probable bugs

Compare the directory entry with false.

Example ​

Finding:

php
<?php
while ($entry = readdir($dir)) { echo $entry; }

Reported:

  • line 2: Compare the directory entry with false.

Detection ​

  • Report a resolved readdir call whose result decides an if, elseif, while, do … while condition or the last condition expression of a for, when it is tested for truthiness: directly, through a plain = assignment in the condition, through any number of ! negations, or through a comparison with the true/false constant that is loose on the entry itself (readdir($h) == false). A legal filename consisting of digit zero is otherwise treated as end-of-directory. Once the entry has been turned into a boolean by ! or a loose comparison, further !, ==, !=, ===, !== tests of that boolean against true/false are followed too ((!readdir($h)) === false is still a truthiness test).
  • 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) ​

  • Strict comparisons against false, merely assigned results and unrelated functions are excluded. A strict ===/!== comparison applied to the entry itself exempts the whole condition, however it is then negated or compared (!(readdir($h) === false), (readdir($h) !== false) == true).
  • The tested value must be the entry: compound assignments (.=, ??=, …), by-reference assignments, an assignment of the already negated result ($done = !readdir($h)), comparisons with anything other than the true/false constants, other operators and call arguments are not reported. First-class callable syntax readdir(...) is not a call. Earlier comma-separated expressions of a for condition are not tested by the loop.
  • Exclude shadowed builtin symbols, unresolved required types, incomplete syntax and unsupported PHP versions.

Fix ​

Replace the whole condition with the entry expression, parenthesized, compared strictly: !== false when the original condition held for a truthy entry and === false otherwise. The entry expression is the call together with its own parentheses and plain assignment, copied verbatim, so the assignment and the call are evaluated exactly once and comments inside it survive. Negations and boolean comparisons around it are folded into the chosen operator (!!readdir($h) becomes (readdir($h)) !== false).

The surrounding !, parentheses and true/false comparison are dropped by the rewrite; when that dropped text contains a comment the finding is reported without a fix.

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 ​

Available throughout PHP 5.3–8.5.

Configure ​

In custos.json:

json
{
  "rules": {
    "ReaddirFalsyFilenameLoss": {
      "enabled": false
    }
  }
}

Suppress ​

Before the statement or declaration (or the first statement of the file):

php
// @custos-ignore ReaddirFalsyFilenameLoss

Released under the MIT License. Native rules and rules modeled on Php Inspections (EA Extended); independent clean-room implementation.