swoole / typephp
- суббота, 29 августа 2026 г. в 00:00:05
Compile PHP to Native Binaries
A native AOT compiler for PHP
Compile PHP source code into native machine code ahead of time — producing native executables, PHP extensions, and shared libraries — while keeping the PHP syntax you already know.
TypePHP is an Ahead-Of-Time (AOT) compiler that translates PHP source code into C++ and then into native machine code. Unlike a bytecode cache or a VM, it does not interpret opcodes at runtime: it generates optimized native binaries that run directly on the CPU.
It keeps familiar PHP syntax and adds compile-time type information, so the compiler can emit fast, statically-typed C++ for hot paths. Dynamic PHP values, internal functions, reflection, and object metadata continue to interoperate with the Zend runtime through PHPX; user functions are not executed as Zend opcodes after they have been compiled.
TypePHP is written entirely in PHP and is fully self-hosting: the tpc
compiler binary is built by compiling the compiler's own PHP source code with
TypePHP. The bootstrap chain is pure PHP — no C or C++ glue in the compiler
itself.
TypePHP is under active development. It intentionally supports a defined, testable subset of PHP rather than claiming drop-in compatibility with every dynamic PHP program. Read Compatibility model and the incompatible-feature list before adopting it for an existing application.
PHP source + .stub.php declarations + optional C/C++ sources
│
▼
parse, validate, and collect declarations
│
▼
lower function bodies and constants to C++17
│
▼
native compiler + reusable object/PCH caches
│
▼
executable | PHP extension | shared library | WASI component
The prepare phase builds the complete symbol model without allocating runtime cache IDs. Constants and declaration defaults retain their AST until the convert phase, where they are lowered after all project symbols are known. This two-phase design keeps multi-file and self-hosted builds deterministic.
tpc compiles the compiler's own
source into a native binary.bin executable, a loadable
PHP ext extension, or a reusable lib shared library from the same codebase.int, float, and bool map directly to C++
scalar types (int64_t, double, bool) for orders-of-magnitude speedups
on numeric code.bigInt (GMP), decimal (libmpdec), and
bigFloat (MPFR), with typed operators and method APIs.std::array, std::vector, std::map, and
std::ordered_map with compile-time element types; up to 10× faster than
PHP arrays and on par with C++ std::vector.$s->upper(), $arr->contains(), $big->mul(2)); statically-known calls
are resolved directly at compile time.any(), refval(), objval(),
expected(), unexpected(), plus toInt(), toString(), toArray() and
friends.#[Immutable] read-only contracts and #[ArrayDef]
array-shape metadata, checked at compile time with zero runtime cost.#[Getter], #[Setter], #[With],
#[Constructor], #[Printer], and #[Arrayable] generate type-safe methods
from property declarations.clone()-with, and (void) discard expressions.| TypePHP AOT | Opcode cache (OPcache) | JIT (PHP 8+) | |
|---|---|---|---|
| Compilation target | Native machine code | Bytecode | Machine code (trace) |
| Startup / warm-up | None (already compiled) | Per-process warm-up | JIT warm-up |
| Type-driven optimization | Compile-time, full-program | None | Limited, trace-based |
| Native executable output | Yes | No | No |
| Source code protection | Compiled to machine code | Bytecode (reversible) | Bytecode (reversible) |
| Deterministic performance | Yes | No | No |
Strengths over plain PHP:
libphp, and any configured
native libraries, which must be available in the deployment package.use native_types, std::
containers, and type declarations only where performance matters; the rest
stays ordinary PHP.php-configlibphp.so or libphp.dylib) for binary/shared-library
builds on Unix-like systems# Ubuntu/Debian
sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev
# RHEL/CentOS/Fedora
sudo dnf install gcc gcc-c++ cmake pkgconf-pkg-config gmp-devel mpfr-devel
# Arch Linux
sudo pacman -S base-devel cmake pkgconf gmp mpfrGMP powers
bigIntand MPFR powersbigFloat. Thedecimaltype is backed by libmpdec, which is bundled with PHPX — no separate install required.
Linux x64 is the primary development and full-test CI platform. The compiler also has Windows, macOS, ARM64, and WASI backends; availability of PHP embed, toolchain, and third-party libraries still determines which target can be built on a given host.
Native release assets are built with the latest PHP 8.5 ZTS release. TypePHP publishes Linux x64, Linux ARM64, macOS ARM64, and Windows x64 packages. Native NTS and 32-bit x86 packages are not provided. Linux and macOS archives contain the compiler and production Composer dependencies, while the Windows archive contains the complete matching PHP/PHPX runtime and SDK.
composer require --dev swoole/typephpThen compile your project:
vendor/bin/tpc.php project.ymlWhen working inside the TypePHP source repository, use the local entry point instead:
bin/tpc.php project.ymlgit clone https://github.com/swoole/typephp.git
cd typephp
composer install
php bin/tpc.php --helpPHPX_HOME may point to a separate PHPX checkout or installation. PHP_HOME
may point to the PHP embed prefix; it must contain bin/php-config, PHP headers,
and lib/libphp.so on Unix-like systems.
Binary and shared-library builds require PHP's embed SAPI. If libphp.so is
missing on Linux, tpc.php can interactively download the PHP source and build
it for you. A PHP extension build resolves Zend symbols from the host SAPI and
must not load a second libphp. See
Automatic libphp.so build.
Create hello.php:
<?php
function main(): void
{
echo "Hello World!\n";
var_dump(PHP_VERSION);
var_dump(php_uname());
}Compile and run it:
bin/tpc.php hello.php
./helloExample output (the exact PHP version and platform strings depend on the linked runtime):
Hello World!
string(5) "8.x.x"
string(16) "Linux ..."
Binary mode requires a global
main()function. It may be declared with no parameters, or asmain(int $argc, array $argv)to receive command-line arguments, and must returnvoid. Top-level executable statements are not allowed; executable code belongs in a function or method.
TypePHP supports three build modes, selected with -m / --mode:
| Mode | Flag | Output | Needs main() |
Typical use |
|---|---|---|---|---|
| Binary | -m bin (default) |
Executable | Yes | CLI tools, long-running services, standalone apps |
| Extension | -m ext |
PHP .so / .dll |
No | Loading compiled functions/classes into a PHP SAPI |
| Library | -m lib |
Shared library plus generated .stub.php |
No | Reusing a compiled TypePHP API from another project |
# Binary (default)
bin/tpc.php app.php -o myapp
# PHP extension
bin/tpc.php extension/ -m ext -o my_extension
# Shared library; also generates mylib.stub.php
bin/tpc.php lib/ -m lib -o mylibSee Compilation modes for details.
For multi-file projects, keep repeatable build settings in project.yml:
name: myapp
mode: bin
php-version: "8.5"
optimize: 2
job: 8
build-dir: build
cxx-std: c++17
sources:
- src
- cpp-src
- path: src/php85
if: PHP_VERSION_ID >= 80500
- path: src/windows
if: PHP_OS_FAMILY == "Windows"
ignore:
- src/experimental
include-paths:
- native/include
defines:
- FEATURE_FAST_PATH=1
link-paths:
- native/lib
link-libs:
- curl
# Zend extension requirements, not native linker libraries.
# `extension-dependencies` is the equivalent long name; do not use both.
ext-deps:
- pdo_mysql
- curlPaths are resolved relative to the YAML file. A source entry may be a file or
directory; conditional entries support PHP_VERSION, PHP_VERSION_ID, and
PHP_OS_FAMILY. CLI arguments override their YAML counterparts. Native linker
dependencies belong in link-libs; ext-deps writes ZEND_MOD_REQUIRED
entries so Zend can reject loading when a required PHP extension is missing.
The build directory contains generated C++, dependency objects, and the
precompiled-header cache. Reusing it makes incremental builds much faster;
use --force only when the reusable PHPX objects must be rebuilt.
See Compiler CLI for all project keys and command-line precedence rules.
TypePHP follows PHP syntax and runtime behavior where they are compatible with ahead-of-time compilation, but it also makes several deliberate restrictions:
main() signature;use native_types opts scalar declarations into fixed native storage, so a
value cannot later change to an incompatible type;.stub.php files declare C++ or imported-library APIs and must contain empty
bodies; #[Native] classes are not permitted in stub files;The compatibility boundary is part of the public contract and has both positive and negative tests. Consult Incompatible PHP features for the current, specific list instead of assuming that absence from this README means support.
TypePHP consumes its built-in code-generation attributes while lowering the class. The generated methods retain the declared property types and take part in the same conflict, inheritance, and final-method checks as explicitly declared methods.
| Attribute | Target | Generated API |
|---|---|---|
#[Getter] |
Instance property, including a promoted property | public function getName(): T |
#[Setter] |
Mutable instance property, including a promoted property | public function setName(T $name): void |
#[With] |
Mutable instance property, including a promoted property | public function withName(T $name): static; clones the object, updates the clone, and returns it |
#[Constructor] |
Declared instance property | Adds the property to a generated public __construct() |
#[Printer] |
Named class | public function __toString(): string |
#[Arrayable] |
Named class | public function toArray(): array |
<?php
#[Printer(fields: ['id', 'name'])]
#[Arrayable(fields: ['id', 'name'])]
final class User
{
#[Constructor, Getter, With]
public int $id;
#[Constructor, Getter, Setter]
public string $name = 'guest';
}
function main(): void
{
$user = new User(7);
$user->setName('Alice');
$copy = $user->withId(8);
echo $user->getId(); // 7
echo $copy->getId(); // 8
echo $user; // User(id=7, name=Alice)
echo $user->toArray()['name'];
}Without fields, #[Printer] and #[Arrayable] use the class's own public
instance properties. The positional form, such as #[Arrayable(['id'])], is
equivalent to #[Arrayable(fields: ['id'])].
#[Getter], #[Setter], and #[With] cannot target static properties or
properties with hooks. #[Setter] and #[With] additionally reject readonly
properties. #[Constructor] cannot be used when the class already declares
__construct(), and required constructor properties must precede properties
with defaults. A generated method name that conflicts with a declared or
inherited final method is a compile-time error.
<?php
use native_types;
function fib(int $n): int
{
if ($n == 1 || $n == 2) {
return 1;
}
return fib($n - 1) + fib($n - 2);
}
function main(int $argc, array $argv): void
{
$n = (int)$argv[1];
$begin = microtime(true);
echo fib($n) . "\n";
echo "Time: " . (microtime(true) - $begin) . "\n";
}bin/tpc.php fib.php -O3 -o fib
./fib 30With use native_types, int variables become C++ int64_t and arithmetic
compiles to plain CPU instructions instead of ZendVM calls.
<?php
declare(strict_types=1);
use native_types;
function main(): void
{
// 54-digit integer — automatically detected and stored as bigInt
$a = std::bigInt("123456789012345678901234567890123456789012345678901234");
$b = std::bigInt("987654321098765432109876543210987654321098765432109876");
echo $a->add($b)->toString() . "\n"; // exact, no overflow
// Exact decimal arithmetic — no binary floating-point error
$c = std::decimal("0.1")->add(std::decimal("0.2"));
echo $c->toString() . "\n"; // "0.3"
// 256-bit floating point
$pi = std::bigFloat("3.14159265358979323846264338327950288419716939937510");
echo $pi->mul(2)->toString() . "\n";
}See High-precision types and Native types.
<?php
use native_types;
function main(): void
{
$vector = std::vector(Type::Int);
$vector[] = 1;
$vector[] = 2;
$vector[] = 3;
$sum = 0;
foreach ($vector as $value) {
$sum += $value;
}
echo $sum . "\n"; // 6
echo $vector[1] . "\n"; // 2
// key-value map with fixed key/value types
$map = std::ordered_map(Type::String, Type::Int);
$map["a"] = 1;
$map["b"] = 2;
}See Std containers.
<?php
function main(): void
{
$s = "hello world";
echo $s->length() . "\n"; // strlen()
echo $s->upper() . "\n"; // strtoupper()
echo $s->substr(0, 5) . "\n"; // substr()
$arr = [1, 3, 5, 7, 9];
echo $arr->count() . "\n"; // count()
var_dump($arr->contains(3)); // in_array()
$big = std::bigInt("12345678901234567890");
echo $big->mul(2)->toString() . "\n";
}Method calls on primitives are resolved at compile time into direct C/C++ function calls — no vtable lookup, no reflection, no runtime dispatch. See Universal methods.
Write performance-critical kernels in C++ and call them from PHP:
// math.cpp
#include <phpx.h>
using namespace php;
Int php_fast_sum(Int a, Int b) {
return a + b;
}<?php
// math.stub.php — declares the C++ function signature
function fast_sum(int $a, int $b): int {}<?php
function main(): void
{
echo fast_sum(3, 4) . "\n"; // 7
}Add math.cpp, math.stub.php, and the calling PHP source to the same project
configuration. The php_ C++ symbol prefix is the TypePHP callable ABI; stub
functions provide type metadata only and must not contain an implementation.
See Mixed C++/PHP.
TypePHP runs the official bench.php and micro_bench.php language
benchmarks that ship with the PHP source tree, compiled with -O3:
| Benchmark | Interpreted PHP | TypePHP AOT (-O3) |
Speedup |
|---|---|---|---|
bench.php (total) |
5.034 s | 0.603 s | ~8× |
micro_bench.php (total) |
13.045 s | 2.021 s | ~6.5× |
Both benchmarks measure core PHP language performance — function calls, object
property access, array/hash access, string handling, control flow, and more.
The checked-in workloads are benchmark/bench.php and
benchmark/micro_bench.php. Additional focused
performance regressions live in the same benchmark/ directory.
These numbers are a project measurement snapshot, not a performance guarantee. PHP version, compiler, CPU, optimization flags, and enabled extensions can all change the result; compare on the same machine with the same workload before making deployment decisions.
A 10000×100000 element update loop, comparing PHP arrays against TypePHP's
std::array and native C++:
| Implementation | Time |
|---|---|
| PHP array (JIT) | 67.6 s |
std::array (TypePHP AOT) |
6.4 s |
C++ std::vector |
6.2 s |
std::array is roughly 10× faster than PHP arrays and performs
close to the hand-written C++ result in this workload. See the benchmark in
Std containers.
bin/tpc.php <file|dir|project.yml> [options] [-- program-args...]Common usage:
# Compile a single file
bin/tpc.php app.php
# Optimize and run, passing args to the program after `--`
bin/tpc.php app.php -O3 -r -- --flag value
# Compile a project defined in project.yml
bin/tpc.php project.yml -O2 -j 8
# Build a PHP extension
bin/tpc.php extension/ -m ext -o my_extension
# Only generate C++ (skip compile & link)
bin/tpc.php app.php --dry --build-dir /tmp/typephp-build
# Compile to WASI 0.2
bin/tpc.php --wasm app.php
# Compile for the browser (requires jco)
bin/tpc.php --wasm=browser app.phpKey options:
| Option | Description |
|---|---|
-O <0-3> |
Optimization level (default 0) |
-d, --debug |
Debug build with symbols and source tracking |
-o, --output <file> |
Output file name |
-m, --mode <bin|lib|ext> |
Build mode (default bin) |
-r, --run |
Run after a successful build |
-j, --job <num> |
Parallel compile jobs (default 4) |
-f, --force |
Rebuild reusable PHPX objects instead of using the cache |
--build-dir <dir> |
Directory for generated C++ and intermediates |
--dry |
Generate C++ only, skip compile and link |
--php-version <8.4|8.5> |
PHP syntax version to accept |
--cxx-std <ver> |
C++ standard (e.g. c++17, c++20) |
--march <arch> |
Target instruction set (e.g. native) |
--target-platform <triple> |
Cross-compilation target triple |
--lto |
Enable link-time optimization |
--sanitize <type> |
Enable a sanitizer (e.g. address) |
--profile |
Enable Linux gperftools profiling |
--format |
Format generated C++ with clang-format |
--no-literal-strings |
Disable the literal-string table optimization |
--no-progress, --no-color |
CI-friendly output controls |
-I, -D, -L, -l |
Repeatable native include, define, library path, and library options |
Run bin/tpc.php --help for the authoritative, up-to-date list. See
Compiler CLI for details, including Bash completion:
source <(./tpc --generate-completion=bash)libphp.so / libphp.dylib is missing: install/build the matching PHP embed SAPI, set
PHP_HOME, or let bin/tpc.php offer the interactive Linux installer.PHPX_HOME to a PHPX installation containing
include/ and lib/libphpx.so (or the platform equivalent), then build PHPX
before compiling the project.php-config, libphp,
and loaded extension ABI must agree on the PHP version and ZTS/NTS mode. Do
not mix artifacts from different PHP builds.--build-dir so
object and PCH caches can be reused. When an external test runner already
runs several tests concurrently, avoid multiplying that concurrency by an
unnecessarily large tpc -j value.bin/tpc.php but fails with tpc: reproduce with
the self-hosted compiler. Bootstrap execution can expose dynamic-call or ABI
paths that the PHP-hosted compiler does not exercise.TypePHP ships a Python tool submodule that shares the tpc entry point:
# Generate IDE helpers for Python modules
./tpc --gen-python-helper math
./tpc --gen-python-helper numpy --output-dir .ide-helper
# Convert a Python script to TypePHP
./tpc --convert-python-to-php script.py > script.phpInstall development dependencies and run the compiler unit suite:
composer install
PHPX_HOME=/path/to/phpx vendor/bin/phpunitPHPT is the end-to-end suite. Build the self-hosted compiler first and pass it
explicitly to the test runner; using the Zend PHP executable as --compiler
does not test the deployed compiler:
PHPX_HOME=/path/to/phpx php bin/tpc.php project.yml --job 2 --no-progress
php run-tests.php -q -j8 --compiler ./tpc tests/compilerStatic analysis and the source-derived coverage matrix are separate checks:
composer analyse
php bin/analyze-test-coverage.php
php bin/analyze-test-coverage.php \
--format=markdown --output=build/test-coverage.md --strictThe coverage tool reports PHP version × feature × positive compilation × runtime semantics × negative diagnostics, plus concrete PHP-parser AST nodes. It intentionally does not publish a single percentage without an explicit denominator. See Test coverage analyzer.
GitHub Actions runs PHPUnit and self-hosted PHPT on PHP 8.4 and 8.5. Changes to compiler behavior should add a focused PHPUnit test for internal/code-generation rules and a PHPT whenever runtime output or diagnostics are observable.
bin, ext, libany(), refval(), objval(), …#[Immutable] — compile-time read-only contracts#[ArrayDef] — typed array-property contractsTypePHP is licensed under the GNU General Public License v3.0.