Object Cloning & Security Implications
debt(d8/e5/b3/t7)
Closest to 'silent in production' (d8), PHPStan won't reliably detect shallow-clone aliasing of nested objects; bugs surface only when shared state mutates unexpectedly, slightly better than d9 because code review can spot missing __clone().
Closest to 'touches multiple files / significant refactor' (e5), implementing __clone() properly requires understanding the full object graph and may cascade to nested classes that also need __clone() methods.
Closest to 'localised tax' (b3), the cloning contract is per-class but applies across web/cli/queue contexts where the object is used; doesn't reshape the whole system.
Closest to 'serious trap' (t7), the misconception states clone looks like a deep copy but is shallow — contradicts intuition from languages/contexts where 'copy' means independent, and silently shares references to nested objects including sensitive state.
TL;DR
Explanation
PHP's clone keyword creates a shallow copy — scalar properties are copied but object properties remain references to the same objects. This causes security issues: cloning an authenticated session object shares the same internal state, cloning a PDO connection shares the resource, unserialised clones may bypass constructor validation. __clone() is called after cloning — use it to deep-clone nested objects and reset sensitive state (auth tokens, connection handles). Readonly properties cannot be modified in __clone() before PHP 8.3. Security: cloning PDO/resource objects is undefined behaviour.
Common Misconception
Why It Matters
Common Mistakes
- Cloning objects with nested objects and assuming full independence.
- Not implementing __clone() to deep-copy nested value objects.
- Cloning PDO or resource objects — undefined/broken behaviour.
Code Examples
class User {
public Address $address; // Object reference
public string $authToken;
}
$copy = clone $user;
$copy->address->city = 'Warsaw'; // Also changes $user->address->city!
class User {
public Address $address;
public string $authToken;
public function __clone() {
$this->address = clone $this->address; // Deep copy
$this->authToken = bin2hex(random_bytes(32)); // New token
}
}