- Add embedded-files configuration option for packaging Composer vendor files and PHP fallback files - Implement OPcache bytecode generation for embedded PHP files during build process - Update GitHub Actions workflows to handle PHP version-specific release packaging - Modify package.php to embed Composer runtime dependencies directly in compiler binaries - Remove vendor directory from release packages since dependencies are now embedded - Add comprehensive documentation for embedded-files feature in English and Chinese - Update README files to include embedded dependencies documentation links - Implement PHP ABI versioning in package filenames to distinguish different PHP builds - Add verification mechanism to ensure embedded Composer runtime functions correctly - Create detailed guides covering recommended setup, performance considerations, and build requirementsmaster
parent
cbdf5d6a11
commit
43e85b8866
13 changed files with 714 additions and 105 deletions
@ -0,0 +1,288 @@ |
|||||||
|
# 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 |
||||||
|
``` |
||||||
@ -0,0 +1,262 @@ |
|||||||
|
# 将 PHP 依赖嵌入可执行文件 |
||||||
|
|
||||||
|
`embedded-files` 用于把 Composer 依赖、PHP fallback 文件和运行时资源打包进 |
||||||
|
TypePHP 可执行文件。它主要解决以下部署问题: |
||||||
|
|
||||||
|
- 第三方库不支持 TypePHP AOT,无法放入 `sources` 原生编译; |
||||||
|
- 程序仍需通过 Composer autoload 按需加载这些 PHP 类; |
||||||
|
- 发布环境不希望执行 `composer install`,也不希望携带独立的 `vendor` 目录; |
||||||
|
- 配置、模板、证书等只读文件也需要随程序一起发布。 |
||||||
|
|
||||||
|
启用后,TypePHP 在构建期将不能 AOT 编译的 PHP 文件转换为 OPcache opcode, |
||||||
|
同时把配置中选中的全部文件写入可执行文件。运行时 `require`、`require_once` 和 |
||||||
|
Composer autoload 直接从内存中的 opcode 表加载 PHP 文件,普通只读文件则通过 |
||||||
|
内存文件表读取。 |
||||||
|
|
||||||
|
## 推荐用法 |
||||||
|
|
||||||
|
开发构建不启用 `embedded-files`,直接使用工作区中的 `vendor`。发布构建通过 |
||||||
|
YAML `include` 复用公共配置,并只在发布阶段嵌入依赖。 |
||||||
|
|
||||||
|
`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 |
||||||
|
``` |
||||||
|
|
||||||
|
构建发布版本: |
||||||
|
|
||||||
|
```bash |
||||||
|
composer install --no-dev --classmap-authoritative |
||||||
|
tpc project-release.yml |
||||||
|
``` |
||||||
|
|
||||||
|
项目根目录的 `app.php` 仍按普通 Composer 方式启动自动加载器: |
||||||
|
|
||||||
|
```php |
||||||
|
<?php |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
require_once __DIR__ . '/vendor/autoload.php'; |
||||||
|
|
||||||
|
$application = new App\Application(); |
||||||
|
$application->run(); |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
生成的可执行文件可以移到没有项目源码和 `vendor` 目录的位置运行: |
||||||
|
|
||||||
|
```bash |
||||||
|
mkdir -p /tmp/myapp-release |
||||||
|
cp myapp /tmp/myapp-release/ |
||||||
|
cd /tmp/myapp-release |
||||||
|
./myapp |
||||||
|
``` |
||||||
|
|
||||||
|
这里的“独立运行”是指不再需要 PHP CLI、Composer 安装和磁盘上的 PHP 项目文件。 |
||||||
|
程序仍需要构建方式所对应的 `libphp`、PHPX 及其他原生共享库;这些依赖需随发布包 |
||||||
|
提供,或使用项目支持的静态链接方式处理。 |
||||||
|
|
||||||
|
## `sources`、`ignore` 和 `embedded-files` 的关系 |
||||||
|
|
||||||
|
三个配置项承担不同职责: |
||||||
|
|
||||||
|
| 配置 | 作用 | |
||||||
|
|---|---| |
||||||
|
| `sources` | 选择要翻译为 C++ 并编译为机器码的 PHP/C/C++ 文件。 | |
||||||
|
| `ignore` | 从 `sources` 的扫描结果中排除文件。 | |
||||||
|
| `embedded-files` | 选择要原样打包进可执行文件的文件,并为没有成功 AOT 编译的 PHP 文件生成 opcode。 | |
||||||
|
|
||||||
|
一个文件可以同时由 `sources` 和 `embedded-files` 选中: |
||||||
|
|
||||||
|
- 成功进入 AOT 的 PHP 文件使用原生实现,不再生成重复 opcode; |
||||||
|
- 被 `ignore` 排除或因不支持的语法而未进入 AOT 的 PHP 文件会进入 opcode 表; |
||||||
|
- `ignore` 不会从 `embedded-files` 中删除文件; |
||||||
|
- `.stub.php` 只作为 API 声明原样打包,不会生成可执行 opcode。 |
||||||
|
|
||||||
|
因此可以先让应用代码进入 `sources`,再将整个 `vendor` 放入 |
||||||
|
`embedded-files`。支持 AOT 的文件继续以机器码运行,其余依赖由 ZendVM 按需执行。 |
||||||
|
|
||||||
|
```yaml |
||||||
|
sources: |
||||||
|
- src |
||||||
|
- vendor/acme/optimized-package/src |
||||||
|
|
||||||
|
embedded-files: |
||||||
|
- vendor |
||||||
|
``` |
||||||
|
|
||||||
|
`embedded-files` 没有单独的排除列表。若不希望打包某个子目录,应列出实际需要的 |
||||||
|
目录或文件,而不是配置整个上级目录。 |
||||||
|
|
||||||
|
## 支持的配置格式 |
||||||
|
|
||||||
|
该配置只在 YAML 项目中显式启用,值必须是列表。每一项可以是文件、目录或带条件的 |
||||||
|
路径,相对路径以最外层项目 YAML 所在目录为基准。 |
||||||
|
|
||||||
|
```yaml |
||||||
|
embedded-files: |
||||||
|
- vendor |
||||||
|
- resources/app.json |
||||||
|
- path: resources/windows |
||||||
|
if: PHP_OS_FAMILY == "Windows" |
||||||
|
- path: resources/php85 |
||||||
|
if: PHP_VERSION_ID >= 80500 |
||||||
|
``` |
||||||
|
|
||||||
|
目录会被递归扫描,其中的所有普通文件都会进入归档。除 PHP 文件外,JSON、YAML、 |
||||||
|
模板和其他资源也会按原始字节保存。 |
||||||
|
|
||||||
|
运行时可以使用原来的路径读取已嵌入文件: |
||||||
|
|
||||||
|
```php |
||||||
|
$config = file_get_contents(__DIR__ . '/resources/app.json'); |
||||||
|
``` |
||||||
|
|
||||||
|
内嵌文件是只读的。程序产生的日志、缓存、上传文件和数据库应写入单独的运行时数据 |
||||||
|
目录。不要依赖对内嵌目录执行 `glob()` 或目录遍历;应使用已知文件路径读取资源。 |
||||||
|
|
||||||
|
## 构建环境要求 |
||||||
|
|
||||||
|
`embedded-files` 仅支持普通的 `mode: bin` 构建。它不适用于 `ext`、`lib`、Nano、 |
||||||
|
WASI、iOS 或 Android 目标。 |
||||||
|
|
||||||
|
构建机必须具备: |
||||||
|
|
||||||
|
1. 与目标 `libphp` 匹配的 `php` 或 `php.exe`; |
||||||
|
2. 可由该 CLI 加载的 Zend OPcache 扩展; |
||||||
|
3. 与最终运行时一致的 PHP 完整版本、ZTS/NTS、Debug 模式和整数宽度。 |
||||||
|
|
||||||
|
可以先检查构建 PHP: |
||||||
|
|
||||||
|
```bash |
||||||
|
php -r 'var_dump(PHP_VERSION, PHP_ZTS, extension_loaded("Zend OPcache"), function_exists("opcache_compile_file"));' |
||||||
|
``` |
||||||
|
|
||||||
|
TypePHP 自己也会用 `-n` 探测 OPcache,并尝试加载标准位置中的扩展。仅有 `tpc` |
||||||
|
可执行文件但没有匹配的 PHP CLI 和 OPcache 时,无法构建 `embedded-files`。 |
||||||
|
|
||||||
|
OPcache 只参与构建期序列化。运行生成的程序时: |
||||||
|
|
||||||
|
- 不需要启用或安装 OPcache 扩展; |
||||||
|
- 不需要 PHP CLI; |
||||||
|
- 不需要执行 `composer install`; |
||||||
|
- 不需要磁盘上的 `vendor/autoload.php` 或其他已嵌入文件。 |
||||||
|
|
||||||
|
二进制中的 opcode 与 PHP 完整版本绑定。若运行时 PHP 与生成 opcode 的 PHP 版本 |
||||||
|
不同,程序会在启动时报告版本不匹配,而不会继续执行不兼容的字节码。 |
||||||
|
|
||||||
|
## 构建输出和日志 |
||||||
|
|
||||||
|
首次构建会看到类似输出: |
||||||
|
|
||||||
|
```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 |
||||||
|
``` |
||||||
|
|
||||||
|
再次构建时,满足缓存条件的 Composer vendor opcode 会被复用: |
||||||
|
|
||||||
|
```text |
||||||
|
Vendor opcode cache: 2886 reused, 0 to generate |
||||||
|
Packed 3012 files and 2886 opcode blobs |
||||||
|
``` |
||||||
|
|
||||||
|
内部 opcode、归档和对象文件位于 `build-dir/cache`。生成的 |
||||||
|
`embedded-opcodes-<name>.cc` 只包含可读的索引代码,大块文件内容不会展开成 C++ |
||||||
|
数组。 |
||||||
|
|
||||||
|
OPcache 无法编译且预期不会被 `require` 的 PHP 文件会显示 |
||||||
|
`Skipping non-executable embedded PHP file`,文件原始内容仍会被打包。若程序实际会 |
||||||
|
加载该文件,应修复构建错误,不能忽略这条日志。 |
||||||
|
|
||||||
|
## vendor opcode 缓存 |
||||||
|
|
||||||
|
只有目录名为 `vendor` 且根目录存在 `autoload.php` 时,TypePHP 才启用持久 opcode |
||||||
|
缓存。缓存键包含: |
||||||
|
|
||||||
|
- vendor 根目录路径和目录 mtime; |
||||||
|
- PHP CLI 与 OPcache 二进制签名; |
||||||
|
- PHP 版本、ZTS/Debug 模式及整数宽度。 |
||||||
|
|
||||||
|
其他 `embedded-files` 目录每次构建都会重新生成 opcode,因为编译器无法确定它们的 |
||||||
|
可靠失效边界。 |
||||||
|
|
||||||
|
直接修改 vendor 中一个已有文件不一定改变 vendor 根目录 mtime。此时使用 |
||||||
|
`--force` 重新生成: |
||||||
|
|
||||||
|
```bash |
||||||
|
tpc project-release.yml --force |
||||||
|
``` |
||||||
|
|
||||||
|
Composer `install` 或 `update` 通常会重建根目录内容和 autoload 文件,但发布流程仍应 |
||||||
|
在依赖发生人工修改或缓存结果可疑时使用 `--force`。 |
||||||
|
|
||||||
|
## 性能和发布建议 |
||||||
|
|
||||||
|
嵌入大量 vendor 文件会增大最终二进制、链接输入和进程启动成本。对于频繁启动大量 |
||||||
|
短进程的 PHPT、单元测试或本地开发,这部分固定成本会很明显。因此建议: |
||||||
|
|
||||||
|
- 开发和测试使用不含 `embedded-files` 的 `project.yml`; |
||||||
|
- 只在交付构建使用 `project-release.yml`; |
||||||
|
- 保留同一个 `build-dir` 以复用 opcode、归档和对象缓存; |
||||||
|
- 修改 vendor 内容但缓存未失效时再使用 `--force`。 |
||||||
|
|
||||||
|
Composer autoload 仍是懒加载:只有代码首次引用某个类时,对应 vendor opcode 才由 |
||||||
|
ZendVM 执行。打包整个 vendor 不会在程序启动时执行全部 PHP 文件。 |
||||||
|
|
||||||
|
## 常见问题 |
||||||
|
|
||||||
|
### 为什么配置后仍然需要 Composer autoload? |
||||||
|
|
||||||
|
`embedded-files` 改变的是文件保存位置和 PHP 文件的编译来源,不会替代 Composer |
||||||
|
生成的类映射与 PSR-4 规则。程序仍然 `require_once` 同一个 |
||||||
|
`vendor/autoload.php`,只是该文件和后续类文件都从二进制内存表加载。 |
||||||
|
|
||||||
|
### 能否只把 `vendor/autoload.php` 放进去? |
||||||
|
|
||||||
|
不能。autoload 文件只包含加载规则,真正的包文件也必须存在于 `embedded-files` |
||||||
|
中。通常应直接配置整个 `vendor`。 |
||||||
|
|
||||||
|
### 运行主机需要 OPcache 吗? |
||||||
|
|
||||||
|
不需要。反序列化和执行支持已经随 PHPX 链入程序。OPcache 扩展只用于构建 opcode。 |
||||||
|
|
||||||
|
### 这是否能保护 PHP 源码? |
||||||
|
|
||||||
|
不要把它当作加密或源码保护功能。为了支持普通文件读取,归档中包含所选文件的原始 |
||||||
|
字节;能够分析二进制文件的人仍可能提取这些内容。 |
||||||
|
|
||||||
|
### vendor 代码依赖其他 PHP 扩展怎么办? |
||||||
|
|
||||||
|
`embedded-files` 只打包 PHP 文件,不会把 `curl`、`pdo_mysql` 等扩展实现打进程序。 |
||||||
|
应使用 `ext-deps` / `extension-dependencies` 声明依赖,并在目标运行时提供对应扩展。 |
||||||
|
|
||||||
|
```yaml |
||||||
|
ext-deps: |
||||||
|
- curl |
||||||
|
- pdo_mysql |
||||||
|
``` |
||||||
Loading…
Reference in new issue