ZipEntryNameCollision
warning native off by default PHP ≥ 5.3Group: Probable bugs
Make ZIP entry replacement explicit. This opt-in rule applies the policy described in Detection.
Example
<?php
$z=new ZipArchive(); $z->open("bundle.zip",ZipArchive::CREATE); if ($z->addFromString("note.txt","one")) { $z->addFromString("note.txt","two"); }Reported:
- line 2: Make ZIP entry replacement explicit.
Detection
- Enabled policy forbids implicit ZIP entry replacement. Report resolved addFromString of a proven literal name in the successful true branch of an earlier addFromString on the same archive object with that identical name. Explicit flags and unknown names are excluded; other insertion patterns remain outside this guarded subset.
- The same archive object means the same proven flow identity, not the same variable spelling: an alias of the guarded archive still counts, while a receiver variable reassigned to another archive between the guard and the second insertion does not.
- The guarded entry is considered gone when a call on that archive, located after the guard and always executed before the second insertion, is one of close, open, deleteName, deleteIndex, renameName, renameIndex, unchangeAll, unchangeArchive, unchangeName or unchangeIndex. Their arguments are not matched against the entry: any such call removes proof. A call that runs only on some paths, or that precedes the guard, does not.
- Resolve builtin functions, extension classes, method receivers, constants and named arguments. Follow unchanged locals and aliases only within the same bounded lexical scope. Require a reachable path; merge branch facts only when all incoming paths establish them.
- Emit one finding at the violating operation, not one per evidence statement. Rebinding, writes, by-reference escape, unresolved calls that can mutate relevant state, and exhausted analysis budgets remove proof.
Exceptions (no report)
Only the successful branch establishes the earlier entry; exclude fallback additions in the failed branch.
- Exclude shadowed builtins, unresolved receivers, incomplete syntax and unknown prerequisite facts. An extension function name alone does not prove an application function is that builtin.
- A dominating valid guard or repair described in Detection prevents the finding. Both legacy noinspection and custos-ignore forms suppress this ID.
- An archive without a proven flow identity, such as a property or the result of a factory call, is matched by its receiver spelling (a variable, or a property chain rooted in a variable or a static property). It is reported only when nothing between the guard and the second insertion could rebind it or change its entries: any method call, static call, object creation, callback definition, call to a non-builtin function, or assignment to the receiver or to an object it is read from removes proof. Other receiver shapes without a proven identity are not reported.
- No report when the archive has a proven identity that became uncertain before the second insertion, for instance after it was passed to an unresolved call, nor when the second insertion is written inside a closure or arrow function nested in the guarded branch.
Fix
No automatic fix. The required repair depends on error handling, data selection, lifecycle ordering or application policy.
All edits use exact byte ranges and preserve comments and evaluation order. Offer no fix when its stated prerequisites cannot be proven or the replacement is unsupported by the configured PHP version.
PHP versions
Requires PHP 5.3 or later and the referenced extension API. Respect method-specific introduction versions within PHP 5.3–8.5; unsupported optional APIs cannot establish facts. Named arguments require PHP 8.0 or later.
Configure
In custos.json:
{
"rules": {
"ZipEntryNameCollision": {
"enabled": true
}
}
}Suppress
Before the statement or declaration (or the first statement of the file):
// @custos-ignore ZipEntryNameCollision