TypePHP 编译器
https://swoole.com/aot/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
9.1 KiB
288 lines
9.1 KiB
# Embedding PHP Dependencies in an Executable
|
|
|
|
`embedded-files` packages Composer dependencies, PHP fallback files, and
|
|
runtime resources into a TypePHP executable. It addresses these deployment
|
|
problems:
|
|
|
|
- a third-party library cannot be compiled by TypePHP AOT;
|
|
- Composer autoload must still load that library lazily;
|
|
- the release host should not run `composer install` or carry a separate
|
|
`vendor` directory;
|
|
- read-only configuration, templates, certificates, or similar resources must
|
|
ship with the application.
|
|
|
|
During the build, TypePHP converts PHP files that were not compiled through
|
|
`sources` into OPcache opcodes and stores every selected file in the executable.
|
|
At runtime, `require`, `require_once`, and Composer autoload load PHP scripts
|
|
from the in-memory opcode table. Other read-only files are served by the
|
|
in-memory file table.
|
|
|
|
## Recommended setup
|
|
|
|
Keep `embedded-files` disabled for development so builds use the workspace
|
|
`vendor` directory directly. Reuse the common configuration through YAML
|
|
`include`, and enable embedding only for release builds.
|
|
|
|
`project.yml`:
|
|
|
|
```yaml
|
|
name: myapp
|
|
mode: bin
|
|
build-dir: build
|
|
|
|
sources:
|
|
- app.php
|
|
- src
|
|
|
|
ignore:
|
|
- src/legacy
|
|
```
|
|
|
|
`project-release.yml`:
|
|
|
|
```yaml
|
|
include: project.yml
|
|
optimize: 2
|
|
|
|
embedded-files:
|
|
- vendor
|
|
- resources/config
|
|
```
|
|
|
|
Build the release:
|
|
|
|
```bash
|
|
composer install --no-dev --classmap-authoritative
|
|
tpc project-release.yml
|
|
```
|
|
|
|
The project-root `app.php` starts Composer autoload in the usual way:
|
|
|
|
```php
|
|
<?php
|
|
|
|
function main(): void
|
|
{
|
|
require_once __DIR__ . '/vendor/autoload.php';
|
|
|
|
$application = new App\Application();
|
|
$application->run();
|
|
}
|
|
```
|
|
|
|
The resulting executable can run without the project source tree or a disk
|
|
`vendor` directory:
|
|
|
|
```bash
|
|
mkdir -p /tmp/myapp-release
|
|
cp myapp /tmp/myapp-release/
|
|
cd /tmp/myapp-release
|
|
./myapp
|
|
```
|
|
|
|
Here, standalone means that PHP CLI, Composer installation, and PHP project
|
|
files are no longer needed. The executable still has the native dependencies
|
|
selected by its build, such as `libphp`, PHPX, and other shared libraries.
|
|
Ship those dependencies in the release package or use a supported static-link
|
|
configuration.
|
|
|
|
## Relationship between `sources`, `ignore`, and `embedded-files`
|
|
|
|
The three settings have separate responsibilities:
|
|
|
|
| Setting | Purpose |
|
|
|---|---|
|
|
| `sources` | Select PHP/C/C++ files translated to C++ and native machine code. |
|
|
| `ignore` | Remove files from the `sources` scan result. |
|
|
| `embedded-files` | Package files verbatim and generate opcodes for PHP files that were not successfully compiled by AOT. |
|
|
|
|
A file may be selected by both `sources` and `embedded-files`:
|
|
|
|
- a successfully AOT-compiled PHP file uses its native implementation and
|
|
does not receive a duplicate opcode blob;
|
|
- a PHP file excluded by `ignore`, or rejected because it uses unsupported AOT
|
|
syntax, enters the opcode table;
|
|
- `ignore` does not remove files from `embedded-files`;
|
|
- `.stub.php` files remain raw API declarations and never receive executable
|
|
opcodes.
|
|
|
|
This lets the application enter `sources` while the complete `vendor` tree is
|
|
embedded. AOT-compatible code continues to run as machine code, and ZendVM
|
|
executes the remaining dependencies lazily.
|
|
|
|
```yaml
|
|
sources:
|
|
- src
|
|
- vendor/acme/optimized-package/src
|
|
|
|
embedded-files:
|
|
- vendor
|
|
```
|
|
|
|
There is no separate exclusion list for `embedded-files`. List the required
|
|
subdirectories or files instead of their common parent when some content must
|
|
not be packaged.
|
|
|
|
## Configuration syntax
|
|
|
|
The setting is explicitly enabled in a YAML project and must be a list. Each
|
|
entry may be a file, a directory, or a conditional path. Relative paths use the
|
|
outermost project YAML directory as their base.
|
|
|
|
```yaml
|
|
embedded-files:
|
|
- vendor
|
|
- resources/app.json
|
|
- path: resources/windows
|
|
if: PHP_OS_FAMILY == "Windows"
|
|
- path: resources/php85
|
|
if: PHP_VERSION_ID >= 80500
|
|
```
|
|
|
|
Directories are scanned recursively and all regular files are archived. JSON,
|
|
YAML, templates, and other non-PHP resources retain their original bytes.
|
|
|
|
Read embedded resources through their original paths:
|
|
|
|
```php
|
|
$config = file_get_contents(__DIR__ . '/resources/app.json');
|
|
```
|
|
|
|
Embedded files are read-only. Logs, caches, uploads, and databases belong in a
|
|
separate runtime data directory. Do not rely on `glob()` or directory iteration
|
|
over an embedded directory; read resources through known file paths.
|
|
|
|
## Build requirements
|
|
|
|
`embedded-files` is available only for a regular `mode: bin` build. It is not
|
|
available for `ext`, `lib`, Nano, WASI, iOS, or Android targets.
|
|
|
|
The build host needs:
|
|
|
|
1. `php` or `php.exe` matching the target `libphp`;
|
|
2. Zend OPcache loadable by that CLI;
|
|
3. the same full PHP version, ZTS/NTS mode, Debug mode, and integer width as
|
|
the final runtime.
|
|
|
|
Check the build PHP before compiling:
|
|
|
|
```bash
|
|
php -r 'var_dump(PHP_VERSION, PHP_ZTS, extension_loaded("Zend OPcache"), function_exists("opcache_compile_file"));'
|
|
```
|
|
|
|
TypePHP probes OPcache with `-n` and tries the standard extension locations.
|
|
A standalone `tpc` without the matching PHP CLI and OPcache cannot build an
|
|
`embedded-files` project.
|
|
|
|
OPcache is only the build-time serializer. The generated program does not need:
|
|
|
|
- the OPcache extension;
|
|
- PHP CLI;
|
|
- a Composer installation;
|
|
- disk copies of `vendor/autoload.php` or any other embedded file.
|
|
|
|
Embedded opcodes are tied to the complete PHP version. If the runtime PHP does
|
|
not match the PHP that generated the opcodes, the program reports the mismatch
|
|
at startup instead of executing incompatible bytecode.
|
|
|
|
## Build output and logs
|
|
|
|
A first build prints messages similar to:
|
|
|
|
```text
|
|
embedded-files: found 3012 files (2886 PHP)
|
|
Generating embedded opcodes for 2886 PHP files
|
|
Vendor opcode cache: 0 reused, 2886 to generate
|
|
Packed 3012 files and 2886 opcode blobs
|
|
```
|
|
|
|
A later build can reuse Composer vendor opcodes:
|
|
|
|
```text
|
|
Vendor opcode cache: 2886 reused, 0 to generate
|
|
Packed 3012 files and 2886 opcode blobs
|
|
```
|
|
|
|
Opcode blobs, the archive, and object files live under `build-dir/cache`.
|
|
Generated `embedded-opcodes-<name>.cc` contains only readable index code; large
|
|
file contents are not expanded into C++ arrays.
|
|
|
|
A PHP file that OPcache cannot compile and is not expected to be required is
|
|
reported as `Skipping non-executable embedded PHP file`; its original bytes are
|
|
still archived. If the application can load that file, fix the build error
|
|
instead of ignoring the message.
|
|
|
|
## Vendor opcode cache
|
|
|
|
Persistent opcode caching is enabled only for a directory named `vendor` whose
|
|
root contains `autoload.php`. Its key includes:
|
|
|
|
- the vendor root path and directory mtime;
|
|
- PHP CLI and OPcache binary signatures;
|
|
- PHP version, ZTS/Debug mode, and integer width.
|
|
|
|
Other `embedded-files` directories regenerate their opcodes on every build
|
|
because the compiler has no reliable invalidation boundary for them.
|
|
|
|
Editing an existing nested file does not necessarily change the vendor root
|
|
mtime. Use `--force` when this happens:
|
|
|
|
```bash
|
|
tpc project-release.yml --force
|
|
```
|
|
|
|
Composer `install` or `update` normally rebuilds root entries and autoload
|
|
files, but a release build should still use `--force` after manual vendor
|
|
changes or whenever a cached result is questionable.
|
|
|
|
## Performance and release guidance
|
|
|
|
Embedding a large vendor tree increases the executable size, linker input, and
|
|
process startup cost. That fixed cost is visible in PHPT, unit tests, and local
|
|
workflows that start many short-lived processes. Recommended practice:
|
|
|
|
- use `project.yml` without `embedded-files` for development and tests;
|
|
- enable it only in `project-release.yml`;
|
|
- keep the same `build-dir` to reuse opcode, archive, and object caches;
|
|
- reserve `--force` for vendor changes that did not invalidate the cache.
|
|
|
|
Composer autoload remains lazy. ZendVM executes a vendor opcode only when code
|
|
first requests the corresponding class; embedding the complete vendor tree
|
|
does not execute every PHP file during process startup.
|
|
|
|
## Frequently asked questions
|
|
|
|
### Why is Composer autoload still required?
|
|
|
|
`embedded-files` changes where files are stored and how PHP scripts are
|
|
compiled. It does not replace Composer's class map and PSR-4 rules. Require the
|
|
same `vendor/autoload.php`; the autoloader and later class files are loaded from
|
|
the executable's memory tables.
|
|
|
|
### Can only `vendor/autoload.php` be embedded?
|
|
|
|
No. The autoload file contains loading rules, while the actual package files
|
|
must also be present. Embed the complete `vendor` directory in normal projects.
|
|
|
|
### Does the runtime host need OPcache?
|
|
|
|
No. PHPX provides the decoding and execution integration linked into the
|
|
program. The OPcache extension is needed only to generate opcodes at build time.
|
|
|
|
### Does this protect PHP source code?
|
|
|
|
Do not treat it as encryption or source protection. The archive includes the
|
|
original bytes of selected files to support ordinary file reads. Someone who
|
|
can analyze the executable may still extract them.
|
|
|
|
### What if vendor code depends on another PHP extension?
|
|
|
|
`embedded-files` packages PHP files; it does not embed implementations such as
|
|
`curl` or `pdo_mysql`. Declare them through `ext-deps` or
|
|
`extension-dependencies` and provide them in the target runtime.
|
|
|
|
```yaml
|
|
ext-deps:
|
|
- curl
|
|
- pdo_mysql
|
|
```
|
|
|