StrtokStateClobbered
warning native on by defaultGroup: Probable bugs
Detect starting a second two-argument strtok sequence between an initial sequence call and its one-argument continuation on the same reachable path.
Example
php
<?php
$first=strtok($a,','); strtok($b,':'); $next=strtok(',');Reported:
- line 2: Keep strtok sequences separate.
Detection
- Report starting a second two-argument strtok sequence between an initial sequence call and its one-argument continuation on the same reachable 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.
- The path is searched backwards from the continuation over the statements that precede it in the same statement list. The newest start is the closest statement that is a two-argument strtok call, bare or as the assigned value; the initial start is the next such statement before it. Statements in between need not be adjacent to either call.
- D4. A statement crossed by the search must be unable to start a sequence: it contains no function, method or static call, object creation, include, eval, yield, closure or arrow function. Between the newest start and the continuation, one-argument strtok calls are also accepted, since they only advance the current sequence. Any other statement ends the search without a report.
- When the statement list is the body of a
while,foreachorforloop, or the main branch of anif, the search continues with the statements before that construct, provided its header (the condition, the iterated expression, or theforinitializers and conditions) satisfies D4. This covers the nested tokenizer shape, where an inner sequence is started and consumed inside the loop that continues the outer one. Other enclosing constructs (else/elseifbranches,do,switch,try, function bodies) end the search.
Exceptions (no report)
- Completion/restart of the same intended sequence, no later continuation, or a wrapper known not to invoke strtok are excluded. Two starts on the same string expression are a restart.
- 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.
- The continuation belongs to the newest sequence: all three delimiter arguments are plain string literals, the continuation's delimiter equals the newest start's and differs from the initial start's. The initial call then only took a leading token (
$scheme = strtok($url, ':'); $name = strtok($file, '.'); $ext = strtok('.');). When a delimiter is not a literal, or both starts share the delimiter, the owner is ambiguous and the report is kept.
Fix
No automatic fix: independent tokenizer state requires restructuring.
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": {
"StrtokStateClobbered": {
"enabled": false
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
php
// @custos-ignore StrtokStateClobbered