Install

php/php-src

PHP Interpreter

Last updated on Dec 18, 2025 (Commit: b6f786a)

Overview

Relevant Files
  • README.md
  • main/php.h
  • Zend/zend.h
  • main/SAPI.h

PHP is a popular general-purpose scripting language especially suited to web development. The PHP interpreter is a complex system that transforms human-readable PHP code into executable operations through multiple processing stages. This codebase implements the complete PHP runtime environment, including the Zend Engine virtual machine, multiple Server APIs (SAPIs), and a comprehensive extension system.

Core Components

The interpreter is organized around several key architectural layers:

  1. Zend Engine - The core virtual machine that parses, compiles, and executes PHP code. It implements a stack-based bytecode interpreter with support for object-oriented programming, type systems, and error handling.

  2. SAPI Layer - Server API abstraction that allows PHP to run in different environments (CLI, Apache, FPM, CGI, embedded systems). Each SAPI handles environment-specific initialization, request processing, and output handling.

  3. Extension System - Modular architecture for adding functionality. Bundled extensions include standard library functions, date/time handling, JSON processing, regular expressions (PCRE), and multibyte string support.

  4. Memory Management - Custom allocator optimized for typical PHP workloads with integrated garbage collection for circular references.

Compilation Pipeline

Loading diagram...

The pipeline consists of four stages: lexical analysis (tokenization), syntax analysis (parsing), opcode generation (compilation), and virtual machine execution. The Zend Virtual Machine uses specialized opcode handlers and supports multiple threading models for high performance.

Key Features

  • Type System - Dynamic typing with type declarations and strict type checking modes
  • Object-Oriented - Full OOP support including classes, interfaces, traits, and attributes
  • Error Handling - Exceptions, error levels, and custom error handlers
  • Streams - Unified I/O abstraction for files, networks, and custom protocols
  • Cross-Platform - Runs on Unix-like systems and Windows with platform-specific optimizations

Building and Testing

PHP can be built from source using autoconf and standard build tools. The project includes an extensive test suite (make test) covering language features, extensions, and security. Development builds support debugging with --enable-debug, while production builds optimize for performance.

Architecture & Compilation Pipeline

Relevant Files
  • Zend/zend_compile.c - Core compilation logic
  • Zend/zend_execute.c - Virtual machine execution engine
  • Zend/zend_language_scanner.l - Lexer definition (re2c)
  • Zend/zend_language_parser.y - Parser definition (Bison)
  • Zend/zend_vm_def.h - VM opcode handler definitions
  • Zend/zend_vm_gen.php - VM code generator
  • Zend/Optimizer/ - Optimization passes

PHP's compilation pipeline transforms source code into executable opcodes through four distinct phases. Understanding this flow is essential for working with the Zend Engine.

The Four-Phase Pipeline

Loading diagram...

Tokenization breaks source files into tokens using re2c, a lexer generator. The scanner definition in Zend/zend_language_scanner.l defines token patterns. The zendlex() function in zend_compile.c drives this phase.

Parsing builds an Abstract Syntax Tree (AST) from tokens using Bison, a parser generator. The grammar lives in Zend/zend_language_parser.y. Each AST node represents a language construct with type, children, and source position metadata.

Compilation traverses the AST and emits opcodes—virtual machine instructions. The zend_compile.c file contains the core logic, with functions like zend_compile_expr() and zend_emit_op() generating instructions. Each opcode follows three-address code format: result, operand1, operand2.

Execution interprets opcodes via the virtual machine in zend_execute.c. The VM uses a dispatch loop that reads each opcode and executes its handler. Handlers are defined in zend_vm_def.h and generated into zend_vm_execute.h by zend_vm_gen.php.

Opcode Generation & VM Threading

The VM supports multiple threading models (CALL, SWITCH, GOTO, HYBRID) configured at build time. zend_vm_gen.php generates specialized opcode handlers based on operand types, reducing type-checking overhead. Specialization creates separate handlers for common cases like ZEND_ADD with two constants versus mixed types.

Optimization Pipeline

After compilation, opcodes pass through the Optimizer (Zend/Optimizer/) before caching. Key passes include:

  • Block Pass - Control flow analysis
  • Dead Code Elimination - Removes unused instructions
  • Constant Propagation - Evaluates constant expressions at compile-time
  • Escape Analysis - Optimizes object allocation
  • Type Inference - Deduces variable types for specialization

Opcache & JIT

The opcache extension caches compiled opcodes in shared memory, skipping tokenization, parsing, and compilation on subsequent requests. It also includes a JIT compiler that converts hot opcodes to native machine code, providing significant performance gains for CPU-bound workloads.

Zend Engine & Virtual Machine

Relevant Files
  • Zend/zend.c
  • Zend/zend_execute.c
  • Zend/zend_vm_def.h
  • Zend/zend_vm_execute.skl
  • Zend/zend_compile.h
  • Zend/zend_vm_gen.php

The Zend Engine is PHP's core execution engine, responsible for compiling PHP code into opcodes and executing them via a specialized virtual machine. It provides the foundation for all PHP script execution.

Core Architecture

The Zend VM is a stack-based virtual machine that executes a stream of opcodes. Each opcode represents a low-level operation (arithmetic, function calls, variable access, etc.). The VM uses a specialized architecture that allows multiple execution strategies and opcode handler specialization for performance optimization.

Key Components:

  • Opcodes (zend_op): Represent individual operations with operands and result storage
  • Execution Data (zend_execute_data): Maintains the execution context for each function call
  • Opcode Handlers: Specialized functions that implement each opcode's behavior
  • VM Stack: Manages function call frames and temporary variables

Opcode Structure

Each opcode is defined by the zend_op structure:

struct _zend_op {
    zend_vm_opcode_handler_t handler;  /* Handler function pointer */
    znode_op op1, op2, result;         /* Operand references */
    uint32_t extended_value;           /* Extra data (flags, counts) */
    uint32_t lineno;                   /* Source line number */
    uint8_t opcode;                    /* Opcode type (ZEND_ADD, etc.) */
    uint8_t op1_type, op2_type, result_type;  /* Operand types */
};

Operand types include: IS_UNUSED, IS_CONST, IS_TMP_VAR, IS_VAR, and IS_CV (compiled variable).

Execution Data Frame

The zend_execute_data structure tracks execution state:

struct _zend_execute_data {
    const zend_op *opline;           /* Current opcode */
    zend_execute_data *call;         /* Current function call */
    zval *return_value;              /* Return value storage */
    zend_function *func;             /* Executing function */
    zval This;                       /* Object context + metadata */
    zend_execute_data *prev_execute_data;  /* Previous frame */
    zend_array *symbol_table;        /* Local variables */
    void **run_time_cache;           /* Cached lookups */
};

VM Execution Models

The VM supports multiple threading models (selectable at compile time):

  • CALL Threading: Each handler is a function call (portable, slower)
  • SWITCH Threading: Single switch statement dispatches handlers (moderate speed)
  • GOTO Threading: Direct jumps to handler labels (fastest, requires GCC)
  • HYBRID Threading: Combines CALL and GOTO for balance

Opcode Handler Specialization

Handlers are specialized based on operand types to optimize common cases. For example, ZEND_ADD has specialized versions for:

  • CONST|CONST (compile-time optimization)
  • LONG|LONG (fast integer addition)
  • DOUBLE|DOUBLE (fast float addition)
  • Generic fallback for mixed types

Specialization is generated by zend_vm_gen.php, which processes zend_vm_def.h templates and produces optimized zend_vm_execute.h.

Execution Flow

Loading diagram...

Memory Management

The Zend Engine includes a custom memory allocator that:

  • Reduces allocation overhead through pooling
  • Tracks allocations per request for cleanup
  • Supports persistent allocations across requests
  • Integrates with garbage collection for circular references

Performance Optimizations

  1. Opcode Caching: OPcache extension caches compiled opcodes
  2. JIT Compilation: Modern PHP includes a tracing JIT that compiles hot code paths to native machine code
  3. Type Inference: The optimizer analyzes types to enable specialization
  4. Global Register Allocation: On supported architectures, critical pointers use CPU registers

SAPI Modules & Server Integration

Relevant Files
  • main/SAPI.h
  • main/SAPI.c
  • sapi/cli/php_cli.c
  • sapi/fpm/fpm/fpm_main.c
  • sapi/cgi/cgi_main.c
  • sapi/apache2handler/sapi_apache2.c
  • sapi/embed/php_embed.c

SAPI (Server API) is the abstraction layer that allows PHP to run in different server environments. Each SAPI module implements a standardized interface defined by sapi_module_struct, enabling PHP to integrate with web servers, command-line environments, and embedded applications.

SAPI Module Architecture

The sapi_module_struct (defined in main/SAPI.h) is the core interface that every SAPI implementation must provide. It contains function pointers for critical operations:

  • Lifecycle hooks: startup(), shutdown(), activate(), deactivate()
  • I/O operations: ub_write() (unbuffered write), flush(), read_post(), read_cookies()
  • Header management: header_handler(), send_headers(), send_header()
  • Environment: getenv(), get_stat(), register_server_variables()
  • Error handling: sapi_error(), log_message()

When PHP starts, sapi_startup() in main/SAPI.c registers the active SAPI module globally, initializing thread-safe globals and reentrancy support. Each request then calls sapi_activate() to prepare request-specific state.

Built-in SAPI Modules

CLI (Command Line Interface) (sapi/cli/php_cli.c)

  • Executes PHP scripts from the terminal
  • No HTTP headers or server context
  • Supports interactive mode and script execution
  • Minimal I/O overhead for direct output

FPM (FastCGI Process Manager) (sapi/fpm/fpm/fpm_main.c)

  • Production-grade process manager for web servers
  • Manages worker pools with configurable concurrency
  • Communicates via FastCGI protocol
  • Provides status monitoring and graceful reload

CGI/FastCGI (sapi/cgi/cgi_main.c)

  • Traditional CGI interface for web servers
  • Spawns a new process per request (CGI) or reuses processes (FastCGI)
  • Reads request data from environment variables and stdin
  • Outputs HTTP headers and response body to stdout

Apache 2 Handler (sapi/apache2handler/sapi_apache2.c)

  • Direct Apache module integration (mod_php)
  • Runs PHP in Apache's process space
  • Accesses Apache request structures directly
  • Supports Apache-specific features like authentication hooks

Embed (sapi/embed/php_embed.c)

  • Lightweight SAPI for embedding PHP in C/C++ applications
  • No HTTP or server context
  • Minimal initialization overhead
  • Useful for scripting engines and application extensions

Request Lifecycle

Loading diagram...

Key Integration Points

Each SAPI module must implement request/response handling. For example, the CLI SAPI's sapi_cli_ub_write() writes directly to stdout, while Apache's php_apache_sapi_ub_write() writes through Apache's output filters. Similarly, read_post() varies: CGI reads from stdin, Apache accesses request body through Apache structures, and CLI has no POST data.

The register_server_variables() callback populates $_SERVER with environment-specific data. CLI provides minimal variables, while web SAPIs populate HTTP headers, request method, and URI information.

SAPI modules can also define custom INI settings via ini_entries and ini_defaults(), allowing per-SAPI configuration tuning.

Memory Management & Garbage Collection

Relevant Files
  • Zend/zend_alloc.c
  • Zend/zend_alloc.h
  • Zend/zend_alloc_sizes.h
  • Zend/zend_gc.c
  • Zend/zend_gc.h

PHP's memory management system combines a high-performance allocator with cycle-detecting garbage collection. The design prioritizes CPU cache efficiency and is inspired by modern allocators like jemalloc and tcmalloc.

Memory Allocation Strategy

The allocator divides all allocations into three categories based on size:

  • Huge (> 2MB): Allocated via mmap(), aligned to 2MB boundaries
  • Large (4KB to 2MB): Allocated as pages within chunks, page-aligned
  • Small (< 3KB): Rounded to one of 30 predefined sizes (8, 16, 24, 32, ..., 3072 bytes)

Chunks are 2MB blocks allocated from the OS. Each chunk reserves a header page containing metadata: a bitset of free pages, run availability maps, and per-page usage information. Small allocations are packed into RUNs (contiguous pages), with free blocks tracked via linked lists.

Allocation API

The public API provides familiar functions with optional size hints for optimization:

void *emalloc(size_t size);           /* Allocate */
void efree(void *ptr);                /* Free */
void *erealloc(void *ptr, size_t size); /* Reallocate */
void *ecalloc(size_t nmemb, size_t size); /* Allocate + zero */

When the compiler supports __builtin_constant_p(), the preprocessor substitutes calls with specialized routines like _emalloc_8(), _emalloc_16(), etc., for known sizes, eliminating size classification overhead.

Garbage Collection: Cycle Detection

PHP uses a concurrent cycle-detecting garbage collector based on the Bacon algorithm. Objects and arrays with reference cycles can leak if not collected. The GC maintains a root buffer of possible cycle roots.

Color-Based Algorithm

The collector uses four colors to track object state:

  • BLACK: In use or already freed (safe)
  • PURPLE: Possible cycle root (added to buffer)
  • GREY: Possible cycle member (during marking)
  • WHITE: Confirmed garbage (during collection)

Collection Phases

Loading diagram...
  1. Mark Roots: Traverse purple nodes depth-first, marking reachable nodes grey
  2. Scan Roots: Check each grey node; if refcount > 0, recursively mark descendants black; otherwise mark white
  3. Collect: Traverse white nodes and free them; adjust refcounts

Triggering Collection

The GC runs automatically when the root buffer reaches a threshold (default 10,000 roots). It can be controlled via:

bool gc_enable(bool enable);      /* Enable/disable automatic collection */
int gc_collect_cycles(void);      /* Manually trigger collection */

Reference Counting Integration

Every collectable object (arrays, objects, references) has a refcount. When refcount reaches zero, the object is immediately freed. The GC only handles cycles—situations where objects reference each other but have no external references.

Performance Considerations

  • Chunk-based allocation reduces fragmentation and improves cache locality
  • Predefined size bins eliminate expensive size classification
  • Lazy cycle detection defers expensive graph traversal until buffer fills
  • Concurrent marking prevents long GC pauses by interleaving with application code

Core Extensions

Relevant Files
  • ext/standard/basic_functions.c
  • ext/date/php_date.c
  • ext/json/json.c
  • ext/pcre/php_pcre.c
  • ext/mbstring/mbstring.c
  • ext/spl/php_spl.c

PHP's core extensions provide essential functionality for common programming tasks. These extensions are either built-in or compiled by default, offering critical APIs for string handling, date/time operations, data serialization, pattern matching, and object-oriented utilities.

Standard Extension (Basic Functions)

The Standard extension is PHP's largest and most fundamental extension, containing hundreds of functions across multiple domains. It includes:

  • String functions: strlen(), substr(), strpos(), str_replace(), explode(), implode()
  • Array functions: array_map(), array_filter(), array_merge(), sort(), usort()
  • File I/O: fopen(), fread(), fwrite(), file_get_contents(), file_put_contents()
  • Variable handling: var_dump(), print_r(), serialize(), unserialize()
  • Math functions: abs(), round(), ceil(), floor(), pow(), sqrt()
  • Type checking: is_array(), is_string(), is_numeric(), gettype()

The extension is organized into submodules (var, file, array, assert, crypt, dir) initialized during module startup via BASIC_MINIT_SUBMODULE() macros.

Date Extension

The Date extension provides comprehensive date/time handling using the embedded timelib library. Key features:

  • DateTime class: Object-oriented interface for date/time manipulation with timezone support
  • DateTimeImmutable: Immutable variant for functional programming patterns
  • DateTimeZone: Timezone database and conversion utilities
  • DateInterval: Duration representation for date arithmetic
  • DatePeriod: Iterator for recurring date ranges

The extension maintains a timezone cache (DATEG(tzcache)) and error tracking (DATEG(last_errors)) for efficient repeated operations.

JSON Extension

JSON provides serialization and deserialization of PHP values to/from JSON format:

  • json_encode(): Converts PHP values to JSON strings with options for formatting, escaping, and depth control
  • json_decode(): Parses JSON strings into PHP arrays or objects
  • json_validate(): Validates JSON syntax without full parsing
  • JsonSerializable interface: Allows custom objects to define their JSON representation

The extension tracks encoder depth and error codes in module globals to prevent stack overflow and provide detailed error reporting.

PCRE Extension

PCRE (Perl Compatible Regular Expressions) provides pattern matching using the PCRE2 library:

  • preg_match(): Single pattern matching with capture groups
  • preg_match_all(): Global pattern matching returning all matches
  • preg_replace(): Pattern-based string replacement
  • preg_split(): Split strings by regex patterns
  • preg_grep(): Filter arrays by regex patterns

The extension maintains a pattern cache (pcre_cache) for performance and supports JIT compilation when available.

Mbstring Extension

Mbstring handles multibyte character encodings (UTF-8, Shift-JIS, EUC-JP, etc.):

  • mb_strlen(), mb_substr(): Multibyte-aware string operations
  • mb_convert_encoding(): Convert between character encodings
  • mb_detect_encoding(): Detect string encoding automatically
  • mb_regex_match(): Regex matching with multibyte support
  • mb_send_mail(): Email with proper encoding headers

Uses the embedded libmbfl library for encoding detection and conversion.

SPL Extension

The Standard PHP Library provides object-oriented utilities and design patterns:

  • Iterators: Iterator, IteratorAggregate, RecursiveIterator interfaces
  • Data structures: SplStack, SplQueue, SplHeap, SplFixedArray
  • File handling: DirectoryIterator, RecursiveDirectoryIterator, SplFileInfo
  • Exceptions: LogicException, RuntimeException, OutOfBoundsException
  • Utility functions: class_parents(), class_implements(), spl_autoload_register()

SPL depends on PCRE and Standard extensions, with optional JSON support for serialization.

Loading diagram...

Extension System & Module Loading

Relevant Files
  • Zend/zend_extensions.c
  • Zend/zend_extensions.h
  • Zend/zend_API.c
  • Zend/zend_modules.h
  • ext/standard/dl.c
  • ext/ext_skel.php
  • main/main.c

PHP supports two distinct extension types: Zend Extensions (low-level engine hooks) and PHP Modules (user-facing functionality). Both are loaded dynamically or compiled statically, with a well-defined lifecycle managed by the Zend Engine.

Module Types & Lifecycle

PHP Modules are registered via zend_module_entry structures containing:

  • Module metadata (name, version, API version)
  • Lifecycle callbacks: module_startup_func, module_shutdown_func, request_startup_func, request_shutdown_func
  • Function table and INI entries
  • Global state management (constructor/destructor for thread-safe globals)

Zend Extensions are lower-level hooks that intercept opcode compilation and execution:

  • Startup/shutdown handlers
  • Op-array constructors/destructors for bytecode manipulation
  • Statement and function call handlers for profiling/debugging
  • Persistence callbacks for opcode caching

Module Registration & Loading

Modules are registered via zend_register_module_ex(), which:

  1. Allocates a unique module number
  2. Registers all exported functions in the function table
  3. Stores the module in the global module_registry hash table
  4. Initializes module globals (thread-safe or process-wide)

Static modules are compiled into PHP and registered during php_module_startup(). Dynamic modules are loaded via php_load_extension() (from dl() or php.ini), which:

  1. Uses DL_LOAD() to dynamically load the shared object
  2. Fetches the get_module() symbol to retrieve the module entry
  3. Validates the module isn't already loaded
  4. Registers it with type MODULE_TEMPORARY

Zend Extension Loading

Zend Extensions are loaded via zend_load_extension():

  1. Dynamically loads the shared object
  2. Fetches extension_version_info and zend_extension_entry symbols
  3. Validates API version compatibility
  4. Registers via zend_register_extension(), storing in the zend_extensions linked list

Extensions can provide custom API version checks via api_no_check() callback for forward compatibility.

Startup Sequence

The engine initializes extensions in this order:

  1. Zend startup mechanism initializes the extensions list
  2. Internal modules (compiled-in) are registered
  3. Shared modules from php.ini are loaded and registered
  4. Module startup callbacks execute (module-level initialization)
  5. Zend extension startup callbacks execute
  6. Request startup callbacks execute per-request

Each phase can fail, preventing subsequent phases from running. Module dependencies are validated before startup.

Extension Handles & Metadata

Extensions can reserve op-array handles via zend_get_op_array_extension_handle() to attach custom metadata to compiled functions. This enables:

  • Opcode caching (OPcache)
  • Profiling and debugging tools
  • Custom compilation passes

The engine maintains flags tracking which extensions provide op-array constructors, destructors, or persistence callbacks, optimizing the hot path.

Creating Extensions

The ext_skel.php script generates extension scaffolding with:

  • Standard module entry structure
  • Lifecycle function stubs
  • Function registration macros
  • Build configuration (config.m4)

Extensions must export a get_module() function returning the zend_module_entry, and optionally a zend_extension_entry for Zend-level hooks.

Streams & I/O Layer

Relevant Files
  • main/streams/streams.c
  • main/streams/plain_wrapper.c
  • main/php_streams.h
  • main/fopen_wrappers.c
  • main/streams/filter.c
  • main/streams/php_stream_context.h

PHP's Streams & I/O Layer provides a unified abstraction for reading and writing data from diverse sources—files, sockets, memory buffers, and custom wrappers. This layer is fundamental to PHP's file operations, network communication, and extensibility.

Core Architecture

The streams system is built on three main components:

  1. Stream Operations (php_stream_ops): Define how data flows through a stream. Each stream type (file, socket, memory) implements read, write, close, flush, seek, and stat operations.

  2. Stream Wrappers (php_stream_wrapper): Enable protocol-specific handling. Wrappers like file://, http://, and ftp:// are registered globally and handle opening, closing, and metadata operations for their respective protocols.

  3. Stream Filters: Transform data in-flight using a bucket-brigade pattern. Filters can be chained for reading and writing, enabling compression, encryption, and encoding without modifying the underlying stream.

Stream Structure

struct _php_stream {
    const php_stream_ops *ops;      /* operation handlers */
    void *abstract;                 /* wrapper-specific context */
    php_stream_filter_chain readfilters, writefilters;
    php_stream_wrapper *wrapper;    /* which wrapper opened this */
    uint32_t flags;                 /* PHP_STREAM_FLAG_* */
    unsigned char *readbuf;         /* internal read buffer */
    size_t chunk_size;              /* buffer fill size */
    zend_resource *res;             /* resource handle */
};

Opening Streams

Streams are opened via _php_stream_open_wrapper_ex(), which:

  1. Locates the appropriate wrapper based on the protocol (e.g., http:// → HTTP wrapper)
  2. Calls the wrapper's stream_opener callback
  3. Wraps the result in a php_stream structure with buffering and filter chains
  4. Registers the stream as a Zend resource for automatic cleanup

Buffering & Reading

Streams use internal buffering to optimize I/O. The _php_stream_fill_read_buffer() function fills the read buffer in chunks (default 8192 bytes). Flags like PHP_STREAM_FLAG_NO_BUFFER and PHP_STREAM_FLAG_AVOID_BLOCKING control buffering behavior for different use cases.

Filter Chain (Bucket-Brigade Pattern)

Filters process data through a chain of buckets:

Input Data → Bucket → Filter 1 → Filter 2 → Output

Each filter implements php_stream_filter_ops with a filter() callback that processes bucket brigades and returns PSFS_PASS_ON (continue), PSFS_FEED_ME (need more data), or PSFS_ERR_FATAL (error).

Stream Context

Stream contexts provide metadata and options to wrappers:

struct _php_stream_context {
    php_stream_notifier *notifier;  /* progress callbacks */
    zval options;                   /* wrapper-specific options */
    zend_resource *res;             /* resource handle */
};

Contexts enable features like HTTP headers, SSL options, and progress notifications without changing the stream API.

Persistent Connections

Persistent streams are cached in EG(persistent_list) and reused across requests. The STREAM_OPEN_PERSISTENT flag enables this. Persistent streams must be explicitly freed with PHP_STREAM_FREE_PERSISTENT to avoid resource leaks.

Common Operations

  • Read/Write: php_stream_read(), php_stream_write() handle buffering transparently
  • Seek: php_stream_seek() repositions the stream pointer
  • Stat: php_stream_stat() retrieves file metadata
  • Cast: php_stream_cast() converts streams to FILE* or file descriptors for legacy APIs
  • Copy: php_stream_copy_to_stream_ex() efficiently transfers data between streams

Build System & Configuration

Relevant Files
  • configure.ac
  • buildconf
  • build/php.m4
  • build/Makefile.global
  • Zend/Makefile.frag

PHP uses a traditional Autoconf-based build system for Unix-like platforms, complemented by separate Windows build infrastructure. The system handles platform detection, feature configuration, extension management, and compilation orchestration.

Build Pipeline Overview

Loading diagram...

Key Build Components

buildconf is the entry point for Git checkouts. It wraps Autoconf and Autoheader to generate the configure script and main/php_config.h.in. It validates Autoconf version requirements (minimum 2.68) and cleans cached build artifacts before regeneration.

configure.ac is the master configuration template. It defines:

  • PHP version parsing and generation
  • System capability detection (headers, functions, libraries)
  • SAPI module selection (CLI, FPM, Apache, etc.)
  • Extension configuration
  • Compiler flags and optimization settings
  • Thread safety (ZTS) and debug options

build/php.m4 contains custom Autoconf macros for PHP-specific logic, including path expansion, variable substitution, library detection, and build system helpers.

Configuration Options

Common configure flags include:

./configure --prefix=/usr/local \
  --enable-fpm \
  --with-config-file-path=/etc/php \
  --enable-debug \
  --enable-zts

Key option categories:

  • SAPI Selection: --enable-fpm, --enable-cli, --with-apache2handler
  • Extensions: --with-curl, --enable-mbstring, --with-pdo-mysql
  • Features: --enable-zts (thread safety), --enable-debug, --enable-gcov
  • Paths: --with-config-file-path, --with-libdir, --with-layout

Build System Architecture

The Makefile system uses:

  • Makefile.global: Core build rules for compilation, linking, and installation
  • Makefile.frag: Fragment files in subdirectories (Zend, main, extensions) define local rules
  • Libtool: Manages shared library creation across platforms
  • PHP_ADD_SOURCES: Macro to register source files with compilation flags

Generated files include parser/lexer code from Bison and RE2C:

  • zend_language_parser.c from zend_language_parser.y
  • zend_language_scanner.c from zend_language_scanner.l
  • zend_vm_execute.h from zend_vm_def.h (generated by PHP script)

Extension Integration

Extensions are configured via build/config-stubs, which processes config.m4 files in each extension directory. Extensions can be built as:

  • Static: Linked into the PHP binary
  • Shared: Compiled as .so files loaded at runtime via extension_dir

The EXTENSION_DIR is automatically determined based on debug/ZTS flags and module API version.

Platform-Specific Handling

The build system detects and adapts to:

  • CPU Architecture: Fiber assembly files for x86_64, ARM, PowerPC, RISC-V, etc.
  • OS-Specific Flags: Solaris, FreeBSD, macOS, Haiku, AIX
  • Compiler Features: Visibility attributes, computed goto, sanitizers
  • Threading: POSIX threads configuration for ZTS builds

Compilation Flags

Standard flags are set based on configuration:

  • Optimization: -O2 by default; -O0 with --enable-debug
  • Visibility: -fvisibility=hidden to reduce binary size
  • Sanitizers: Memory, address, and undefined behavior detection
  • Profiling: GCOV support for code coverage analysis

Testing & Quality Assurance

Relevant Files
  • run-tests.php - Main test runner orchestrating all test execution
  • tests/run-test/ - Test framework self-tests
  • sapi/fuzzer/README.md - Fuzzing infrastructure documentation
  • CONTRIBUTING.md - Testing requirements for contributions

Test Framework Overview

PHP uses the PHPT (PHP Test) format for its comprehensive test suite. Tests are organized across multiple directories: Zend/tests/ for engine tests, tests/ for core features, ext/*/tests/ for extension tests, and sapi/*/tests/ for SAPI-specific tests. The test runner (run-tests.php) is a sophisticated PHP script that discovers, executes, and reports on thousands of tests.

PHPT Test Format

PHPT files are structured text files with multiple sections delimited by --SECTION-- markers:

--TEST--
Brief description of what is being tested

--FILE--
<?php
// PHP code to execute
?>

--EXPECT--
Expected output

--SKIPIF--
<?php if (condition) die('skip reason'); ?>

Additional sections include --EXPECTF-- (with format specifiers), --EXPECTREGEX--, --INI--, --ENV--, --EXTENSIONS--, --SKIPIF--, --XFAIL-- (expected failures), and --XLEAK-- (expected memory leaks).

Running Tests

Execute tests with php run-tests.php from the repository root. Key options include:

  • -j<N> - Run tests in parallel with N workers
  • -m or -M <tool> - Enable Valgrind memory leak detection
  • -g FAIL,BORK,LEAK - Filter output by result type
  • --set-timeout <seconds> - Set per-test timeout (default: 60s)
  • -d foo=bar - Pass INI settings to test PHP
  • --show-slow <ms> - Display slow tests

Test Results

Tests produce result codes: PASS, FAIL, XFAIL (expected failure), SKIP, BORK (broken test), LEAK (memory leak), XLEAK (expected leak), and WARN (warning). The runner generates a summary report and can save detailed results to files.

Parallel Testing

The -j flag enables parallel test execution using worker processes communicating via TCP sockets. Tests can declare conflicts via --CONFLICTS-- sections or CONFLICTS files to prevent concurrent execution of incompatible tests. This significantly reduces total test time on multi-core systems.

Memory Leak Detection

Valgrind integration (-m flag) wraps test execution with Valgrind's memcheck tool. Tests marked with --XLEAK-- are expected to leak and won't fail. The runner analyzes Valgrind logs to detect and report memory leaks, essential for maintaining PHP's memory safety.

Fuzzing Infrastructure

The fuzzer SAPI (sapi/fuzzer/) provides coverage-guided fuzzing for critical components. Build with --enable-fuzzer and sanitizers (ASAN, UBSAN, MSAN). Multiple fuzzers target the parser, unserialize, JSON, EXIF, and JIT components. Seed corpora in sapi/fuzzer/corpus/ accelerate fuzzing discovery.

Quality Assurance Practices

Contributors must run make test before submitting changes. The CONTRIBUTING.md file requires testing with --enable-debug and --enable-zts flags to catch memory errors and thread-safety issues. CI pipelines run comprehensive test suites with multiple configurations, JIT modes, and sanitizers to ensure code quality across platforms.