← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Thread Safety

Concurrency PHP 7.0+ Intermediate
debt(d7/e7/b7/t9)
d7 Detectability Operational debt — how invisible misuse is to your safety net

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.

e7 Effort Remediation debt — work required to fix once spotted

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.

b7 Burden Structural debt — long-term weight of choosing wrong

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.

t9 Trap Cognitive debt — how counter-intuitive correct behaviour is

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.

About DEBT scoring →

TL;DR

Thread-safe code produces correct results regardless of how multiple threads interleave — achieved through immutability, atomic operations, or synchronisation primitives.

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

PHP is immune to concurrency issues because it's single-threaded — PHP-FPM runs many processes concurrently, all hitting shared resources (DB, files, cache). Concurrency bugs are real.

Why It Matters

Stateless, immutable design is the most reliable path to thread safety — and also makes code easier to test and reason about.

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

✗ Vulnerable
// Mutable static — breaks in Swoole/FrankenPHP:
class RequestContext {
    private static ?User $currentUser = null;
    public static function setUser(User $u): void { self::$currentUser = $u; }
}
✓ Fixed
// 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

Added 23 Mar 2026
Edited 5 Apr 2026
Views 54
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 2 pings S 1 ping M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 2 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 2 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Perplexity 8 Amazonbot 7 Scrapy 5 Google 4 Ahrefs 4 ChatGPT 4 Unknown AI 3 Claude 2 Bing 2 PetalBot 2 Meta AI 1
crawler 35 crawler_json 6 pre-tracking 1
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Prefer immutable objects and stateless functions. Avoid static mutable state — breaks under Swoole/FrankenPHP. Protect shared resources (files, DB, cache) with locks.
📦 Applies To
PHP 7.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
static \$|static::
Auto-detectable: ✗ No phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: High Context: Class Tests: Update
CWE-362 CWE-820


✓ schema.org compliant