Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[Makefile]
indent_style = tab

[*.{zig,c,cpp,h,php,json,yml,yaml,md}]
indent_style = space
indent_size = 4

[*.{yml,yaml}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
21 changes: 21 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
* text=auto

*.zig text eol=lf
*.c text eol=lf
*.cpp text eol=lf
*.h text eol=lf
*.php text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.zon text eol=lf
.gitignore text eol=lf
.gitattributes text eol=lf
.editorconfig text eol=lf
Makefile text eol=lf
audit text eol=lf
php text eol=lf

*.bin binary
*.gz binary
*.phar binary
25 changes: 21 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
zig-out/
zig-cache/
.zig-cache/
test_compress
/*.md
!README.md
.handoff/
docs/book/

# Local tooling
.handoff/
.claude/
.idea/
.vscode/
*.code-workspace

# Local state
.env
.env.*
!.env.example
*.log
*.tmp
*.swp
.DS_Store
Thumbs.db

# Generated dependencies and test artifacts
**/vendor/
**/.cache/
test_compress
23 changes: 6 additions & 17 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,60 +1,49 @@
PKG_CONFIG_PATH := /opt/homebrew/opt/mysql-client/lib/pkgconfig:/opt/homebrew/opt/libpq/lib/pkgconfig:/opt/homebrew/opt/openssl@3/lib/pkgconfig:/opt/homebrew/opt/curl/lib/pkgconfig:/opt/homebrew/opt/icu4c@77/lib/pkgconfig:/opt/homebrew/opt/icu4c/lib/pkgconfig:/opt/homebrew/opt/gmp/lib/pkgconfig:/opt/homebrew/opt/gd/lib/pkgconfig:/opt/homebrew/opt/libsodium/lib/pkgconfig:/opt/homebrew/opt/openldap/lib/pkgconfig:$(PKG_CONFIG_PATH)
export PKG_CONFIG_PATH

.PHONY: build
build: ## Build zphp (Debug; ~30x slower than release due to Zig's debug allocator stack-trace capture). use `make release` for benchmarking
.PHONY: build release test compat pdo examples bench bench-macro laravel symfony all-tests check docs clean help
build: ## Build the debug binary
zig build

.PHONY: release
release: ## Build zphp in ReleaseFast (no debug allocator overhead). prefer for any perf-sensitive run; for testing zphp's actual PHP-execution speed
release: ## Build the optimized binary
zig build -Doptimize=ReleaseFast

.PHONY: test
test: ## Run zig unit tests
test: ## Run Zig unit tests
zig build test

.PHONY: compat
compat: build ## Run PHP compatibility tests (requires PHP 8.4)
./tests/run

.PHONY: pdo
pdo: build ## Run PDO driver tests
./tests/pdo_test

.PHONY: examples
examples: build ## Run example project tests (requires PHP 8.4)
./tests/examples_test

.PHONY: bench
bench: ## Run runtime benchmarks (ReleaseFast)
zig build -Doptimize=ReleaseFast
./benchmarks/runtime/run

.PHONY: bench-macro
bench-macro: ## Track real-app perf vs php (WordPress + Laravel harnesses, ReleaseFast)
zig build -Doptimize=ReleaseFast
./benchmarks/macro/run

.PHONY: laravel
laravel: build ## Run Laravel compatibility tests (requires PHP 8.4 + composer)
./tests/laravel/run

.PHONY: symfony
symfony: build ## Run Symfony component and serve compatibility tests (requires PHP 8.4 + composer)
./tests/symfony/run
./tests/symfony/serve_run

.PHONY: all-tests
all-tests: test compat examples laravel ## Run all tests

.PHONY: docs
check: test compat examples ## Run the standard local verification suite

docs: ## Serve docs locally with live reload
mdbook serve docs

.PHONY: clean
clean: ## Clean build artifacts
rm -rf zig-out .zig-cache

.PHONY: help
help: ## Show help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(firstword $(MAKEFILE_LIST)) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-20s\033[0m %s\n", $$1, $$2}'
67 changes: 47 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,59 @@
# zphp

zphp is a PHP runtime written in Zig with PHP 8.x compatibility. It includes a built-in HTTP server with WebSocket support, TLS, and HTTP/2, database drivers for SQLite, MySQL, and PostgreSQL, and cURL bindings for HTTP client operations.
**A high-performance PHP runtime built in Zig.**

zphp is an experimental PHP 8.x-compatible runtime focused on **speed, low memory usage, and modern deployment**. It combines a custom runtime with a built-in HTTP server, WebSocket support, TLS, HTTP/2, database drivers, cURL bindings, package management, testing, formatting, and standalone compilation.

Built with **Zig**, zphp is designed to give PHP workloads lower-level control, reduced overhead, and a more performance-oriented runtime architecture.

```sh
zphp run app.php # run a script
zphp serve app.php --port 8080 # start an HTTP server
zphp build --compile app.php # compile to a standalone executable
zphp test # run tests
zphp fmt src/*.php # format code
zphp install # install packages from composer.json
zphp run app.php
zphp serve app.php --port 8080
zphp build --compile app.php
zphp test
zphp fmt src/*.php
zphp install
```

## Quick comparison

| | PHP | zphp |
|---|---|---|
| Run a script | `php script.php` | `zphp run script.php` |
| HTTP server | php-fpm + nginx | `zphp serve app.php` |
| Install deps | `composer install` | `zphp install` |
| Add a package | `composer require pkg` | `zphp add pkg` |
| Run tests | `phpunit` | `zphp test` |
| Format code | `php-cs-fixer fix` | `zphp fmt` |
| Standalone binary | - | `zphp build --compile app.php` |
## Features

* PHP 8.x compatibility
* Runtime written in Zig
* Built-in HTTP server
* HTTP/2, TLS, and WebSockets
* SQLite, MySQL, and PostgreSQL
* cURL bindings
* Composer package support
* Standalone executable compilation
* Performance and low-memory focused architecture

## Comparison

| Task | PHP | zphp |
| ----------------- | ------------------ | ---------------------- |
| Run script | `php app.php` | `zphp run app.php` |
| HTTP server | PHP-FPM + nginx | `zphp serve app.php` |
| Dependencies | `composer install` | `zphp install` |
| Tests | PHPUnit | `zphp test` |
| Formatting | PHP-CS-Fixer | `zphp fmt` |
| Standalone binary | External tooling | `zphp build --compile` |

## Installation

Download prebuilt binaries from [GitHub Releases](https://github.com/nvms/zphp/releases). See the [documentation](https://nvms.github.io/zphp/) for building from source and detailed guides.
Download builds from [GitHub Releases](https://github.com/nvms/zphp/releases).

See the [documentation](https://nvms.github.io/zphp/) for build instructions and usage guides.

## Project Status

zphp was originally developed and maintained heavily through AI-assisted development.

The project is now being actively reviewed and hardened with a stronger focus on **security, correctness, memory safety, testing, performance validation, and production readiness**.

AI may still be used as a development tool, but critical runtime code is expected to be reviewed, tested, and independently verified.

zphp is still experimental and should be thoroughly tested before production use.

---

This project is an experiment in AI-maintained open source - autonomously built, tested, and refined by AI with human oversight.
**PHP on the surface. Zig at the core. Built for speed.**
4 changes: 0 additions & 4 deletions benchmarks/runtime/array_ops.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,23 @@
// array operations - tests array creation, access, manipulation
$n = 50000;

// build array
$arr = [];
for ($i = 0; $i < $n; $i++) {
$arr[] = $n - $i;
}

// sum
$sum = 0;
for ($i = 0; $i < $n; $i++) {
$sum += $arr[$i];
}

// filter to new array
$filtered = [];
for ($i = 0; $i < $n; $i++) {
if ($arr[$i] % 3 === 0) {
$filtered[] = $arr[$i];
}
}

// map
$mapped = [];
for ($i = 0; $i < count($filtered); $i++) {
$mapped[] = $filtered[$i] * 2;
Expand Down
3 changes: 0 additions & 3 deletions benchmarks/runtime/closures.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// closure operations - tests closure creation, invocation, captures
$n = 50000;

// create and call closures
$adders = [];
for ($i = 0; $i < 100; $i++) {
$adders[] = function ($x) use ($i) { return $x + $i; };
Expand All @@ -13,12 +12,10 @@
$sum += $adders[$i % 100]($i);
}

// higher-order: array_map with closure
$data = range(1, 10000);
$squared = array_map(function ($x) { return $x * $x; }, $data);
$total = array_sum($squared);

// nested closures
function compose(callable $f, callable $g): callable {
return function ($x) use ($f, $g) { return $f($g($x)); };
}
Expand Down
3 changes: 0 additions & 3 deletions benchmarks/runtime/loops.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// tight loop arithmetic - tests raw bytecode execution speed
$n = 5000000;

// integer arithmetic loop
$sum = 0;
for ($i = 0; $i < $n; $i++) {
$sum += $i;
}
echo "$sum\n";

// nested loops with conditionals
$count = 0;
for ($i = 0; $i < 2000; $i++) {
for ($j = 0; $j < 2000; $j++) {
Expand All @@ -20,7 +18,6 @@
}
echo "$count\n";

// while loop with mixed ops
$x = 1.0;
$i = 0;
while ($i < 1000000) {
Expand Down
3 changes: 0 additions & 3 deletions benchmarks/runtime/objects.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,15 @@ public function add(Point $other): Point {
$n = 50000;
$points = [];

// create objects
for ($i = 0; $i < $n; $i++) {
$points[] = new Point($i * 0.1, $i * 0.2);
}

// method calls
$totalDist = 0.0;
for ($i = 1; $i < $n; $i++) {
$totalDist += $points[$i]->distanceTo($points[$i - 1]);
}

// chained operations
$sum = new Point(0, 0);
for ($i = 0; $i < 1000; $i++) {
$sum = $sum->add($points[$i]);
Expand Down
3 changes: 0 additions & 3 deletions benchmarks/runtime/string_ops.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@
$s .= "item" . $i . ",";
}

// count occurrences
$count = substr_count($s, "item1");

// replace
$replaced = str_replace("item", "elem", $s);

// split and rejoin
$parts = explode(",", $s);
$joined = implode(";", $parts);

Expand Down
8 changes: 4 additions & 4 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,10 @@ fn addXxhashShim(b: *std.Build, mod: *std.Build.Module) void {
fn addIcuShim(b: *std.Build, mod: *std.Build.Module) void {
var flags = std.ArrayList([]const u8){};
defer flags.deinit(b.allocator);
flags.append(b.allocator, "-std=c11") catch {};
flags.append(b.allocator, "-std=c11") catch return;
if (pkgConfigCflagsIncludes(b, "icu-i18n")) |inc| {
const flag = std.fmt.allocPrint(b.allocator, "-I{s}", .{inc}) catch return;
flags.append(b.allocator, flag) catch {};
flags.append(b.allocator, flag) catch return;
}
mod.addCSourceFile(.{
.file = b.path("src/stdlib/icu_shim.c"),
Expand All @@ -188,10 +188,10 @@ fn addIcuShim(b: *std.Build, mod: *std.Build.Module) void {
// exposed in the C++ API. link libc++ once for the whole module
var cpp_flags = std.ArrayList([]const u8){};
defer cpp_flags.deinit(b.allocator);
cpp_flags.append(b.allocator, "-std=c++17") catch {};
cpp_flags.append(b.allocator, "-std=c++17") catch return;
if (pkgConfigCflagsIncludes(b, "icu-i18n")) |inc| {
const flag = std.fmt.allocPrint(b.allocator, "-I{s}", .{inc}) catch return;
cpp_flags.append(b.allocator, flag) catch {};
cpp_flags.append(b.allocator, flag) catch return;
}
mod.addCSourceFile(.{
.file = b.path("src/stdlib/icu_msg_shim.cpp"),
Expand Down
2 changes: 2 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
.paths = .{
"build.zig",
"build.zig.zon",
"LICENSE",
"README.md",
"src",
},
}
Loading
Loading