# PHP Interpreter (php-src) — Admin & Architecture Guide

This guide is the deep reference for the **`php-src`** codebase — the C implementation of the PHP interpreter. It is written for administrators deploying PHP runtimes, engineers building or extending PHP, and architects who need to understand how the interpreter is structured internally.

It covers the Zend Engine, the OPcache bytecode cache and JIT, the SAPI layer, the build system, production configuration (php.ini, FPM pools), security, operations, and the implementation internals down to module level.

Everything here is derived directly from the source tree at commit `95b5b487`, which builds **PHP 8.6.0-dev**.

> [!NOTE]
> For day-to-day use of the binaries (`php`, `php-fpm`, `phpdbg`) and basic configuration, start with the companion **User Guide**. This Admin Guide assumes that material and goes deeper.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Architecture Overview](#2-architecture-overview)
3. [The Zend Engine](#3-the-zend-engine)
4. [OPcache & the JIT](#4-opcache--the-jit)
5. [The SAPI Layer & Request Lifecycle](#5-the-sapi-layer--request-lifecycle)
6. [Repository Structure](#6-repository-structure)
7. [Build System & Requirements](#7-build-system--requirements)
8. [Configuration Mastery](#8-configuration-mastery)
9. [PHP-FPM Operations](#9-php-fpm-operations)
10. [Security](#10-security)
11. [Operations & Observability](#11-operations--observability)
12. [Extension Development Internals](#12-extension-development-internals)
13. [Testing & Quality Assurance](#13-testing--quality-assurance)
14. [Troubleshooting](#14-troubleshooting)
15. [Contributing](#15-contributing)
16. [Potential Improvements](#16-potential-improvements)
17. [Quick Reference](#17-quick-reference)

---

## 1. Executive Summary

PHP is a general-purpose scripting language implemented in C. The `php-src` repository contains the entire interpreter: the Zend Engine (compiler + virtual machine), the core runtime (`main/`), the bundled extension library (`ext/`), the server interface bindings (`sapi/`), and the thread-safety layer (`TSRM/`).

| Attribute | Value |
|-----------|-------|
| Language | C (with some C++ in select extensions/JIT) |
| Builds | PHP 8.6.0-dev (Zend Engine v4.6) |
| License | BSD-3-Clause (Modified BSD) |
| Build system | Autotools (`buildconf` → `configure` → `make`) + Windows `nmake` |
| Source size | ~27.9M chars across ~2,400 code files; 26,483 files total |
| Bundled extensions | ~70 in `ext/` |
| SAPIs | cli, cgi, fpm, phpdbg, embed, apache2handler, litespeed, fuzzer |
| Threading | NTS (non-thread-safe) or ZTS (Zend Thread Safety) |
| JIT | IR-based, embedded in OPcache (`ext/opcache/jit`) |

**Design in one paragraph:** PHP source is lexed and parsed into an AST, compiled to Zend opcodes, and executed by a register-based virtual machine. OPcache persists the compiled opcodes in shared memory so they are reused across requests, and its JIT compiler can translate hot opcode sequences into native machine code via an embedded IR framework. The same engine is wrapped by multiple SAPIs that adapt it to a host (command line, FastCGI, Apache, an embedding C program). Memory is managed per-request by a custom allocator (Zend MM) that is bulk-freed at request shutdown.

> [!IMPORTANT]
> PHP **does not provide a sandbox**. Executing untrusted PHP code is always unsafe by design (see [Security](#10-security)). Treat every PHP process as fully trusting the code it runs.

---

## 2. Architecture Overview

```text
                         ┌──────────────────────────────────────────┐
   Request / invocation  │                 SAPI                      │
   ───────────────────▶  │  cli │ cgi │ fpm │ apache2handler │ embed │
                         └───────────────────┬──────────────────────┘
                                              │  sapi_module_struct hooks
                         ┌────────────────────▼──────────────────────┐
                         │             main/ (PHP core)               │
                         │  startup · php.ini · streams · output ·    │
                         │  superglobals · SAPI glue (main/SAPI.c)    │
                         └────────────────────┬──────────────────────┘
            ┌─────────────────────────────────┼─────────────────────────────────┐
            ▼                                  ▼                                  ▼
 ┌────────────────────┐         ┌──────────────────────────┐        ┌────────────────────┐
 │   ext/ extensions  │         │      Zend/ (engine)       │        │  ext/opcache       │
 │ standard, pdo,     │◀───────▶│ scanner→parser→AST→       │◀──────▶│  SHM opcode cache  │
 │ mbstring, curl,... │  zend   │ compiler→opcodes→VM       │  zend  │  + JIT (IR)        │
 └────────────────────┘  API    │ MM allocator · GC · OO    │  ext   └────────────────────┘
                                 └──────────────────────────┘
                         ┌────────────────────────────────────────────┐
                         │   TSRM/ (thread-safe resource manager)      │  ← ZTS builds only
                         └────────────────────────────────────────────┘
```

### The four layers

1. **Zend Engine (`Zend/`)** — the language core: lexer, parser, AST, compiler, opcode VM, memory manager, garbage collector, object model, and the C API that everything else builds on.
2. **PHP core (`main/`)** — process and request lifecycle, `php.ini` handling, the stream abstraction, output buffering, superglobals, and the SAPI bridge (`main/SAPI.c`, `main/SAPI.h`).
3. **Extensions (`ext/`)** — the function/class library exposed to userland, from the always-present `standard`/`spl` to optional database, crypto, and string extensions.
4. **SAPIs (`sapi/`)** — thin adapters that embed the engine into a host environment and implement the `sapi_module_struct` callbacks.

`TSRM/` (Thread-Safe Resource Manager) is a cross-cutting layer that, in ZTS builds, gives each thread its own copy of global state.

> [!NOTE]
> Extensions and OPcache both talk to the engine through the **Zend API** (`Zend/zend_API.h`) and the **extension hook structs** (`zend_module_entry`, `zend_extension`). OPcache is itself a Zend extension that hooks the compiler.

---

## 3. The Zend Engine

The Zend Engine lives in `Zend/` and is the heart of PHP. Its job: turn PHP source text into executable opcodes and run them.

### 3.1 The compilation pipeline

```text
PHP source
   │
   ▼  Zend/zend_language_scanner.l   (re2c-generated lexer)
Tokens
   │
   ▼  Zend/zend_language_parser.y    (bison-generated parser)
Abstract Syntax Tree (AST)           Zend/zend_ast.c
   │
   ▼  Zend/zend_compile.c            (compiler)
Opcode arrays (zend_op_array)        Zend/zend_opcode.c
   │
   ▼  Zend/zend_execute.c            (the virtual machine)
Result
```

| Stage | File | Role |
|-------|------|------|
| Lexer | `zend_language_scanner.l` | Tokenizes source. Generated by **re2c** at build time. |
| Parser | `zend_language_parser.y` | LALR grammar. Generated by **bison** at build time. |
| AST | `zend_ast.c` | Builds and walks the abstract syntax tree. |
| Compiler | `zend_compile.c` | Lowers the AST into opcode arrays; handles scoping, type info, inheritance prep. |
| Opcodes | `zend_opcode.c`, `zend_vm_opcodes.h` | The opcode set and the per-function `zend_op_array`. |

> [!IMPORTANT]
> The lexer and parser are **generated** files. After changing `*.l` or `*.y` you must have `re2c` and `bison` installed and re-run the build; `./buildconf` and `make` regenerate them.

### 3.2 The virtual machine

The executor in `zend_execute.c` is a register-based VM over the opcode arrays. Crucially, the VM dispatch loop is itself **generated**:

```text
Zend/zend_vm_def.h        ── opcode handler definitions (source of truth)
        │
        ▼  Zend/zend_vm_gen.php   (code generator, run at build time)
Zend/zend_vm_execute.h    ── the generated dispatch loop
Zend/zend_vm_opcodes.c    ── generated opcode metadata
```

`zend_vm_gen.php` specializes each opcode for its operand types (CONST/TMP/VAR/CV) to avoid runtime type checks in the hot path. This is why `zend_vm_execute.h` is enormous and machine-generated rather than hand-written.

> [!WARNING]
> Never edit `zend_vm_execute.h` directly — it is overwritten. Edit `zend_vm_def.h` (and the helpers) and regenerate with `php Zend/zend_vm_gen.php`.

The VM supports three dispatch models (selected at build time): CALL (function per handler), SWITCH, and the default GOTO threading where the compiler supports computed gotos (`--enable-re2c-cgoto` aids the related scanner path).

### 3.3 Values: `zval` and the type system

Every PHP value is a `zval` (`Zend/zend_types.h`) — a small tagged union carrying a type tag and either an inline scalar or a pointer to a reference-counted structure (string, array, object, resource). Reference-counted types share a common `zend_refcounted` header.

Key supporting subsystems:

| Subsystem | File | Purpose |
|-----------|------|---------|
| Hash tables | `zend_hash.c` | The `HashTable` — backs PHP arrays, symbol tables, and most internal maps. |
| Strings | `zend_string.h` | Interned, length-prefixed, reference-counted strings. |
| Objects | `zend_objects.c`, `zend_object_handlers.c` | Object storage and the per-class handler table (property access, casting, comparison). |
| Classes/inheritance | `zend_inheritance.c`, `zend_API.c` | Class linking, interface/trait composition, method tables. |
| Closures | `zend_closures.c` | First-class closures and `Closure` binding. |
| Enums | `zend_enum.c` | Backed/pure enumerations. |
| Constants | `zend_constants.c` | Compile-time and runtime constants. |

### 3.4 Memory management

PHP uses a **custom request-scoped allocator**, Zend MM, in `Zend/zend_alloc.c`:

- Memory is organized into **chunks → pages → bins** (`ZEND_MM_PAGE_SIZE`-based). Small allocations come from size-class bins; large ones are page-runs; huge ones go to the OS directly.
- The default heap maps memory and is reset cheaply at request end — most per-request memory is reclaimed in bulk rather than individually freed.
- A request's allocations are bounded by the `memory_limit` INI directive; exceeding it raises a fatal error.

On top of the allocator sits the **cycle-collecting garbage collector** (`zend_gc.c`). Reference counting reclaims most values immediately; the GC exists only to break reference *cycles*. It runs when the configurable root buffer fills (controlled by `zend.enable_gc`).

> [!TIP]
> `zend.enable_gc = On` (the default) is correct for almost all workloads. Long-running CLI daemons that build large cyclic graphs are the main case to monitor; `gc_collect_cycles()` can be called manually there.

### 3.5 Concurrency primitives

| Feature | File | Notes |
|---------|------|-------|
| Fibers | `zend_fibers.c` | Stackful coroutines (PHP 8.1+); the substrate for async libraries. |
| Generators | `zend_generators.c` | Lazy iterators via `yield`. |
| Observer API | `zend_observer.c` | Hook function entry/exit — used by profilers, debuggers, tracing extensions. |
| Atomics | `zend_atomic.c` | Low-level atomic operations. |
| Call stack | `zend_call_stack.c` | Stack-limit detection to convert C stack overflow into a catchable PHP error. |

> [!NOTE]
> The Observer API (`zend_observer.c`) is the modern, low-overhead extension point for APM/tracing tools (e.g. OpenTelemetry, Datadog, Tideways). Prefer it over older opcode-overriding hacks.

---

## 4. OPcache & the JIT

OPcache (`ext/opcache/`) is a **Zend extension** that eliminates recompilation and, optionally, JIT-compiles hot code. It is the single most important performance component in a production deployment.

### 4.1 The shared-memory bytecode cache

On first request, PHP compiles each script to opcodes. OPcache persists those opcodes in **shared memory (SHM)** so every subsequent request — across all FPM workers — reuses them without re-parsing or re-compiling.

| Component | File | Role |
|-----------|------|------|
| Accelerator core | `ZendAccelerator.c` | Hooks the compiler; manages the SHM cache lifecycle. |
| SHM backends | `shared_alloc_mmap.c`, `shared_alloc_posix.c`, `shared_alloc_shm.c`, `shared_alloc_win32.c` | Platform-specific shared-memory allocation (`opcache.preferred_memory_model`). |
| Persist | `zend_persist.c`, `zend_persist_calc.c` | Serialize/deserialize opcode arrays into/out of SHM. |
| File cache | `zend_file_cache.c` | On-disk second-level cache (`opcache.file_cache`), survives restarts. |
| Interned strings | within accelerator | Deduplicate strings across scripts in SHM. |
| Blacklist | `zend_accelerator_blacklist.c` | Exclude files from caching (`opcache.blacklist_filename`). |

**Validation model:** with `opcache.validate_timestamps=1` (the default), OPcache checks each file's mtime every `opcache.revalidate_freq` seconds and recompiles changed files. In production you typically set `validate_timestamps=0` and explicitly reset the cache on deploy.

> [!CAUTION]
> With `opcache.validate_timestamps=0`, OPcache will **not** notice changed files. You must reload FPM (or call `opcache_reset()`) on every deploy, or users will keep running stale code.

### 4.2 Preloading

`opcache.preload` points at a PHP script run once at server startup that loads classes/functions permanently into SHM, available to every request with zero per-request loading cost. `opcache.preload_user` specifies the user to run preload as (required when starting as root).

### 4.3 The JIT compiler

The JIT lives in `ext/opcache/jit/` and is built when `--enable-opcache-jit` is set (default when OPcache builds). It does **not** generate machine code directly from opcodes; instead it lowers them to an intermediate representation and uses an embedded **IR framework** (`ext/opcache/jit/ir/`) to optimize and emit native code.

```text
Hot opcodes
   │
   ▼  zend_jit.c / zend_jit_trace.c    (function & tracing JIT)
   ▼  zend_jit_ir.c                    (lower to IR)
   ▼  ext/opcache/jit/ir/ir_emit.c     (IR optimizer + native codegen)
Native machine code  (x86_64 / i386 / arm64)
```

| File | Role |
|------|------|
| `zend_jit.c` | JIT entry, function-mode compilation, configuration. |
| `zend_jit_trace.c` | Tracing JIT: records hot execution traces and compiles them. |
| `zend_jit_ir.c` | Translates opcodes/traces into the IR. |
| `jit/ir/ir.c`, `ir_emit.c`, `ir_cfg.c`, `ir_gcm.c` | The IR framework: SSA construction, optimization, register allocation, code emission. |

The JIT mode is configured by the 4-digit `opcache.jit` value (CPU/register/trigger/optimization). The common, recommended value is `tracing` (alias for `1254`); `function` (`1205`) compiles whole functions instead.

> [!IMPORTANT]
> The JIT requires both an explicit `opcache.jit_buffer_size` (e.g. `64M`) **and** `opcache.enable=1`. Without a non-zero buffer the JIT is inert even if `opcache.jit` is set.

> [!TIP]
> The JIT helps CPU-bound code (math, image processing, tight loops) the most. Typical web workloads — already I/O-bound and well-served by the opcode cache — gain less. Benchmark your own workload before assuming a win.

### 4.4 JIT tuning directives

The JIT exposes a large set of trigger/threshold directives (`opcache.jit_hot_loop`, `opcache.jit_hot_func`, `opcache.jit_max_trace_length`, `opcache.jit_blacklist_*`, etc.). These rarely need changing — see the [Quick Reference](#17-quick-reference) for the full list.

---

## 5. The SAPI Layer & Request Lifecycle

A **SAPI** adapts the engine to a host. Each SAPI in `sapi/` provides a `sapi_module_struct` (`main/SAPI.h`) of callbacks the core invokes at well-defined points.

### 5.1 The `sapi_module_struct` hooks

| Hook | When called | Typical job |
|------|-------------|-------------|
| `startup` | Once, at process start | Initialize the SAPI, register the module. |
| `shutdown` | Once, at process end | Tear down. |
| `activate` | At the start of each request | Per-request setup. |
| `deactivate` | At the end of each request | Per-request flush/cleanup. |
| `ub_write` | When PHP produces output | Write bytes to the client/stdout. |
| `flush` | On flush | Push buffered output. |
| `send_header` / `send_headers` | Before body output | Emit HTTP headers (no-op for CLI). |
| `read_post` | Reading the request body | Provide POST data. |
| `read_cookies` | Reading cookies | Provide the `Cookie` header. |
| `getenv` | Env lookup | SAPI-specific environment. |
| `sapi_error` | On error | Error reporting path. |

The core exposes `sapi_startup()`, `sapi_activate()`, `sapi_deactivate()`, and `sapi_shutdown()` (`main/SAPI.c`) that drive these hooks.

### 5.2 The five lifecycle phases

```text
1. Module Startup (MINIT)     once per process
     └─ each extension's MINIT: register classes, constants, INI entries
2. Request Startup (RINIT)    once per request
     └─ each extension's RINIT: per-request init, reset superglobals
3.   ── script execution ──   compile (or fetch from OPcache) + run opcodes
4. Request Shutdown (RSHUTDOWN) once per request
     └─ each extension's RSHUTDOWN; Zend MM bulk-frees request memory
5. Module Shutdown (MSHUTDOWN) once per process
     └─ each extension's MSHUTDOWN; persistent resources released
```

This is the model that makes PHP's "shared-nothing" concurrency work: state created in RINIT is destroyed in RSHUTDOWN, so each request starts clean. Long-lived workers (FPM) keep MINIT state (including OPcache SHM) across thousands of requests.

> [!NOTE]
> Extensions implement these phases via `PHP_MINIT_FUNCTION`, `PHP_RINIT_FUNCTION`, `PHP_RSHUTDOWN_FUNCTION`, `PHP_MSHUTDOWN_FUNCTION`, and `PHP_GINFO`/globals macros. See [Extension Development Internals](#12-extension-development-internals).

### 5.3 SAPI inventory

| SAPI | Directory | Host | Concurrency model |
|------|-----------|------|-------------------|
| cli | `sapi/cli` | Terminal | One process per invocation; long-lived for `-S`/`-a`. |
| cgi | `sapi/cgi` | CGI/FastCGI servers | Process per request (CGI) or FastCGI loop. |
| fpm | `sapi/fpm` | nginx/Apache via FastCGI | Master + worker pool (process manager). |
| apache2handler | `sapi/apache2handler` | Apache httpd | In-process module (`mod_php`); threads in worker MPM (needs ZTS). |
| embed | `sapi/embed` | Any C/C++ program | Library; host controls the lifecycle. |
| litespeed | `sapi/litespeed` | LiteSpeed/OpenLiteSpeed | LSAPI worker. |
| phpdbg | `sapi/phpdbg` | Terminal | Interactive debugger. |
| fuzzer | `sapi/fuzzer` | libFuzzer harness | Security fuzzing only (`--enable-fuzzer`). |

> [!TIP]
> For modern production, **FPM behind nginx** is the standard. `mod_php` ties PHP to Apache's process model and requires a thread-safe (ZTS) build under threaded MPMs; FPM decouples them and gives you process-pool control.

---

## 6. Repository Structure

The triage scan identified **121 high-priority module groups**. The largest and most important:

| Path | Size (chars) | What it contains |
|------|-------------:|------------------|
| `Zend/` | ~3.78M | The engine: scanner, parser, AST, compiler, VM, MM, GC, OO, API. |
| `ext/standard/` | ~2.48M | Core function library (strings, arrays, math, files, URLs, var, …). |
| `ext/pcre/pcre2lib/` | ~2.25M | Bundled PCRE2 regex library (third-party). |
| `ext/dom/` | ~1.03M | DOM extension (built on Lexbor). |
| `Zend/Optimizer/` | ~0.80M | Opcode optimizer (constant folding, DCE, SCCP, etc.). |
| `ext/mysqlnd/` | ~0.75M | MySQL native driver. |
| `main/` | ~0.74M | PHP core runtime + SAPI glue + streams. |
| `ext/phar/` | ~0.62M | PHP Archive (phar) support. |
| `ext/soap/` | ~0.62M | SOAP client/server. |
| `ext/spl/` | ~0.62M | Standard PHP Library (data structures, iterators). |
| `ext/hash/` | ~0.48M | Hashing algorithms. |
| `ext/opcache/` | ~0.47M | Bytecode cache + JIT. |
| `ext/openssl/` | ~0.45M | TLS/crypto via OpenSSL. |
| `sapi/phpdbg/` | ~0.44M | Interactive debugger. |
| `ext/fileinfo/libmagic/` | ~0.40M | Bundled libmagic (file type detection). |
| `ext/ffi/` | ~0.39M | Foreign Function Interface. |
| `ext/uri/uriparser/` | ~0.40M | Bundled uriparser (the new `uri` extension). |
| `ext/reflection/` | ~0.38M | Reflection API. |
| `main/streams/` | ~0.37M | Stream wrappers, filters, transports. |
| `TSRM/` | ~0.06M | Thread-Safe Resource Manager. |

### Top-level directories

```text
php-src/
├── Zend/          The Zend Engine (compiler + VM + runtime core)
│   ├── Optimizer/ Opcode optimizer passes
│   └── asm/       Fiber context-switch assembly per platform
├── main/          PHP core: startup, php.ini, output, SAPI bridge
│   └── streams/   Stream abstraction (wrappers, filters, transports)
├── ext/           ~70 bundled extensions
│   └── opcache/   Bytecode cache + JIT (jit/, jit/ir/)
├── sapi/          Server API bindings (cli, cgi, fpm, phpdbg, embed, …)
├── TSRM/          Thread-Safe Resource Manager (ZTS)
├── win32/         Windows-specific platform code
├── build/         Autotools macros and build helpers
├── scripts/       Dev tooling (phpize, php-config, gdb macros)
├── tests/         Cross-cutting language/integration .phpt tests
├── docs/          Internal implementation docs (Sphinx/reStructuredText)
├── benchmark/     Benchmark harness (incl. WordPress docker-compose)
├── configure.ac   Autotools entry point
├── php.ini-development / php.ini-production   INI templates
└── README.md / CONTRIBUTING.md / CODING_STANDARDS.md / SECURITY.md / UPGRADING
```

> [!NOTE]
> Several `ext/` directories vendor third-party libraries (PCRE2, libmagic, uriparser, libgd, libbcmath, Lexbor, the IR JIT framework). These carry their own licenses and are synced from upstream — see `ext/lexbor/patches/README.md` for an example of the patch-on-sync workflow.

---

## 7. Build System & Requirements

### 7.1 The Autotools flow

```text
configure.ac + ext/*/config.m4 + sapi/*/config.m4
        │  ./buildconf  (runs autoconf)
        ▼
    configure
        │  ./configure [flags]
        ▼
    Makefile + main/php_config.h
        │  make
        ▼
    binaries (sapi/*/...) + libphp
        │  make install
        ▼
    installed PHP
```

- `buildconf` regenerates `configure` from the `.m4` files. Required on Git checkouts; use `--force` after build-system changes.
- Each extension/SAPI declares its options and dependencies in its own `config.m4` (Unix) and `config.w32` (Windows).
- `main/php_config.h` is the generated header capturing the resolved build configuration (including whether this is a ZTS build).

### 7.2 Build prerequisites

| Requirement | Minimal Git build | Default build |
|-------------|-------------------|---------------|
| C toolchain + `make` | Required | Required |
| `autoconf` | Required | Required |
| `bison` | Required (parser) | Required |
| `re2c` | Required (lexer) | Required |
| `pkg-config` | Recommended | Recommended |
| `libxml2` dev headers | — | Required |
| `libsqlite3` dev headers | — | Required |

### 7.3 NTS vs ZTS

- **NTS (non-thread-safe)** — the default and the right choice for CLI and FPM (process-based concurrency). No locking overhead.
- **ZTS (Zend Thread Safety)** — required for threaded SAPIs (e.g. Apache worker MPM) and the `parallel` extension. Each thread gets its own globals via TSRM, at a small performance cost.

`php -v` prints `(NTS)` or `(ZTS)`.

### 7.4 Windows

Windows builds use `buildconf.bat`, `configure.bat`, and `nmake` with the PHP SDK, plus `win32/` for platform code. Prebuilt Windows binaries are available from [windows.php.net](https://windows.php.net).

### 7.5 Cross-architecture and 32-bit

The repo documents building 32-bit on x86_64 (`./configure --disable-all --build=i686-pc-linux-gnu` with `-m32` flags) and cross-testing arm64 via Docker buildx (see `ext/opcache/jit/README.md`). The JIT supports `x86_64`, `i386`, and `arm64`.

> [!TIP]
> For sanitizer builds use `--enable-debug` together with `--enable-address-sanitizer` (or `--enable-undefined-sanitizer`/`--enable-memory-sanitizer`). These are invaluable when chasing memory bugs in the engine or extensions.

---

## 8. Configuration Mastery

### 8.1 Configuration resolution order

1. Built-in defaults (compiled in).
2. Main `php.ini` — directory set by `--with-config-file-path`, overridable at runtime with `-c` or the `PHPRC` environment variable.
3. Scan-directory `*.ini` fragments — `--with-config-file-scan-dir` (e.g. distro `conf.d/`/`php.d/` files, loaded alphabetically).
4. Per-context overrides: CLI `-d key=value`; FPM `php_value`/`php_admin_value`; userland `ini_set()` for `PHP_INI_USER`/`PHP_INI_ALL` directives.

```bash
php --ini          # which files are loaded, and from where
php --ini=diff     # which entries differ from built-in defaults
```

### 8.2 Directive changeability (`access level`)

Every INI directive has an access level controlling where it may be set:

| Level | Settable in | Example |
|-------|-------------|---------|
| `PHP_INI_SYSTEM` | `php.ini`, `php_admin_value` only | `opcache.*`, `disable_functions` |
| `PHP_INI_PERDIR` | + `.htaccess`/`.user.ini` | `upload_max_filesize` |
| `PHP_INI_USER` | + `ini_set()` at runtime | `display_errors`, `memory_limit` |
| `PHP_INI_ALL` | anywhere | `default_charset` |

> [!IMPORTANT]
> OPcache settings are `PHP_INI_SYSTEM`: they must be in `php.ini`/an FPM `php_admin_value` and take effect at startup. You cannot change them per-request. This is the root of the "cannot be changed when OPcache is already set up" warning (issue #22216).

### 8.3 Production-hardening checklist

- [ ] `display_errors = Off`, `display_startup_errors = Off`
- [ ] `log_errors = On` with a writable `error_log`
- [ ] `expose_php = Off` (remove the `X-Powered-By` header)
- [ ] `allow_url_include = Off` (keep `allow_url_fopen` off too unless needed)
- [ ] `opcache.enable = 1`, `opcache.validate_timestamps = 0` (+ reload on deploy)
- [ ] `opcache.memory_consumption` sized for your codebase (e.g. `256M`)
- [ ] `disable_functions` for dangerous primitives if your app does not need them
- [ ] `open_basedir` to confine filesystem access where appropriate
- [ ] `session.cookie_secure = 1` (HTTPS), `cookie_httponly = 1`, `cookie_samesite = Lax/Strict`
- [ ] `memory_limit` and `max_execution_time` sized to your workload

### 8.4 Key directive groups

See the [Quick Reference](#17-quick-reference) for the full production-default tables. The groups that matter most operationally:

- **Resource limits:** `memory_limit`, `max_execution_time`, `max_input_time`, `post_max_size`, `upload_max_filesize`, `max_file_uploads`.
- **Error handling:** `error_reporting`, `display_errors`, `log_errors`, `error_log`, `zend.exception_ignore_args`.
- **OPcache/JIT:** `opcache.enable`, `opcache.memory_consumption`, `opcache.max_accelerated_files`, `opcache.validate_timestamps`, `opcache.revalidate_freq`, `opcache.preload`, `opcache.jit`, `opcache.jit_buffer_size`.
- **Sessions:** `session.save_handler`, `session.cookie_*`, `session.gc_maxlifetime`, `session.use_strict_mode`.
- **Security:** `disable_functions`, `disable_classes`, `open_basedir`, `allow_url_include`, `expose_php`.

---

## 9. PHP-FPM Operations

`php-fpm` is configured by a global section plus one or more **pools**. Global config: `php-fpm.conf` (which by default just `include`s `php-fpm.d/*.conf`). Each pool file (template: `www.conf`) defines a worker pool.

### 9.1 Global directives (`[global]`)

From `sapi/fpm/fpm/fpm_conf.c`:

| Directive | Purpose |
|-----------|---------|
| `pid` | Master PID file. |
| `error_log` | FPM master error log path. |
| `log_level` | `alert`/`error`/`warning`/`notice`/`debug`. |
| `log_limit`, `log_buffering` | Log line limit and buffering. |
| `daemonize` | Run in background (`no` under systemd/Docker). |
| `emergency_restart_threshold` / `emergency_restart_interval` | Auto-restart workers if too many crash in a window. |
| `process_control_timeout` | Time to wait for workers to react to signals. |
| `process.max`, `process.priority` | Global process cap and nice level. |
| `events.mechanism` | Event backend (`epoll`/`kqueue`/`select`). |
| `systemd_interval` | systemd watchdog ping interval. |
| `syslog.facility`, `syslog.ident` | syslog routing. |

### 9.2 Pool directives (per `[pool]`)

| Directive | Default (template) | Purpose |
|-----------|--------------------|---------|
| `user` / `group` | build-time | OS identity workers run as. |
| `listen` | `127.0.0.1:9000` | TCP `host:port` or a Unix socket path. |
| `listen.owner`/`group`/`mode` | — | Permissions for a Unix-socket listener. |
| `listen.allowed_clients` | — | IP allowlist for the listener. |
| `listen.backlog` | `511` | Socket listen backlog. |
| `pm` | `dynamic` | Process manager: `static`, `dynamic`, or `ondemand`. |
| `pm.max_children` | `5` | Hard cap on worker processes. |
| `pm.start_servers` | `2` | Workers spawned at start (dynamic). |
| `pm.min_spare_servers` | `1` | Min idle workers (dynamic). |
| `pm.max_spare_servers` | `3` | Max idle workers (dynamic). |
| `pm.max_spawn_rate` | `32` | Max workers spawned per cycle. |
| `pm.process_idle_timeout` | `10s` | Idle worker reaping (ondemand/dynamic). |
| `pm.max_requests` | `500` (commented) | Recycle a worker after N requests (leak mitigation). |
| `pm.status_path` / `pm.status_listen` | `/status` | Status endpoint. |
| `ping.path` / `ping.response` | — | Health-check endpoint. |
| `access.log` / `access.format` | — | Per-request access log. |
| `slowlog` / `request_slowlog_timeout` | — | Log stack traces of slow requests. |
| `request_terminate_timeout` | `0` | Hard kill a request after N seconds. |
| `rlimit_files` / `rlimit_core` | — | Per-worker resource limits. |
| `chroot` / `chdir` | — | Filesystem confinement / working dir. |
| `catch_workers_output` | `no` | Capture worker stdout/stderr to the FPM log. |
| `clear_env` | `yes` | Clear the environment for workers (set `no` to inherit). |
| `security.limit_extensions` | `.php …` | Only execute these file extensions. |
| `env[NAME]` | — | Set environment variables for workers. |
| `php_value[]` / `php_flag[]` | — | Override changeable INI directives. |
| `php_admin_value[]` / `php_admin_flag[]` | — | Override INI directives non-overridably. |

### 9.3 Sizing `pm.max_children`

```text
pm.max_children ≈  (RAM available to PHP)  /  (average worker RSS)
```

Measure average worker memory under load (e.g. via `pm.status_path`), leave headroom for the OS and other services, and set `pm.max_children` accordingly. Too high → swapping/OOM; too low → requests queue and latency spikes.

> [!IMPORTANT]
> Always `php-fpm -t` before reloading. Reload gracefully with `SIGUSR2` (or `systemctl reload php-fpm`) so in-flight requests finish.

### 9.4 Monitoring endpoints

- **Status:** enable `pm.status_path = /status`; query `?json`, `?full`, or `?html`. Reports active/idle/total processes, queue depth, slow requests, accepted connections.
- **Ping:** `ping.path = /ping` returns `ping.response` (`pong`) — a cheap liveness probe.

> [!NOTE]
> Issue #14212 documents that the status page can transiently report active processes above `pm.max_children` during scaling. Treat status as a sampled signal; alert on sustained saturation, not single snapshots.

### 9.5 Worker output and slow requests

- `catch_workers_output = yes` + `decorate_workers_output = no` routes worker stderr into the FPM log (essential for seeing fatal errors).
- `slowlog` + `request_slowlog_timeout = 5s` dumps a PHP backtrace for any request exceeding the threshold — the fastest way to find a hung endpoint.
- `request_terminate_timeout` is the hard backstop that kills a runaway request (set it above `slowlog` timeout).

---

## 10. Security

### 10.1 The trust model — read this first

PHP's security posture is explicit in `SECURITY.md`:

> PHP does not offer sandboxing, and the execution of untrusted code is always considered unsafe.

This means:

- **Executing attacker-controlled PHP is game over** — memory-safety violations from malicious destructors, `__toString()`, error handlers, etc., are classified as *bugs, not security issues*.
- Passing unsanitized input to functions that explicitly expect trusted input (e.g. `mysqli_query()`) is the caller's responsibility, not a PHP vulnerability.
- The hardening boundary is at *input* (`escapeshellarg()`, prepared statements, validation), not at *execution*.

> [!CAUTION]
> Never run user-supplied PHP, never `eval()` untrusted strings, and never `include`/`require` attacker-controlled paths. There is no safe sandbox to fall back on.

### 10.2 Reporting vulnerabilities

Report via GitHub Security Advisories: <https://github.com/php/php-src/security/advisories/new>, or `[email removed]`. Reports stay private until a fix ships; reporters are credited (MITRE Credit System).

### 10.3 Runtime hardening surface

| Control | Directive / mechanism | Effect |
|---------|-----------------------|--------|
| Disable risky functions | `disable_functions = exec,system,...` | Remove dangerous primitives from userland. |
| Confine filesystem | `open_basedir = /var/www` | Restrict file access to a subtree. |
| Block URL includes | `allow_url_include = Off` | Prevent remote code inclusion. |
| Hide version | `expose_php = Off` | Drop the `X-Powered-By` header. |
| Limit executable files | FPM `security.limit_extensions` | Only run intended extensions. |
| Drop privileges | FPM `user`/`group`, run master non-root | Least privilege per pool. |
| Chroot workers | FPM `chroot` | Filesystem isolation per pool. |
| Strip exception args | `zend.exception_ignore_args = On` | Keep sensitive args out of stack traces/logs. |
| Secure sessions | `session.cookie_secure/httponly/samesite`, `use_strict_mode` | Harden session cookies. |

### 10.4 Crypto and TLS

`ext/openssl` provides TLS and crypto; `ext/sodium` provides modern libsodium primitives. Password hashing (`password_hash()`) supports Argon2 when built with `--with-password-argon2`. Prefer sodium/Argon2 for new code.

### 10.5 Fuzzing

`sapi/fuzzer` (`--enable-fuzzer`) builds libFuzzer harnesses for parsers and unserializers; PHP is continuously fuzzed via OSS-Fuzz. When hardening a parser-adjacent extension, add a fuzzer target.

### 10.6 Notable issues from the tracker

| Issue | Area | Operational takeaway |
|-------|------|----------------------|
| #22218 | SOAP | `SoapServer::handle()` segfault from a `$_SERVER` lookup outside an `IS_ARRAY` guard — keep SOAP endpoints patched. |
| #22208 | Zend INI | NULL-deref in `zend_ini_bool_literal()` for non-existent INIs — validate INI files. |
| #22167 | SOAP | Out-of-range XML Schema ints silently accepted (`atoi()`), input-validation gap. |
| #22198 / #22200 | curl / streams | Memory leaks (callback exceptions, news streams) — watch long-lived worker RSS; use `pm.max_requests`. |

---

## 11. Operations & Observability

### 11.1 Inspecting a running build

```bash
php -v                        # version, Zend version, NTS/ZTS
php -i                        # full phpinfo (build flags, all INI values, loaded ext)
php -m                        # loaded modules
php --ri opcache              # OPcache + JIT runtime status
php -i | grep 'Configure Command'   # exact ./configure line
```

### 11.2 OPcache health

Monitor OPcache via `opcache_get_status()` in a small endpoint (or `php --ri opcache`):

- **Cache full / restarts:** if `opcache.memory_consumption` or `opcache.max_accelerated_files` is too small, OPcache thrashes (frequent restarts). Size both generously.
- **Wasted memory:** rises with `validate_timestamps=1` deployments; an explicit `opcache_reset()` on deploy is cleaner.
- **Interned strings buffer:** size with `opcache.interned_strings_buffer`.

### 11.3 FPM operations

| Task | Command |
|------|---------|
| Validate config | `php-fpm -t` |
| Graceful reload | `kill -USR2 <master_pid>` / `systemctl reload php-fpm` |
| Reopen logs | `kill -USR1 <master_pid>` |
| Status (JSON) | `curl 'http://127.0.0.1/status?json'` (via the web server) |
| Liveness | `curl http://127.0.0.1/ping` → `pong` |

### 11.4 Logging

- **FPM master log:** `error_log` (global). Set `log_level` and enable `catch_workers_output` to capture worker fatals.
- **Application errors:** PHP `error_log` directive (file or `syslog`).
- **Slow requests:** `slowlog` + `request_slowlog_timeout`.
- **Access log:** per-pool `access.log` with `access.format` placeholders (`%R`, `%t`, `%m`, `%r`, `%s`, `%{milli}d`, `%{kilo}M`, `%C`).

### 11.5 Profiling & tracing

- Use the **Observer API** (`zend_observer.c`)-based tools (XHProf successors, OpenTelemetry, Tideways) for function-level tracing with low overhead.
- `phpdbg -p` disassembles opcodes for inspection.
- `scripts/gdb/php_gdb.py` provides gdb macros (`zval` dumping, etc.) for debugging the C side. Build with `--enable-debug` for usable symbols.

> [!WARNING]
> Memory leaks in long-lived FPM workers accumulate across requests (several are tracked upstream — see §10.6). Mitigate with `pm.max_requests` to recycle workers, and monitor per-worker RSS.

---

## 12. Extension Development Internals

Extensions are the primary way to extend PHP in C. They register through `zend_module_entry` and implement the lifecycle hooks.

### 12.1 Anatomy of an extension

```text
ext/my_ext/
├── config.m4        Unix build config (PHP_ARG_ENABLE/WITH, PHP_NEW_EXTENSION)
├── config.w32       Windows build config
├── php_my_ext.h     Module header, globals struct
├── my_ext.c         Implementation (functions, MINIT/RINIT/...)
├── my_ext.stub.php  Function/class signatures (generates arginfo)
└── tests/*.phpt     Tests
```

Scaffold a new one:

```bash
php ext/ext_skel.php --ext my_ext
```

### 12.2 The lifecycle macros

| Macro | Maps to | Use |
|-------|---------|-----|
| `PHP_MINIT_FUNCTION` | Module startup | Register classes, constants, INI entries. |
| `PHP_MSHUTDOWN_FUNCTION` | Module shutdown | Release process-wide resources. |
| `PHP_RINIT_FUNCTION` | Request startup | Per-request init. |
| `PHP_RSHUTDOWN_FUNCTION` | Request shutdown | Per-request cleanup. |
| `PHP_MINFO_FUNCTION` | `phpinfo()` section | Report config/version. |
| `ZEND_DECLARE_MODULE_GLOBALS` | Globals | Thread-safe per-request globals. |

### 12.3 Userland-facing definitions: stubs & arginfo

Modern extensions declare their function/method signatures in a `.stub.php` file; `build/gen_stub.php` generates the `*_arginfo.h` with the C `zend_function_entry`/argument metadata. This keeps the reflection-visible signature and the C registration in sync.

> [!IMPORTANT]
> Edit the `.stub.php` and regenerate arginfo (`build/gen_stub.php`); do not hand-edit `*_arginfo.h`. Signature drift between stub and arginfo is a common review rejection.

### 12.4 The Zend API

`Zend/zend_API.h` is the surface for extension authors: parameter parsing (`ZEND_PARSE_PARAMETERS_*` / the older `zend_parse_parameters`), `zval` manipulation, class/object construction, exception throwing, and the `RETURN_*`/`RETVAL_*` result macros. `UPGRADING.INTERNALS` documents API changes between releases — read it when porting an extension across PHP versions.

### 12.5 `phpize` for out-of-tree extensions

Extensions built against an installed PHP use `phpize` (in `scripts/`) to generate a build system without the full source tree:

```bash
cd my_ext
phpize
./configure --with-php-config=$(which php-config)
make && make install
```

---

## 13. Testing & Quality Assurance

### 13.1 The `.phpt` test format

Tests are `.phpt` files driven by `run-tests.php`. Each has sections — `--TEST--`, `--FILE--`, and one of `--EXPECT--`/`--EXPECTF--`/`--EXPECTREGEX--`, plus optional `--SKIPIF--`, `--INI--`, `--ENV--`. The runner raises a `BorkageException` for malformed tests (missing `--TEST--`, missing `--FILE--`, unknown sections).

```bash
make test                              # full suite
make TEST_PHP_ARGS=-j$(nproc) test     # parallel
make TESTS=ext/json test               # one extension
```

### 13.2 Environment-driven extension tests

Database and network extensions need a live service and credentials via environment variables (documented in each `ext/<name>/tests/README.md`):

| Extension | Key env vars |
|-----------|--------------|
| mysqli | `MYSQL_TEST_HOST/USER/PASSWD/DB/PORT/SOCKET/ENGINE` |
| pdo_mysql | `PDO_MYSQL_TEST_DSN/HOST/DB/USER/PASS/...` |
| pgsql | local PostgreSQL + a `test` database |
| pdo_dblib | `PDO_DBLIB_TEST_DSN/USER/PASS` (SQL Server) |
| ldap | `LDAP_TEST_HOST/BASE/USER/PASSWD` (+ Vagrant setup) |
| snmp | `SNMP_HOSTNAME/PORT/COMMUNITY/MIBDIR` (+ snmpd) |

### 13.3 Sanitizers, valgrind, and the JIT

```bash
# AddressSanitizer build (needs rebuild, fast at runtime)
./configure --enable-debug --enable-address-sanitizer && make -j$(nproc)

# valgrind at test time (no rebuild, slower)
make test TEST_PHP_ARGS="-m --show-mem -j$(nproc)"

# JIT stress
make test TESTS="-d opcache.jit_buffer_size=16M -d opcache.enable=1 \
  -d opcache.enable_cli=1 -d opcache.protect_memory=1 \
  -d opcache.jit=tracing --repeat 2 -j$(nproc) ext/opcache Zend"
```

`opcache.protect_memory=1` makes the JIT trap writes to read-only memory — a frequent root cause of JIT bugs. `--repeat 2` catches trace bugs that only appear on the second execution.

### 13.4 CI and fuzzing

The project runs GitHub Actions on every push and is continuously fuzzed via OSS-Fuzz. `scripts/dev/bless_tests.php` regenerates expected output for `--EXPECTF--` tests after intentional changes.

---

## 14. Troubleshooting

### Error: `bison`/`re2c` not found during build

**Cause:** Parser/lexer generators missing (or `bison` too old on macOS).
**Solution:** Install recent `bison` and `re2c`; put them on `PATH` before `./buildconf`.
**Prevention:** Install the full prerequisite list; on macOS source Homebrew's `bison` path.

### Warning: `opcache.X cannot be changed when OPcache is already set up` (#22216)

**Cause:** OPcache directives are `PHP_INI_SYSTEM`; some are locked once OPcache initializes (notably on ZTS).
**Solution:** Set all `opcache.*` values in `php.ini`/FPM `php_admin_value`, applied at startup — never via `-d`/`ini_set()` after init.
**Prevention:** Treat OPcache config as static, per-process startup config.

### Symptom: code changes don't take effect

**Cause:** `opcache.validate_timestamps=0` and the cache was not reset on deploy.
**Solution:** Reload FPM (`systemctl reload php-fpm`) or call `opcache_reset()` after deploying.
**Prevention:** Wire a cache reset into your deploy pipeline.

### Symptom: FPM returns 502/504 under load

**Cause:** `pm.max_children` too low → request queue saturates; or `request_terminate_timeout` killing long requests.
**Solution:** Inspect `pm.status_path`; raise `pm.max_children` within your memory budget; profile slow endpoints via `slowlog`.
**Prevention:** Size the pool from `RAM / avg_worker_RSS`; set `slowlog`/`request_terminate_timeout`.

### Symptom: worker memory grows over time

**Cause:** A leak accumulating across requests in a long-lived worker (several tracked upstream — #22198, #22200).
**Solution:** Set `pm.max_requests` to recycle workers; identify the leaking extension; upgrade.
**Prevention:** Monitor per-worker RSS; keep extensions current.

### Error: `No input file specified.`

**Cause:** FastCGI `SCRIPT_FILENAME` points to a missing file, or `security.limit_extensions` blocks it.
**Solution:** Align the web server's `SCRIPT_FILENAME` with the real path; check `security.limit_extensions`.
**Prevention:** Keep document-root and FastCGI params consistent.

### Crash: segfault only with the JIT enabled

**Cause:** A JIT trace bug (often surfacing on repeated execution).
**Solution:** Reproduce with `--repeat 2 -d opcache.protect_memory=1`; rebuild `--enable-debug --enable-address-sanitizer`; report upstream.
**Prevention:** Run the JIT stress test before deploying a JIT build; consider `opcache.jit=function` if tracing is unstable for your code.

### Build fails after `git pull`

**Cause:** Stale generated parser/lexer/`configure`.
**Solution:** `./buildconf --force`, re-`./configure`, `make`. If needed, `make clean`/`git clean -xfd`.
**Prevention:** Regenerate the build system after upstream build-file changes.

### Tests `BorkageException: missing section --FILE--`

**Cause:** A malformed `.phpt` (missing required section).
**Solution:** Ensure the test has `--TEST--`, `--FILE--`, and an `--EXPECT*--`. Use `--SKIPIF--` for conditional skips.
**Prevention:** Copy an existing well-formed `.phpt` as a template.

---

## 15. Contributing

| Item | Where |
|------|-------|
| Source / PRs | [github.com/php/php-src](https://github.com/php/php-src) |
| Contributing guide | `CONTRIBUTING.md` |
| Coding standards | `CODING_STANDARDS.md` |
| Internal docs | [php.github.io/php-src](https://php.github.io/php-src/) + `docs/` (Sphinx) |
| Upgrading notes | `UPGRADING` (userland), `UPGRADING.INTERNALS` (C API) |
| Mailing list | [email removed] |

**Process:**
- **Bug fixes** don't require an RFC. Reference issues as `GH-NNNNNN` (or `#NNNNNN` for the legacy bugs.php.net tracker) in the commit message.
- **New features/language changes** require an [RFC](https://wiki.php.net/rfc) accepted by a developer vote.
- Fork → branch → PR; merges follow the [Git workflow](https://wiki.php.net/vcs/gitworkflow).

**Building the internal docs:**

```bash
cd docs
pip install -r requirements.txt   # Sphinx + sphinx-design + sphinxawesome-theme + rstfmt
make html                         # output in docs/build/html/index.html
```

> [!NOTE]
> The C code follows the conventions in `CODING_STANDARDS.md` (tabs for indentation, specific brace style, naming). PRs that violate them are routinely sent back. Generated files (`zend_vm_execute.h`, `*_arginfo.h`, scanner/parser) must be regenerated from their sources, not hand-edited.

---

## 16. Potential Improvements

These are observations for maintainers and downstream packagers, derived from the source and the issue tracker — not defects in your deployment.

- **OPcache config rigidity on ZTS (#22216):** the "cannot be changed when already set up" path could fail more gracefully or document the constraint inline. Packagers should ship sane static `opcache.*` defaults.
- **Long-lived worker leaks (#22198, #22200):** periodic leaks in callback/exception and stream paths argue for keeping `pm.max_requests` set as a routine mitigation until each is fixed upstream.
- **SOAP input validation (#22167, #22218):** `ext/soap` has had `$_SERVER`-guard and `atoi()`-range issues; deployments exposing SOAP should track these closely or front the endpoint with validation.
- **JIT observability:** the JIT's many `opcache.jit_*` tuning knobs are powerful but opaque; richer runtime introspection (beyond `opcache_get_status()`) would help operators decide between `tracing` and `function` modes.
- **Vendored third-party libs:** PCRE2, libmagic, libgd, Lexbor, uriparser, and the IR framework are vendored and patched on sync (see `ext/lexbor/patches/`). Track upstream CVEs for these independently of PHP releases.

---

## 17. Quick Reference

### Build essentials

```bash
./buildconf [--force]                 # regenerate configure
./configure --enable-debug            # development build
./configure                           # production build
./configure --disable-all --enable-debug   # minimal engine-hacking build
make -j$(nproc)                       # compile
make install                          # install (may need sudo)
make TEST_PHP_ARGS=-j$(nproc) test    # run test suite
```

### Binaries & SAPIs

```text
php (cli)   php-cgi (cgi)   php-fpm (fpm)   phpdbg   libphp (embed/apache2handler)   litespeed
```

### Key engine files

| File | Role |
|------|------|
| `Zend/zend_language_scanner.l` | Lexer (re2c) |
| `Zend/zend_language_parser.y` | Parser (bison) |
| `Zend/zend_ast.c` | AST |
| `Zend/zend_compile.c` | Compiler |
| `Zend/zend_vm_def.h` + `zend_vm_gen.php` | VM definition + generator |
| `Zend/zend_execute.c` | Virtual machine |
| `Zend/zend_alloc.c` | Zend MM allocator |
| `Zend/zend_gc.c` | Cycle-collecting GC |
| `Zend/zend_hash.c` | HashTable (arrays) |
| `Zend/zend_API.c/.h` | Extension C API |
| `main/main.c` | Process/request lifecycle |
| `main/SAPI.c/.h` | SAPI bridge |
| `ext/opcache/ZendAccelerator.c` | SHM bytecode cache |
| `ext/opcache/jit/zend_jit*.c` + `jit/ir/` | JIT compiler + IR framework |

### OPcache directives (full list)

```text
opcache.enable                       opcache.enable_cli
opcache.memory_consumption           opcache.interned_strings_buffer
opcache.max_accelerated_files        opcache.max_wasted_percentage
opcache.max_file_size                opcache.validate_timestamps
opcache.revalidate_freq              opcache.revalidate_path
opcache.use_cwd                      opcache.save_comments
opcache.enable_file_override         opcache.optimization_level
opcache.dups_fix                     opcache.blacklist_filename
opcache.force_restart_timeout        opcache.error_log
opcache.log_verbosity_level          opcache.preferred_memory_model
opcache.protect_memory               opcache.restrict_api
opcache.mmap_base                    opcache.cache_id
opcache.file_cache                   opcache.file_cache_only
opcache.file_cache_consistency_checks  opcache.file_cache_fallback
opcache.file_cache_read_only         opcache.huge_code_pages
opcache.lockfile_path                opcache.file_update_protection
opcache.preload                      opcache.preload_user
opcache.record_warnings              opcache.validate_permission
opcache.validate_root
# JIT
opcache.jit                          opcache.jit_buffer_size
opcache.jit_debug                    opcache.jit_bisect_limit
opcache.jit_prof_threshold           opcache.jit_max_root_traces
opcache.jit_max_side_traces          opcache.jit_max_exit_counters
opcache.jit_hot_loop                 opcache.jit_hot_func
opcache.jit_hot_return               opcache.jit_hot_side_exit
opcache.jit_blacklist_root_trace     opcache.jit_blacklist_side_trace
opcache.jit_max_loop_unrolls         opcache.jit_max_recursive_calls
opcache.jit_max_recursive_returns    opcache.jit_max_polymorphic_calls
opcache.jit_max_trace_length         opcache.opt_debug_level
```

### FPM process-manager cheat-sheet

```text
pm = static     → fixed pool of pm.max_children (predictable, lowest latency)
pm = dynamic    → scale between min/max_spare_servers up to max_children
pm = ondemand   → spawn only on request, reap after pm.process_idle_timeout
```

### Lifecycle phases

```text
MINIT → RINIT → [execute] → RSHUTDOWN → MSHUTDOWN
(once per process) (per request)        (once per process)
```

### Production hardening (one-liner audit)

```bash
php -i | grep -iE 'display_errors|expose_php|allow_url_include|opcache.enable|opcache.validate_timestamps'
```

### Reference links

- Internal docs: [php.github.io/php-src](https://php.github.io/php-src/)
- RFC process: [wiki.php.net/rfc](https://wiki.php.net/rfc)
- Security advisories: [github.com/php/php-src/security/advisories/new](https://github.com/php/php-src/security/advisories/new)
- Issues: [github.com/php/php-src/issues](https://github.com/php/php-src/issues)
- IR JIT framework: [github.com/dstogov/ir](https://github.com/dstogov/ir)

---

_Generated June 2026 from php/php-src at 95b5b48. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
