# How TypePHP Achieves Native AOT Compilation for PHP: A Deep Dive into the Source Code

> Discover how TypePHP achieves native AOT compilation for PHP by compiling PHP AST to C++ source code and then to standalone executables. Learn the internals of this innovative technology.

- Repository: [Swoole Project/typephp](https://github.com/swoole/typephp)
- Tags: deep-dive
- Published: 2026-08-30

---

**TypePHP compiles PHP source code into native machine code by first lowering the PHP AST into C++17 source files, then invoking a system C++ compiler (Clang or GCC) to produce standalone executables or shared libraries that run without the PHP interpreter.**

TypePHP, developed by the Swoole team, implements a true ahead-of-time (AOT) compiler for PHP written entirely in PHP. Unlike JIT compilation that still operates within the Zend engine at runtime, TypePHP generates self-contained native binaries by bridging PHP's dynamic nature with C++'s static compilation model. This article examines the internal architecture, type mapping strategies, and build pipeline as implemented in the `swoole/typephp` repository.

## The AOT Compilation Pipeline

TypePHP orchestrates native compilation through a six-stage pipeline managed primarily in [`src/Translator.php`](https://github.com/swoole/typephp/blob/main/src/Translator.php) and [`src/compiler.php`](https://github.com/swoole/typephp/blob/main/src/compiler.php). Each stage transforms the source representation until it becomes executable machine code.

### Stage 1: Parse and Validate

The process begins with `Translator::prepare()`, which parses all input files into an AST and constructs a global symbol model. This phase performs type checking and resolves declarations across the entire project before any code generation occurs.

### Stage 2: Lowering to C++17

During `Translator::convert()`, the compiler walks the PHP AST and lowers each function, class, constant, and statement into C++17 code that utilizes the PHPX runtime API. Native scalar types undergo direct mapping: PHP `int` becomes `int64_t`, `float` becomes `double`, and `bool` becomes C++ `bool`. This transformation enables ABI-level calls without `zval` boxing for performance-critical paths.

### Stage 3: Code Generation

The lowering phase emits `*.cc` source files to a build directory along with `*.stub.php` files for exported symbols. These C++ sources include embedded PHPX headers ([`phpx.h`](https://github.com/swoole/typephp/blob/main/phpx.h)) that provide thin C++ wrappers around the Zend API, allowing generated code to call `php::getFunction()` and `php::getClassEntrySafe()` when interaction with the PHP runtime is required.

### Stage 4: Compilation

`Translator::compile()` delegates to `CompilerFactory` to detect the platform-specific C++ compiler (Clang on macOS, GCC on Linux, or WASM compilers for WASI targets). The detected compiler builds object files from the generated C++ sources with user-specified optimization levels (`-O3`, etc.).

### Stage 5: Linking

Finally, `Translator::build()` links the object files into the final artifact. The linker selection depends on the target platform—`llvm-ld` for WASI targets or standard `ld` for native Linux/macOS binaries—producing either an executable (`.exe`), shared library (`.so` or `.dll`), or PHP extension.

### Stage 6: Execution (Optional)

When invoked with the `-r` or `--run` flag, TypePHP immediately executes the freshly linked binary, completing the edit-compile-run cycle in a single command.

## Two-Phase Architecture: Prepare vs. Convert

TypePHP employs a deterministic two-phase design that separates symbol resolution from code generation, enabling cross-file optimization and self-hosting builds.

**Prepare Phase** builds the complete symbol model—including all functions, classes, and constants—without allocating runtime IDs. This global analysis allows the compiler to resolve every declaration before lowering begins, which is essential for handling circular dependencies and cross-file references.

**Convert Phase** uses the completed symbol model to lower each PHP file's body into C++ while preserving the original semantics. This separation guarantees that identical source code always yields identical C++ output, supporting aggressive caching and incremental builds.

The orchestration in [`src/compiler.php`](https://github.com/swoole/typephp/blob/main/src/compiler.php) demonstrates this flow:

```php
$files = $translator->prepare($translator->parseArgv($argv));
$sourceFiles = $translator->convert($files);

```

## Native Type Mapping and ABI Optimization

TypePHP achieves near-C performance through aggressive type specialization via `NativeTypeCompatibilityTrait` located in [`src/TypeSystem/NativeTypeCompatibilityTrait.php`](https://github.com/swoole/typephp/blob/main/src/TypeSystem/NativeTypeCompatibilityTrait.php).

When native scalar types are detected, the compiler emits direct ABI calls that bypass `zval` boxing entirely. The trait maps PHP return types to C++ equivalents, with special handling for edge cases:

```php
protected function getReturnType(): string {
    $type = $this->functionDef->returnType;
    return $type === Type::STREAM ? Type::VAR : $type;
}

```

This mapping allows compiled functions to pass `int64_t` or `double` directly in CPU registers rather than allocating PHP variables on the heap, eliminating interpreter overhead for numeric computations.

## Platform Abstraction and Compiler Detection

The compiler abstracts platform differences through `PlatformFactory` and `CompilerFactory` classes found in the `src/Build` directory.

`Translator` discovers the host OS and instantiates the appropriate platform handler:

```php
$this->platform = PlatformFactory::create();
$this->cppCompiler = $this->platform instanceof Wasi
    ? $this->platform->getDefaultCompiler()
    : CompilerFactory::detectCompilerName($this->cppCompiler);

```

This architecture allows TypePHP to target Linux, macOS, Windows, or WASI (WebAssembly System Interface) without modifications to the compilation logic. The factory pattern automatically detects available toolchains, selecting Clang on Darwin systems or GCC on Linux distributions.

## Build Modes: Binary, Extension, and Library

TypePHP supports three output formats controlled via the `-m` or `--mode` CLI flag:

**Binary Mode (`-m bin`)** requires a `main()` function in the PHP source and produces a standalone executable that embeds the PHPX runtime. The resulting binary runs directly on the target CPU without requiring a system PHP installation.

**Extension Mode (`-m ext`)** generates a loadable PHP extension (`.so` on Linux, `.dll` on Windows) that can be loaded into standard PHP processes via [`php.ini`](https://github.com/swoole/typephp/blob/main/php.ini) or `dl()`. This mode allows selective optimization of performance-critical libraries while maintaining compatibility with existing PHP applications.

**Library Mode (`-m lib`)** creates a shared library accompanied by a [`.stub.php`](https://github.com/swoole/typephp/blob/main/.stub.php) file, enabling compiled PHP code to be consumed by other TypePHP projects or C++ applications through a well-defined C interface.

## Self-Hosting Capability

TypePHP is fully self-hosted: the compiler is written in PHP and can compile its own source code. The entry point [`bin/tpc.php`](https://github.com/swoole/typephp/blob/main/bin/tpc.php) implements a bootstrap process where it first compiles itself into a native `tpc` binary, then uses that binary to compile user projects.

This self-hosting loop validates the compiler's correctness and ensures the same AOT pipeline optimizes both the compiler infrastructure and end-user applications. Developers can build the compiler from source using:

```bash
php bin/tpc.php --dry  # Generates C++ sources for inspection

```

## Practical Code Examples

### Compiling a Simple Script to Native Binary

Create a PHP file with an entry point:

```php
<?php
// hello.php
function main(): void {
    echo "Hello from native binary!\n";
}

```

Compile and execute:

```bash
php bin/tpc.php hello.php -O3 -o hello
./hello

```

The output produces a native executable that prints the greeting without loading the PHP interpreter.

### Building a Loadable PHP Extension

Export functions for use in standard PHP environments:

```php
<?php
// ext.php
function greet(string $name): string {
    return "Hello, $name!";
}

```

Generate the shared object and test it:

```bash
php bin/tpc.php ext.php --mode=ext -o my_ext
php -d extension=./my_ext.so -r 'echo greet("World");'

```

### Optimizing with Native Types

Enable direct C++ type mapping for arithmetic-intensive code:

```php
<?php
use native_types;

function fib(int $n): int {
    if ($n <= 2) return 1;
    return fib($n-1) + fib($n-2);
}

function main(int $argc, array $argv): void {
    $n = (int)($argv[1] ?? 30);
    echo fib($n) . PHP_EOL;
}

```

Compile with maximum optimization:

```bash
php bin/tpc.php fib.php -O3 -o fib_bin
./fib_bin 35  # Executes significantly faster than interpreted PHP

```

## Summary

- **TypePHP implements AOT compilation** by lowering PHP AST into C++17 source code through `Translator::convert()` in [`src/Translator.php`](https://github.com/swoole/typephp/blob/main/src/Translator.php).
- **Two-phase architecture** separates symbol preparation from code generation, enabling cross-file optimization and deterministic builds.
- **Native type mapping** via `NativeTypeCompatibilityTrait` eliminates `zval` overhead for scalar types, producing direct ABI calls.
- **Platform abstraction** through `PlatformFactory` and `CompilerFactory` supports Linux, macOS, Windows, and WASI targets.
- **Multiple build modes** generate standalone binaries (`-m bin`), PHP extensions (`-m ext`), or shared libraries (`-m lib`).
- **Self-hosting design** allows the compiler to optimize its own codebase using the same pipeline it provides to users.

## Frequently Asked Questions

### How does TypePHP differ from PHP's built-in JIT compiler?

PHP's JIT compiles opcodes to machine code at runtime within the Zend engine, requiring the interpreter to remain resident in memory. TypePHP performs ahead-of-time compilation entirely before execution, producing standalone native binaries that run without the PHP interpreter or Zend engine loaded, except when explicitly calling back into PHP functions via the PHPX runtime.

### What C++ standard does TypePHP target and why?

TypePHP targets **C++17** as specified in the lowering phase of [`src/Translator.php`](https://github.com/swoole/typephp/blob/main/src/Translator.php). This standard provides modern C++ features like structured bindings, `constexpr if`, and improved type deduction that the PHPX runtime utilizes for efficient interoperability with the Zend API, while maintaining broad compiler support across Clang, GCC, and MSVC toolchains.

### Can TypePHP compile any PHP code, or are there limitations?

TypePHP supports most PHP 7/8 syntax but requires explicit type declarations for functions intended for native execution to enable optimal code generation. Code using highly dynamic features like `eval()`, `create_function()`, or runtime class declaration modifications may be unsupported or fall back to slower runtime paths through the PHPX bridge. The compiler performs strict type checking during the `prepare()` phase to catch incompatibilities early.

### How does the self-hosting process work in practice?

The self-hosting process begins with [`bin/tpc.php`](https://github.com/swoole/typephp/blob/main/bin/tpc.php), which is PHP source code. When you run `php bin/tpc.php`, it interprets the compiler source, compiles the specified input files (potentially including itself), and generates a native binary named `tpc`. Subsequent invocations can use `./tpc` instead of `php bin/tpc.php`, providing faster compilation times since the compiler itself runs as optimized native code rather than interpreted PHP.