# Phpz - Build PHP Extensions with Zig A Zig framework for building PHP extensions with PHP C API bindings. > **ℹ️ Note**: phpz already covers most PHP extension use cases, but the API is still evolving. Pin to a specific commit if you need stability. ## Requirements - zig: 0.17.0-dev.1398+cb5635714 - php: 8.2-8.5 (NTS/ZTS) on Linux (x86_64/aarch64), macOS (x86_64/aarch64), Windows MSVC (x64) ## Features - **Modules** — extension metadata, lifecycle hooks, phpinfo, INI, globals, observers, and exports. - **Stub integration** — arginfo, function entries, constants, attributes, class metadata, and arginfo checks. - **Functions and methods** — typed `Ctx` arguments, returns, nullable values, raw zvals, and PHP argument errors. - **Classes and OOP** — bind Zig `extern struct` lifecycles to PHP objects, with class entries, methods, inheritance, interfaces, enums, exceptions, and handlers. - **Runtime state** — typed module globals, PHP/Zend globals, and superglobal access for NTS/ZTS builds. - **Zval and Zend APIs** — zval conversions, ownership helpers, arrays, strings, objects, callables, functions, and resources. - **INI, phpinfo, and errors** — typed INI values, phpinfo helpers, PHP errors, exceptions, and bailout-safe cleanup. - **Observers** — function-call, error, and exception hooks for profiling and monitoring. - **Memory** — PHP-backed Zig allocator with optional debug leak traces. - **Build integration** — PHP C translation, extension linking, and platform-specific setup. ## Quick Start Generate a PHP extension skeleton: ```bash # Generate a new PHP extension skeleton # PHP >= 8.3: curl -fsSL https://raw.githubusercontent.com/happystraw/phpz/dev/tools/phpz_skel.php \ | php -- --ext my_php_extension # PHP 8.2: curl -fsSLO https://raw.githubusercontent.com/happystraw/phpz/dev/tools/phpz_skel.php php phpz_skel.php --ext my_php_extension # Run tests (Optional) cd my_php_extension zig build run-tests ``` ## Manual Setup > For a complete minimal project, see [`examples/skeleton`](./examples/skeleton). ### 1. Add phpz ```bash zig fetch --save git+https://github.com/happystraw/phpz ``` ### 2. Create the extension files A phpz extension usually contains: - `build.zig.zon` — declares the `phpz` dependency - `build.zig` — initializes `Phpz` and calls `phpz.addExtension` - `.stub.php` — PHP API declarations - `_arginfo.h` — generated by PHP's `gen_stub.php` - `.h` — includes `phpz.h` and the generated arginfo header - `src/root.zig` — registers functions, classes, and the module ### 3. Generate arginfo ```bash php /path/to/php-src/build/gen_stub.php .stub.php ``` ### 4. Build ```bash zig build -Dphp-include-dir="$(php-config --include-dir)" ``` For the complete `build.zig`, `stub.php`, header, Zig module, and PHPT tests, use [`examples/skeleton`](./examples/skeleton) as the reference. ## Examples Check out the [`examples/`](./examples/) directory for complete working examples. ## How It Works ``` Build-time Compile-time Runtime ────────── ──────────── ─────── stub.php Zig source │ │ │ php-src/build/gen_stub.php │ ▼ │ arginfo.h │ [ext_functions] │ [register_class_*] │ [register_{name}_symbols] │ │ │ │ translate-c (zig build) │ ▼ ▼ PHP C bindings ─────────────► phpz comptime: (php_c module) ├─ phpz.function() → zif_* export ├─ Class.method() → zim_* export ├─ phpz.Class(T) → wrapper type └─ phpz.module() → get_module() export │ ├─── PHP loads .so │ ▼ module_startup [auto] ├─ register_{name}_symbols (constants) ├─ ini_entries └─ user hook └─ Class.register() (classes) [manual] ``` 1. **Build-time** — `gen_stub.php` generates `arginfo.h` from `.stub.php`, containing function metadata and `register_*` symbols. `translate-c` converts PHP C headers into Zig bindings. 2. **Compile-time** — `phpz.function()` exports `zif_*` wrappers, `phpz.Class()` creates wrapper types, `phpz.module()` exports `get_module()` for the dynamic loader. 3. **Runtime** — PHP loads `.so` → `get_module()`: | What | Registered by | When | How | | --- | --- | --- | --- | | Functions | `ext_functions` | Module init | Auto: set in module entry struct | | Constants | `register_{name}_symbols` | MINIT | Auto: called by `module_startup_func` | | Classes | `register_class_*` | MINIT | Manual: `Class.register()` in `module_startup_fn` | > Functions are resolved from the module entry when the extension loads; constants are registered during MINIT via the auto-generated `register_{name}_symbols`. Classes require an explicit `Class.register()` call in `module_startup_fn` — this gives you control over registration order and the opportunity to configure `ObjectHandlers` or parent classes.