From 022ca55ff0e43b7f395428d3c3019bd8528bd643 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Wed, 26 Aug 2026 11:14:13 +0800 Subject: [PATCH] docs(readme): update documentation with comprehensive improvements --- README-CN.md | 301 ++++++++++++++++++++++++++++++++++++++++----- README.md | 335 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 571 insertions(+), 65 deletions(-) diff --git a/README-CN.md b/README-CN.md index bd69376f..b1cb0304 100644 --- a/README-CN.md +++ b/README-CN.md @@ -6,9 +6,13 @@ **PHP 原生 AOT 编译器** -将 PHP 源码提前(AOT)编译为原生机器码,生成独立的可执行文件、PHP 扩展和静态库, +将 PHP 源码提前(AOT)编译为原生机器码,生成原生可执行文件、PHP 扩展和共享库, 同时保留你熟悉的 PHP 语法。 +[![Tests](https://github.com/swoole/typephp/actions/workflows/tests.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/tests.yml) +[![PHP 8.4–8.5](https://img.shields.io/badge/PHP-8.4--8.5-777bb4.svg)](https://www.php.net/) +[![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](LICENSE) + --- @@ -19,34 +23,64 @@ TypePHP 是一个 AOT(Ahead-Of-Time,提前编译)编译器,它把 PHP 再编译为原生机器码。与字节码缓存或虚拟机不同,它不会在运行时解释 opcode, 而是直接生成在 CPU 上运行的原生二进制。 -它保留熟悉的 PHP 语法,同时引入编译期类型信息,让编译器为你的性能热点生成快速、 -静态类型的 C++ 代码,而其余代码仍运行在久经考验的 Zend 引擎上。 +它保留熟悉的 PHP 语法,同时引入编译期类型信息,让编译器为性能热点生成快速、 +静态类型的 C++ 代码。动态 PHP 值、内置函数、反射和对象元数据继续通过 PHPX +与 Zend runtime 互操作;用户函数编译完成后不再以 Zend opcode 方式执行。 TypePHP **完全由 PHP 语言编写**,并且**完全自举**:`tpc` 编译器二进制就是 用 TypePHP 编译编译器自身的 PHP 源码得到的。整个自举链路是纯 PHP——编译器 本身没有任何 C 或 C++ 胶水代码。 +TypePHP 仍在积极开发中。它提供的是边界明确、可测试的 PHP 子集,而不是宣称可以 +无修改替代所有高度动态的 PHP 程序。在将现有项目迁移到 TypePHP 前,请先阅读 +[兼容性模型](#兼容性模型)和[不兼容特性清单](docs/INCOMPATIBLE_PHP_FEATURES.md)。 + +## 工作原理 + +```text +PHP 源码 + .stub.php 声明 + 可选 C/C++ 源码 + │ + ▼ + 解析、校验并收集全部声明 + │ + ▼ + 将函数实现和常量表达式降级为 C++17 + │ + ▼ + 原生编译器 + 可复用对象/PCH 缓存 + │ + ▼ + 可执行文件 | PHP 扩展 | 共享库 | WASI Component +``` + +prepare 阶段只建立完整符号模型,不分配运行时 Cache ID。常量和声明默认值只保留 +AST,待全部项目符号就绪后再在 convert 阶段解析。这一两阶段设计保证多文件构建和 +编译器自举过程具有确定性。 + ## 特性 - **完全自举、纯 PHP 实现** —— TypePHP 编译器完全由 PHP 语言编写,并能自举: 用 `tpc` 编译编译器自身的源码,即可生成原生二进制。 - **真正的 AOT 编译** —— PHP 先降级为 C++17,再编译为原生机器码。无解释器、 无 opcode 缓存、无 JIT 预热。 -- **三种构建模式** —— 同一份代码可编译为独立 `bin` 可执行文件、可加载的 PHP - `ext` 扩展,或 `lib` 静态库。 +- **三种原生构建模式** —— 同一份代码可编译为原生 `bin` 可执行文件、可加载的 + PHP `ext` 扩展,或可复用的 `lib` 共享库。 - **原生类型系统** —— `int`、`float`、`bool` 直接映射为 C++ 标量类型 (`int64_t`、`double`、`bool`),数值代码可获得数量级的性能提升。 - **高精度数值** —— `bigInt`(GMP)、`decimal`(libmpdec)、`bigFloat`(MPFR), - 零开销算术运算。 + 提供强类型运算符和方法 API。 - **强类型容器** —— `std::array`、`std::vector`、`std::map`、`std::ordered_map`, 元素类型在编译期确定;最高比 PHP 数组快 **10 倍**,性能与 C++ `std::vector` 相当。 - **通用方法(Universal Methods)** —— 在原生类型上直接调用方法 - (`$s->upper()`、`$arr->contains()`、`$big->mul(2)`),零运行时派发开销。 + (`$s->upper()`、`$arr->contains()`、`$big->mul(2)`);静态类型已知时在编译期 + 直接解析调用。 - **混合 C++ / PHP 编程** —— 在性能关键内核中直接调用 C++ 函数(反之亦然)。 - **编译期函数与关键词** —— `any()`、`refval()`、`objval()`、`expected()`、 `unexpected()`,以及 `toInt()`、`toString()`、`toArray()` 等。 - **编译期安全检查** —— `#[Immutable]` 只读契约和 `#[ArrayDef]` 数组结构元数据, 在编译期检查,零运行时开销。 +- **编译期代码生成** —— `#[Getter]`、`#[Setter]`、`#[With]`、`#[Constructor]`、 + `#[Printer]` 和 `#[Arrayable]` 根据属性声明生成类型安全的方法。 - **现代 PHP 支持** —— PHP 8.4 property hooks、非对称可见性、PHP 8.5 `clone()`-with 以及 `(void)` 丢弃表达式。 - **跨平台与 WASM** —— 面向 x86-64 和 ARM64 的 Linux、Windows、macOS 目标, @@ -60,7 +94,7 @@ TypePHP **完全由 PHP 语言编写**,并且**完全自举**:`tpc` 编译 | 编译目标 | 原生机器码 | 字节码 | 机器码(trace) | | 启动 / 预热 | 无(已编译完成) | 每进程预热 | JIT 预热 | | 类型驱动优化 | 编译期、全程序 | 无 | 有限,基于 trace | -| 独立可执行文件 | 支持 | 不支持 | 不支持 | +| 生成原生可执行文件 | 支持 | 不支持 | 不支持 | | 源码保护 | 编译为机器码 | 字节码(可还原) | 字节码(可还原) | | 性能确定性 | 是 | 否 | 否 | @@ -69,35 +103,40 @@ TypePHP **完全由 PHP 语言编写**,并且**完全自举**:`tpc` 编译 - **接近原生的性能。** 数值密集和容器密集的热点路径会编译为与 C++ 程序相同的机器码。 见下方[基准测试](#基准测试)。 - **源码保护。** 源码被编译掉——交付物是原生二进制,而不是可读的 PHP 文件。 -- **零依赖部署。** 二进制模式生成单个自包含可执行文件,无需 PHP 运行时即可运行。 +- **原生进程入口。** 二进制模式直接启动原生可执行文件,不需要 PHP CLI 或独立的 + 解释器进程。可执行文件仍会嵌入或链接 PHPX、`libphp` 及项目配置的原生库,部署包 + 中必须提供这些运行时依赖。 - **渐进式类型,真正带来收益。** 只在性能关键处添加 `use native_types`、`std::` 容器和类型声明,其余保持普通 PHP。 -- **完整 PHP 生态互通。** 扩展模式以标准 PHP 扩展形式加载到 `php-fpm`, - 现有框架和工具链可继续使用。 +- **Zend 生态互通。** 扩展模式以标准 PHP 扩展形式加载,项目可以调用受支持的 + 内置函数,并显式声明依赖的其他 Zend 扩展。 ## 前置要求 -- **PHP 8.4 – 8.5**,需包含 `embed` 模块(`libphp.so`) +- **PHP 8.4 – 8.5** CLI、开发头文件及 `php-config` +- 在类 Unix 系统构建二进制/共享库时,需要与 PHP 匹配的 **embed 库**(`libphp.so`) - **GCC 9+**(或 Clang),支持 **C++17** - **CMake 3.24+** +- **Composer 2** - 高精度数学库:**GMP**、**MPFR**(libmpdec 已随 PHPX 内置) ```shell # Ubuntu/Debian -sudo apt install libgmp-dev libmpfr-dev +sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev # RHEL/CentOS/Fedora -sudo dnf install gmp-devel mpfr-devel +sudo dnf install gcc gcc-c++ cmake pkgconf-pkg-config gmp-devel mpfr-devel # Arch Linux -sudo pacman -S gmp mpfr +sudo pacman -S base-devel cmake pkgconf gmp mpfr ``` > GMP 用于 `bigInt`,MPFR 用于 `bigFloat`。`decimal` 底层是 libmpdec, > 已随 PHPX 内置,无需单独安装。 -预览版目前以 **Linux** 为主要开发平台(推荐 Ubuntu 22.04)。Windows 和 macOS -打包通过同一入口点支持。 +Linux 是主要开发和 CI 平台。编译器也提供 Windows、macOS、x86-64、ARM64 和 +WASI 后端;具体主机能否构建某个目标,仍取决于 PHP embed、工具链和第三方库是否 +可用。 ## 安装 @@ -119,11 +158,24 @@ vendor/bin/tpc.php project.yml bin/tpc.php project.yml ``` +### 从源码安装 + +```bash +git clone https://github.com/swoole/typephp.git +cd typephp +composer install +php bin/tpc.php --help +``` + +可以使用 `PHPX_HOME` 指向独立的 PHPX 源码或安装目录。`PHP_HOME` 可以指向 PHP +embed 安装前缀;在类 Unix 系统中,该目录应包含 `bin/php-config`、PHP 头文件和 +`lib/libphp.so`。 + ### 构建 `libphp.so` -`tpc` 需要以 `embed` SAPI 构建的 PHP。如果 Linux 上缺少 `libphp.so`, -`tpc.php` 可以交互式下载 PHP 源码并自动构建。详见 -[自动构建 libphp.so](docs/LIBPHP_INSTALLER.md)。 +二进制和共享库构建需要 PHP 的 `embed` SAPI。如果 Linux 上缺少 `libphp.so`, +`tpc.php` 可以交互式下载 PHP 源码并自动构建。PHP 扩展构建从宿主 SAPI 解析 Zend +符号,不能再加载第二份 `libphp`。详见[自动构建 libphp.so](docs/LIBPHP_INSTALLER.md)。 ## 快速开始 @@ -147,16 +199,17 @@ bin/tpc.php hello.php ./hello ``` -输出: +输出示例(具体 PHP 版本和平台字符串取决于实际链接的运行时): ``` Hello World! -string(5) "8.4.x" +string(5) "8.x.x" string(16) "Linux ..." ``` > 二进制模式需要全局 `main()` 函数。它可以声明为无参数,或 -> `main(int $argc, array $argv)` 以接收命令行参数,且必须返回 `void`。 +> `main(int $argc, array $argv)` 以接收命令行参数,且必须返回 `void`。全局作用域 +> 不允许可执行语句;可执行代码必须位于函数或方法内。 ## 编译模式 @@ -165,8 +218,8 @@ TypePHP 支持三种构建模式,通过 `-m` / `--mode` 选择: | 模式 | 参数 | 输出 | 需要 `main()` | 典型用途 | |---|---|---|---|---| | 二进制 | `-m bin`(默认) | 可执行文件 | 是 | CLI 工具、常驻服务、独立应用 | -| 扩展 | `-m ext` | `.so` / `.dll` | 否 | `php-fpm` 上的 Web 应用、即插即用 PHP 扩展 | -| 库 | `-m lib` | 静态库 | 否 | 将编译后的代码嵌入其他项目 | +| 扩展 | `-m ext` | PHP `.so` / `.dll` | 否 | 将编译后的函数和类加载到 PHP SAPI | +| 库 | `-m lib` | 共享库及自动生成的 `.stub.php` | 否 | 在其他项目中复用编译后的 TypePHP API | ```bash # 二进制(默认) @@ -175,12 +228,130 @@ bin/tpc.php app.php -o myapp # PHP 扩展 bin/tpc.php extension/ -m ext -o my_extension -# 静态库 +# 共享库,同时生成 mylib.stub.php bin/tpc.php lib/ -m lib -o mylib ``` 详见[编译模式](docs/COMPILATION_MODES.md)。 +## 项目配置 + +多文件项目建议使用 `project.yml` 固化可复用的构建配置: + +```yaml +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-dependencies` 是等价长名称,两者不能同时使用。 +ext-deps: + - pdo_mysql + - curl +``` + +路径以 YAML 文件所在目录为基准。source 可以是文件或目录;条件 source 支持 +`PHP_VERSION`、`PHP_VERSION_ID` 和 `PHP_OS_FAMILY`。命令行参数优先于 YAML +中的同名配置。原生链接依赖应写入 `link-libs`;`ext-deps` 会生成 +`ZEND_MOD_REQUIRED`,缺少所需 PHP 扩展时由 Zend 拒绝加载模块。 + +构建目录保存生成的 C++、依赖对象和预编译头缓存。复用同一个构建目录可以显著加快 +增量构建;仅在确实需要重编 PHPX 公共对象时使用 `--force`。 + +全部项目配置项及命令行优先级详见[编译器命令行](docs/COMPILER_CLI.md)。 + +## 兼容性模型 + +TypePHP 会在适合 AOT 编译的范围内保持 PHP 语法和运行行为,同时有一些明确限制: + +- 全局作用域只允许声明,可执行语句必须位于函数或方法内; +- 二进制模式对 `main()` 使用严格签名; +- `use native_types` 会让标量声明使用固定原生存储,之后不能改为不兼容类型; +- 静态可确定的调用和属性会直接编译,受支持的动态操作则通过 PHPX/Zend runtime + fallback 执行; +- `.stub.php` 用于声明 C++ 或外部库 API,函数体必须为空,stub 文件禁止声明 + `#[Native]` 类; +- 部分高度动态的引用、声明、闭包和反射模式仍明确不支持。 + +兼容性边界属于公共契约,同时有正向和负向测试保护。请以 +[不兼容 PHP 特性清单](docs/INCOMPATIBLE_PHP_FEATURES.md)为当前准确列表,不要把 +README 未提及的行为默认理解为已支持。 + +## 编译期 Attribute 与代码生成 + +TypePHP 在 class lowering 阶段消费内置的代码生成 Attribute。生成的方法保留属性 +声明的类型,并与显式声明的方法一样参与名称冲突、继承关系和 final 方法检查。 + +| Attribute | 目标 | 生成的 API | +|---|---|---| +| `#[Getter]` | 实例属性,包括构造器提升属性 | `public function getName(): T` | +| `#[Setter]` | 可变实例属性,包括构造器提升属性 | `public function setName(T $name): void` | +| `#[With]` | 可变实例属性,包括构造器提升属性 | `public function withName(T $name): static`;克隆对象、修改副本并返回副本 | +| `#[Constructor]` | 普通实例属性声明 | 将属性加入自动生成的 public `__construct()` | +| `#[Printer]` | 具名类 | `public function __toString(): string` | +| `#[Arrayable]` | 具名类 | `public function toArray(): array` | + +```php +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']; +} +``` + +未指定 `fields` 时,`#[Printer]` 和 `#[Arrayable]` 使用当前类自身的 public 实例 +属性。位置参数写法 `#[Arrayable(['id'])]` 等价于 +`#[Arrayable(fields: ['id'])]`。 + +`#[Getter]`、`#[Setter]` 和 `#[With]` 不能用于 static 属性或带 property hook 的 +属性;`#[Setter]` 和 `#[With]` 还会拒绝 readonly 属性。类中已经显式声明 +`__construct()` 时不能使用 `#[Constructor]`,必填的构造属性必须位于带默认值的 +属性之前。生成的方法名若与已有方法冲突,或覆盖继承而来的 final 方法,编译期会 +直接报错。 + ## 使用示例 ### 1. 原生类型 —— 编译期数值加速 @@ -306,7 +477,7 @@ function main(): void using namespace php; -var php_fast_sum(Int a, Int b) { +Int php_fast_sum(Int a, Int b) { return a + b; } ``` @@ -314,7 +485,7 @@ var php_fast_sum(Int a, Int b) { ```php ` | 构建模式(默认 `bin`) | | `-r`, `--run` | 构建成功后运行 | | `-j`, `--job ` | 并行编译任务数(默认 `4`) | +| `-f`, `--force` | 不使用缓存,重新编译可复用 PHPX 对象 | | `--build-dir ` | 生成 C++ 与中间产物的目录 | | `--dry` | 只生成 C++,跳过编译与链接 | | `--php-version <8.4\|8.5>` | 接受的 PHP 语法版本 | | `--cxx-std ` | C++ 标准(如 `c++17`、`c++20`) | | `--march ` | 目标指令集(如 `native`) | +| `--target-platform ` | 交叉编译目标 triple | | `--lto` | 启用链接时优化 | | `--sanitize ` | 启用 sanitizer(如 `address`) | +| `--profile` | 启用 Linux gperftools 性能分析 | +| `--format` | 使用 clang-format 格式化生成的 C++ | +| `--no-literal-strings` | 禁用字面量字符串表优化 | +| `--no-progress`, `--no-color` | 适合 CI 的输出控制 | +| `-I`, `-D`, `-L`, `-l` | 可重复指定的原生 include、define、库路径和链接库参数 | 运行 `bin/tpc.php --help` 查看权威的最新参数列表。详见 [编译器命令行](docs/COMPILER_CLI.md),包括 Bash 补全: @@ -412,6 +599,20 @@ bin/tpc.php --wasm=browser app.php source <(./tpc --generate-completion=bash) ``` +## 常见问题 + +- **缺少 `libphp.so`:** 安装或编译与当前 PHP 匹配的 embed SAPI,设置 + `PHP_HOME`,或使用 `bin/tpc.php` 在 Linux 上提供的交互式安装流程。 +- **找不到 PHPX:** 将 `PHPX_HOME` 指向包含 `include/` 和 + `lib/libphpx.so`(或对应平台文件)的 PHPX 安装目录,并在编译项目前先构建 PHPX。 +- **启动崩溃或出现 ABI 错误:** PHP 头文件、`php-config`、`libphp` 和扩展 ABI + 必须使用一致的 PHP 版本及 ZTS/NTS 模式,不能混用不同 PHP 构建产生的产物。 +- **增量构建异常缓慢:** 固定使用同一个 `--build-dir`,以复用对象和 PCH 缓存。 + 当外层测试工具已经并行运行多个测试时,不要再设置过大的 `tpc -j`,避免并发数 + 相乘后造成 CPU 和内存争用。 +- **使用 `bin/tpc.php` 可以编译,但自举 `tpc` 失败:** 必须用自举编译器复现。 + 自举执行可能暴露 PHP-hosted 编译器不会经过的动态调用或 ABI 路径。 + ## Python 桥接 TypePHP 内置一个 Python 工具子模块,复用 `tpc` 入口: @@ -427,6 +628,39 @@ TypePHP 内置一个 Python 工具子模块,复用 `tpc` 入口: 详见 [Python 工具子模块](docs/python/tools.md)。 +## 开发与测试 + +安装开发依赖并运行编译器单元测试: + +```bash +composer install +PHPX_HOME=/path/to/phpx vendor/bin/phpunit +``` + +PHPT 是端到端测试。必须先构建自举编译器,并显式传给测试工具;将 Zend PHP +可执行文件作为 `--compiler` 并不能验证实际交付的编译器: + +```bash +PHPX_HOME=/path/to/phpx php bin/tpc.php project.yml --job 2 --no-progress +php run-tests.php -q -j8 --compiler ./tpc tests/compiler +``` + +静态分析与从测试源码生成的覆盖矩阵是两项独立检查: + +```bash +composer analyse +php bin/analyze-test-coverage.php +php bin/analyze-test-coverage.php \ + --format=markdown --output=build/test-coverage.md --strict +``` + +覆盖工具分别报告 PHP 版本 × 特性 × 正向编译 × 运行语义 × 负向诊断,并列出实际 +出现的 php-parser AST 节点。它不会给出分母不明确的单一百分比。详见 +[测试覆盖分析工具](docs/TEST_COVERAGE_ANALYZER.md)。 + +GitHub Actions 会在 PHP 8.4 和 8.5 上分别运行 PHPUnit 与自举 PHPT。修改编译器 +内部规则或代码生成时应增加聚焦的 PHPUnit;运行输出或诊断可观察时还应增加 PHPT。 + ## 文档 - [快速入门](docs/QUICKSTART.md) —— 最小编译流程 @@ -436,10 +670,15 @@ TypePHP 内置一个 Python 工具子模块,复用 `tpc` 入口: - [原生类型](docs/NATIVE_TYPES.md) —— 原生标量类型 - [高精度类型](docs/HIGH_PRECISION_TYPES.md) —— BigInt / Decimal / BigFloat - [Std 容器](docs/STD_CONTAINERS.md) —— 强类型容器 -- [通用方法](docs/UNIVERSAL_METHODS.md) —— 零开销方法 +- [通用方法](docs/UNIVERSAL_METHODS.md) —— 编译期方法解析 - [编译期函数](docs/COMPILE_TIME_FUNCTIONS.md) —— `any()`、`refval()`、`objval()` 等 - [混合 C++/PHP](docs/MIXED_CPP_PHP.md) —— C++/PHP 互操作 - [`#[Immutable]`](docs/IMMUTABLE.md) —— 编译期只读契约 +- [`#[ArrayDef]`](docs/ARRAY_DEF.md) —— 强类型数组属性契约 +- [Property hooks](docs/PROPERTY_HOOKS.md) —— PHP 8.4 hook 降级和运行时元数据 +- [对象存储模型](docs/OBJECT_STORAGE_AND_PASSING_MODELS.md) —— Zend object、Box 与 Native class 边界 +- [Generator](docs/YIELD_GENERATOR.md) —— 生成器降级与生命周期 +- [测试覆盖分析工具](docs/TEST_COVERAGE_ANALYZER.md) —— AST 与特性证据矩阵 - [WASI 构建](docs/WASI_BUILD.md) —— WASI 目标 ## 授权协议 diff --git a/README.md b/README.md index 9746c3e2..599563bc 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,13 @@ **A native AOT compiler for PHP** Compile PHP source code into native machine code ahead of time — producing -standalone executables, PHP extensions, and static libraries — while keeping +native executables, PHP extensions, and shared libraries — while keeping the PHP syntax you already know. +[![Tests](https://github.com/swoole/typephp/actions/workflows/tests.yml/badge.svg)](https://github.com/swoole/typephp/actions/workflows/tests.yml) +[![PHP 8.4–8.5](https://img.shields.io/badge/PHP-8.4--8.5-777bb4.svg)](https://www.php.net/) +[![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](LICENSE) + --- @@ -22,14 +26,45 @@ 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 your hot paths — while the -rest of your code continues to run on the battle-tested Zend engine. +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](#compatibility-model) and the +[incompatible-feature list](docs/INCOMPATIBLE_PHP_FEATURES.md) before adopting +it for an existing application. + +## How it works + +```text +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. + ## Features - **Self-hosting, written in PHP** — the TypePHP compiler is implemented @@ -37,19 +72,19 @@ itself. source into a native binary. - **True AOT compilation** — PHP is lowered to C++17, then to native machine code. No interpreter, no opcode cache, no JIT warm-up. -- **Three build modes** — build a standalone `bin` executable, a loadable PHP - `ext` extension, or a `lib` static library from the same codebase. +- **Three native build modes** — build a native `bin` executable, a loadable + PHP `ext` extension, or a reusable `lib` shared library from the same codebase. - **Native type system** — `int`, `float`, and `bool` map directly to C++ scalar types (`int64_t`, `double`, `bool`) for orders-of-magnitude speedups on numeric code. - **High-precision numerics** — `bigInt` (GMP), `decimal` (libmpdec), and - `bigFloat` (MPFR) with zero-overhead arithmetic. + `bigFloat` (MPFR), with typed operators and method APIs. - **Strongly-typed containers** — `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`. - **Universal methods** — call methods directly on primitives - (`$s->upper()`, `$arr->contains()`, `$big->mul(2)`) with zero runtime - dispatch overhead. + (`$s->upper()`, `$arr->contains()`, `$big->mul(2)`); statically-known calls + are resolved directly at compile time. - **Mixed C++ / PHP** — call C++ functions from PHP (and vice versa) for performance-critical kernels. - **Compile-time functions & keywords** — `any()`, `refval()`, `objval()`, @@ -57,6 +92,9 @@ itself. friends. - **Compile-time safety** — `#[Immutable]` read-only contracts and `#[ArrayDef]` array-shape metadata, checked at compile time with zero runtime cost. +- **Compile-time code generation** — `#[Getter]`, `#[Setter]`, `#[With]`, + `#[Constructor]`, `#[Printer]`, and `#[Arrayable]` generate type-safe methods + from property declarations. - **Modern PHP support** — PHP 8.4 property hooks, asymmetric visibility, PHP 8.5 `clone()`-with, and `(void)` discard expressions. - **Cross-platform & WASM** — Linux, Windows, and macOS targets for x86-64 and @@ -71,7 +109,7 @@ itself. | 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 | -| Standalone executable | Yes | No | No | +| Native executable output | Yes | No | No | | Source code protection | Compiled to machine code | Bytecode (reversible) | Bytecode (reversible) | | Deterministic performance | Yes | No | No | @@ -82,38 +120,45 @@ itself. [benchmark](#benchmark) below. - **Source protection.** Your source is compiled away — shipped artifacts are native binaries, not readable PHP files. -- **Zero-dependency deployment.** Binary mode produces a single self-contained - executable that runs without a PHP runtime. +- **Native process entry.** Binary mode starts directly from a native + executable and does not require the PHP CLI or a separate interpreter + process. The executable still embeds/links PHPX, `libphp`, and any configured + native libraries, which must be available in the deployment package. - **Gradual typing that actually pays off.** Add `use native_types`, `std::` containers, and type declarations only where performance matters; the rest stays ordinary PHP. -- **Full PHP ecosystem interop.** Extension mode loads as a standard PHP - extension into `php-fpm`, so existing frameworks and tooling keep working. +- **Zend ecosystem interop.** Extension mode loads as a standard PHP extension, + and projects can call supported internal functions and require other Zend + extensions explicitly. ## Requirements -- **PHP 8.4 – 8.5** with the `embed` module (`libphp.so`) +- **PHP 8.4 – 8.5** CLI, development headers, and `php-config` +- The matching **PHP embed library** (`libphp.so`) for binary/shared-library + builds on Unix-like systems - **GCC 9+** (or Clang) with **C++17** - **CMake 3.24+** +- **Composer 2** - High-precision math libraries: **GMP**, **MPFR** (libmpdec is bundled with PHPX) ```shell # Ubuntu/Debian -sudo apt install libgmp-dev libmpfr-dev +sudo apt install build-essential cmake pkg-config libgmp-dev libmpfr-dev # RHEL/CentOS/Fedora -sudo dnf install gmp-devel mpfr-devel +sudo dnf install gcc gcc-c++ cmake pkgconf-pkg-config gmp-devel mpfr-devel # Arch Linux -sudo pacman -S gmp mpfr +sudo pacman -S base-devel cmake pkgconf gmp mpfr ``` > GMP powers `bigInt` and MPFR powers `bigFloat`. The `decimal` type is backed > by libmpdec, which is bundled with PHPX — no separate install required. -The preview currently targets **Linux** as the primary development platform -(Ubuntu 22.04 recommended). Windows and macOS packaging is supported through -the same entry point. +Linux is the primary development and CI platform. The compiler also has +Windows, macOS, x86-64, ARM64, and WASI backends; availability of PHP embed, +toolchain, and third-party libraries still determines which target can be +built on a given host. ## Installation @@ -136,11 +181,26 @@ instead: bin/tpc.php project.yml ``` +### From source + +```bash +git clone https://github.com/swoole/typephp.git +cd typephp +composer install +php bin/tpc.php --help +``` + +`PHPX_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. + ### Building `libphp.so` -`tpc` requires a PHP built with the `embed` SAPI. If `libphp.so` is missing on -Linux, `tpc.php` can interactively download the PHP source and build it for -you. See [Automatic libphp.so build](docs/LIBPHP_INSTALLER.md). +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](docs/LIBPHP_INSTALLER.md). ## Quick Start @@ -164,17 +224,19 @@ bin/tpc.php hello.php ./hello ``` -Output: +Example output (the exact PHP version and platform strings depend on the linked +runtime): ``` Hello World! -string(5) "8.4.x" +string(5) "8.x.x" string(16) "Linux ..." ``` > Binary mode requires a global `main()` function. It may be declared with no > parameters, or as `main(int $argc, array $argv)` to receive command-line -> arguments, and must return `void`. +> arguments, and must return `void`. Top-level executable statements are not +> allowed; executable code belongs in a function or method. ## Compilation Modes @@ -183,8 +245,8 @@ 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` | `.so` / `.dll` | No | Web apps on `php-fpm`, drop-in PHP extension | -| Library | `-m lib` | Static library | No | Embedding compiled code into other projects | +| 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 | ```bash # Binary (default) @@ -193,12 +255,141 @@ bin/tpc.php app.php -o myapp # PHP extension bin/tpc.php extension/ -m ext -o my_extension -# Static library +# Shared library; also generates mylib.stub.php bin/tpc.php lib/ -m lib -o mylib ``` See [Compilation modes](docs/COMPILATION_MODES.md) for details. +## Project configuration + +For multi-file projects, keep repeatable build settings in `project.yml`: + +```yaml +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 + - curl +``` + +Paths 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](docs/COMPILER_CLI.md) for all project keys and command-line +precedence rules. + +## Compatibility model + +TypePHP follows PHP syntax and runtime behavior where they are compatible with +ahead-of-time compilation, but it also makes several deliberate restrictions: + +- global scope is declaration-only; executable statements must be inside a + function or method; +- binary mode has a strict `main()` signature; +- `use native_types` opts scalar declarations into fixed native storage, so a + value cannot later change to an incompatible type; +- statically-known calls and properties are compiled directly, while supported + dynamic operations use PHPX/Zend runtime fallbacks; +- `.stub.php` files declare C++ or imported-library APIs and must contain empty + bodies; `#[Native]` classes are not permitted in stub files; +- some highly dynamic reference, declaration, closure, and reflection patterns + remain intentionally unsupported. + +The compatibility boundary is part of the public contract and has both +positive and negative tests. Consult +[Incompatible PHP features](docs/INCOMPATIBLE_PHP_FEATURES.md) for the current, +specific list instead of assuming that absence from this README means support. + +## Compile-time attributes and code generation + +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 +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. + ## Examples ### 1. Native types — compile-time numeric speedup @@ -326,7 +517,7 @@ Write performance-critical kernels in C++ and call them from PHP: using namespace php; -var php_fast_sum(Int a, Int b) { +Int php_fast_sum(Int a, Int b) { return a + b; } ``` @@ -334,7 +525,7 @@ var php_fast_sum(Int a, Int b) { ```php ` | Build mode (default `bin`) | | `-r`, `--run` | Run after a successful build | | `-j`, `--job ` | Parallel compile jobs (default `4`) | +| `-f`, `--force` | Rebuild reusable PHPX objects instead of using the cache | | `--build-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 ` | C++ standard (e.g. `c++17`, `c++20`) | | `--march ` | Target instruction set (e.g. `native`) | +| `--target-platform ` | Cross-compilation target triple | | `--lto` | Enable link-time optimization | | `--sanitize ` | 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](docs/COMPILER_CLI.md) for details, including Bash completion: @@ -434,6 +642,24 @@ Run `bin/tpc.php --help` for the authoritative, up-to-date list. See source <(./tpc --generate-completion=bash) ``` +## Troubleshooting + +- **`libphp.so` is missing:** install/build the matching PHP embed SAPI, set + `PHP_HOME`, or let `bin/tpc.php` offer the interactive Linux installer. +- **PHPX cannot be found:** set `PHPX_HOME` to a PHPX installation containing + `include/` and `lib/libphpx.so` (or the platform equivalent), then build PHPX + before compiling the project. +- **Startup crashes or ABI errors:** the PHP headers, `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. +- **Incremental builds are unexpectedly slow:** keep a stable `--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. +- **A project compiles with `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. + ## Python bridge TypePHP ships a Python tool submodule that shares the `tpc` entry point: @@ -449,6 +675,42 @@ TypePHP ships a Python tool submodule that shares the `tpc` entry point: See [Python tool submodule](docs/python/tools.md). +## Development and testing + +Install development dependencies and run the compiler unit suite: + +```bash +composer install +PHPX_HOME=/path/to/phpx vendor/bin/phpunit +``` + +PHPT 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: + +```bash +PHPX_HOME=/path/to/phpx php bin/tpc.php project.yml --job 2 --no-progress +php run-tests.php -q -j8 --compiler ./tpc tests/compiler +``` + +Static analysis and the source-derived coverage matrix are separate checks: + +```bash +composer analyse +php bin/analyze-test-coverage.php +php bin/analyze-test-coverage.php \ + --format=markdown --output=build/test-coverage.md --strict +``` + +The 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](docs/TEST_COVERAGE_ANALYZER.md). + +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. + ## Documentation - [Quick Start](docs/QUICKSTART.md) — minimal compilation flow @@ -458,10 +720,15 @@ See [Python tool submodule](docs/python/tools.md). - [Native types](docs/NATIVE_TYPES.md) — native scalar types - [High-precision types](docs/HIGH_PRECISION_TYPES.md) — BigInt / Decimal / BigFloat - [Std containers](docs/STD_CONTAINERS.md) — strongly-typed containers -- [Universal methods](docs/UNIVERSAL_METHODS.md) — zero-overhead methods +- [Universal methods](docs/UNIVERSAL_METHODS.md) — compile-time method resolution - [Compile-time functions](docs/COMPILE_TIME_FUNCTIONS.md) — `any()`, `refval()`, `objval()`, … - [Mixed C++/PHP](docs/MIXED_CPP_PHP.md) — C++/PHP interop - [`#[Immutable]`](docs/IMMUTABLE.md) — compile-time read-only contracts +- [`#[ArrayDef]`](docs/ARRAY_DEF.md) — typed array-property contracts +- [Property hooks](docs/PROPERTY_HOOKS.md) — PHP 8.4 hook lowering and runtime metadata +- [Object storage models](docs/OBJECT_STORAGE_AND_PASSING_MODELS.md) — Zend object, Box, and Native class boundaries +- [Generators](docs/YIELD_GENERATOR.md) — generator lowering and lifecycle +- [Test coverage analyzer](docs/TEST_COVERAGE_ANALYZER.md) — AST and feature evidence matrix - [WASI build](docs/WASI_BUILD.md) — WASI targets ## License