A Zig framework for building PHP extensions with PHP C API bindings.
ℹ️ Note: This is a personal experimental project — functionality works well for its intended use cases, but expect API changes as it evolves. Pin to a specific commit if you need stability.
- zig: 0.16.0
- php: 8.2-8.5 tested on linux/macos
- Module Lifecycle —
module_startup,module_shutdown,request_startup,request_shutdown,info(phpinfo()) - Function Registration —
phpz.function()with type-safe parameter parsing (optional, nullable, typed specifiers) - Class & OOP —
phpz.Class(struct withinit/deinit),phpz.SimpleClass(interfaces, enums, exceptions), static/instance methods, properties, inheritance - Type-safe Zval — checked type conversions,
Zval.Array/Zval.Objectbuilders - Zend APIs —
zend.Array(HashTable),zend.String,zend.Object,zend.Function,zend.ClassEntry,zend.Property,zend.Callable - INI Settings —
php.inidirectives with on-update callbacks (string,long,double,bool) - Error Handling — PHP error triggers, argument errors, exception throwing
- PHP Memory Allocator —
heap.php_allocatorwrappingemallocas a ZigAllocator
💡 Tip: For complete working examples, see the
examples/directory.
Add phpz to your build.zig.zon
zig fetch --save git+https://github.com/happystraw/phpzFor PHP 8.0+, it's recommended to use stub files to generate arginfo headers:
Create a stub file (e.g., my_php_extension.stub.php):
<?php
/**
* @generate-class-entries
* @undocumentable
*/
function hello_world(): void {}
function my_function(string $name, int $age = 0): string {}Generate the arginfo header using PHP's gen_stub.php:
php /path/to/php-src/build/gen_stub.php my_php_extension.stub.phpThis will generate my_php_extension_arginfo.h containing all the necessary argument info definitions.
Create a C header file (e.g., my_php_extension.h):
phpz.h provides the core C API needed for building PHP extensions (includes
php.handZend/zend_API.h).Include it in your extension header to access all necessary PHP C APIs.
#include "phpz.h"
#include "my_php_extension_arginfo.h"
// ... other header filesConfigure your build.zig:
// Import the Phpz build system module
const Phpz = @import("phpz").Phpz;
// Fetch the phpz dependency declared in build.zig.zon
const phpz_dep = b.dependency("phpz", .{});
// Initialize Phpz: translates PHP C headers into Zig bindings
const phpz = Phpz.init(phpz_dep, .{
.target = target,
.optimize = optimize,
// C header for translate-c
.c_source_file = b.path("my_php_extension.h"),
// Directory containing PHP header files (main/, Zend/, TSRM/, ext/).
.php_include_dir = .{ .cwd_relative = "/usr/include/php" },
});
// Import the phpz module into your extension library
lib.root_module.addImport("phpz", phpz.mod);
// Apply OS-specific linker settings (macOS undefined symbols, Windows php8.lib)
phpz.apply(lib);Then zig build !
Check out the examples/ directory for complete working examples.
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
├─ register_{name}_symbols (constants)
├─ register_class_* (classes)
├─ ini_entries
└─ user hook
- Build-time —
gen_stub.phpgeneratesarginfo.hfrom.stub.php, containing function metadata andregister_*symbols.translate-cconverts PHP C headers into Zig bindings. - Compile-time —
phpz.function()exportszif_*wrappers,phpz.Class()creates wrapper types,phpz.module()exportsget_module()for the dynamic loader. - Runtime — PHP loads
.so→get_module()→module_startupcallsregister_{name}_symbols(constants) andregister_class_*(classes), then INI entries and user hook.