Thread Safety
debt(d7/e7/b7/t9)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints list PHPStan, but the metadata explicitly notes automated detection is 'no' — PHPStan can flag static state patterns (`static $|static::`) but cannot reason about concurrency correctness or shared resource contention. Most thread-safety bugs in PHP (especially under Swoole/FrankenPHP) surface only in runtime testing under load or via careful code review of mutable static state.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix says 'prefer immutable objects and stateless functions' and 'avoid static mutable state' — but converting mutable static/global state to immutable, stateless designs touches every class and component that shares state. This is not a single-file fix; it's a design-level refactor that ripples across the codebase wherever shared mutable state was assumed safe.
Closest to 'strong gravitational pull' (b7). The choice applies to web, cli, and queue-worker contexts, meaning it affects every PHP execution model. Under modern runtimes like Swoole or FrankenPHP, every future maintainer must understand and respect thread-safety constraints when writing or modifying any stateful code. Incorrect assumptions compound over time, shaping how services, caches, and shared resources are designed throughout the system.
Closest to 'catastrophic trap — the obvious way is always wrong' (t9). The misconception field states exactly this: 'PHP is immune to concurrency issues because it's single-threaded.' This is the canonical wrong belief, deeply ingrained in PHP culture. A competent developer familiar with traditional PHP-FPM development will confidently use static mutable state, assume global state is process-local everywhere, and ignore locking — all of which are catastrophically wrong under Swoole/FrankenPHP and subtly wrong even in PHP-FPM when shared resources are involved.
TL;DR
Explanation
Code is thread-safe if it behaves correctly when multiple threads execute it simultaneously. Strategies: (1) Immutability — objects that cannot be modified are inherently thread-safe. (2) Thread-local storage — each thread has its own copy of mutable state. (3) Atomic operations — hardware-level indivisible read-modify-write. (4) Synchronisation — mutexes/locks. (5) Stateless design — functions with no shared state are thread-safe. In PHP: the language runtime is not thread-safe by default (ZTS — Zend Thread Safety — is a compile option). PHP-FPM uses separate processes (not threads) so most PHP code is safe. Extensions may not be ZTS-compiled. Swoole and ReactPHP introduce real concurrency within one process.
Common Misconception
Why It Matters
Common Mistakes
- Mutable static/global state in classes — shared across requests in Swoole/FrankenPHP.
- Assuming global state is safe in PHP-FPM — each process has its own, but shared resources (DB, Redis) still need protection.
- Not marking PHP extensions as ZTS-safe when using threaded PHP.
Code Examples
// Mutable static — breaks in Swoole/FrankenPHP:
class RequestContext {
private static ?User $currentUser = null;
public static function setUser(User $u): void { self::$currentUser = $u; }
}
// Immutable request context — safe in all runtimes:
class RequestContext {
public function __construct(
public readonly User $user,
public readonly string $requestId,
) {}
}
// Pass as dependency, not global static