This commit is contained in:
crazywhalecc
2026-04-19 11:49:55 +08:00
parent a175c5862d
commit a348e838d7
120 changed files with 6346 additions and 3391 deletions

View File

@@ -1,51 +1,5 @@
# 贡献指南
感谢你能够看到这里,本项目非常欢迎你的贡献!
## 贡献方法
如果你有代码或文档要贡献,以下是你需要首先了解的内容。
1. 你要贡献什么类型的代码?(新扩展、修复 Bug、安全问题、项目框架优化、文档
2. 如果你贡献了新文件或新片段,你的代码是否经过 `php-cs-fixer``phpstan` 的检查?
3. 在贡献代码前是否充分阅读了 [开发指南](../develop/)
如果你能回答上述问题并对代码进行了修改,可以及时在项目 GitHub 仓库发起 Pull Request。
代码审查完成后,可以根据建议修改代码,或直接合并到主分支。
## 贡献类型
本项目的主要目的是编译静态链接的 PHP 二进制文件,命令行处理功能基于 `symfony/console` 编写。
在开发之前,如果你对它不够熟悉,请先查看 [symfony/console 文档](https://symfony.com/doc/current/components/console.html)。
### 安全更新
因为本项目基本上是一个本地运行的 PHP 项目,一般来说不会有远程攻击。
但如果你发现此类问题,请**不要**在 GitHub 仓库提交 PR 或 Issue
你需要通过 [邮件](mailto:admin@zhamao.me) 联系项目维护者crazywhalecc
### 修复 Bug
修复 Bug 一般不涉及项目结构和框架的修改,所以如果你能定位到错误代码并直接修复它,请直接提交 PR。
### 新扩展
对于添加新扩展,你需要了解项目的一些基本结构以及如何根据现有逻辑添加新扩展。
这将在本页的下一节中详细介绍。
总的来说,你需要:
1. 评估扩展是否可以内联编译到 PHP 中。
2. 评估扩展的依赖库(如果有)是否可以静态编译。
3. 编写不同平台的库编译命令。
4. 验证扩展及其依赖项与现有扩展和依赖项兼容。
5. 验证扩展在 `cli``micro``fpm``embed` SAPIs 中正常工作。
6. 编写文档并添加你的扩展。
### 项目框架优化
如果你已经熟悉 `symfony/console` 的工作原理,并同时要对项目的框架进行一些修改或优化,请先了解以下事情:
1. 添加扩展不属于项目框架优化,但如果你在添加新扩展时发现必须优化框架,则需要先修改框架本身,然后再添加扩展。
2. 对于一些大规模逻辑修改(例如涉及 LibraryBase、Extension 对象等的修改),建议先提交 Issue 或 Draft PR 进行讨论。
3. 在项目早期,它是一个纯私有开发项目,代码中有一些中文注释。项目国际化后,你可以提交 PR 将这些注释翻译为英语。
4. 请不要在代码中提交更多无用的代码片段,例如大量未使用的变量、方法、类以及多次重写的代码。
<!-- TODO: v3 贡献指南。
章节代码风格php-cs-fixer、phpstan、新增 library/extension、
新增 doctor 检查项、提交 PR、安全漏洞披露。 -->

View File

@@ -0,0 +1,5 @@
# 构建生命周期
<!-- TODO: 完整构建流水线阶段顺序说明。
各 Hook 触发时机:#[BeforeStage]、#[AfterStage]、#[PatchBeforeBuild]。
#[BuildFor] 运行时平台选择机制。典型 library 和 extension 构建的阶段顺序图示。 -->

View File

@@ -2,6 +2,6 @@
aside: false
---
# craft.yml 配置
# craft.yml 配置详解
<!--@include: ../../deps-craft-yml.md-->
<!-- TODO: craft.yml 字段完整参考。 -->

View File

@@ -1,60 +1,4 @@
# Doctor 模块
# Doctor 环境检查
Doctor 模块是一个较为独立的用于检查系统环境的模块,可使用命令 `bin/spc doctor` 进入,入口的命令类在 `DoctorCommand.php`
Doctor 模块是一个检查单,里面有一系列的检查项目和自动修复项目。这些项目都存放在 `src/SPC/doctor/item/` 目录中,
并且使用了两种 Attribute 用作检查项标记和自动修复项目标记:`#[AsCheckItem]``#[AsFixItem]`
以现有的检查项 `if necessary tools are installed`,它是用于检查编译必需的包是否安装在 macOS 系统内,下面是它的源码:
```php
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
#[AsCheckItem('if necessary tools are installed', limit_os: 'Darwin', level: 997)]
public function checkCliTools(): ?CheckResult
{
$missing = [];
foreach (self::REQUIRED_COMMANDS as $cmd) {
if ($this->findCommand($cmd) === null) {
$missing[] = $cmd;
}
}
if (!empty($missing)) {
return CheckResult::fail('missing system commands: ' . implode(', ', $missing), 'build-tools', [$missing]);
}
return CheckResult::ok();
}
```
属性的第一个参数就是检查项目的名称,后面的 `limit_os` 参数是限制了该检查项仅在指定的系统下触发,`level` 是执行该检查项的优先级,数字越大,优先级越高。
里面用到的 `$this->findCommand()` 方法为 `SPC\builder\traits\UnixSystemUtilTrait` 的方法,用途是查找系统命令所在位置,找不到时返回 NULL。
每个检查项的方法都应该返回一个 `SPC\doctor\CheckResult`
- 在返回 `CheckResult::fail()` 时,第一个参数用于输出终端的错误提示,第二个参数是在这个检查项可自动修复时的修复项目名称。
- 在返回 `CheckResult::ok()` 时,表明检查通过。你也可以传递一个参数,用于返回检查结果,例如:`CheckResult::ok('OS supported')`
- 在返回 `CheckResult::fail()` 时,如果包含了第三个参数,第三个参数的数组将被当作 `AsFixItem` 的参数。
下面是这个检查项对应的自动修复项的方法:
```php
#[AsFixItem('build-tools')]
public function fixBuildTools(array $missing): bool
{
foreach ($missing as $cmd) {
try {
shell(true)->exec('brew install ' . escapeshellarg($cmd));
} catch (RuntimeException) {
return false;
}
}
return true;
}
```
`#[AsFixItem()]` 属性传入的参数即修复项的名称,该方法必须返回 True 或 False。当返回 False 时,表明自动修复失败,需要手动处理。
此处的代码中 `shell()->exec()` 是项目的执行命令的方法,用于替代 `exec()``system()`,同时提供了 debug、获取执行状态、进入目录等特性。
<!-- TODO: 从 v2 doctor-module.md 迁移并更新
涵盖 v3 变化:--auto-fix、.spc-doctor.lock、v3 工具链新增检查项。 -->

View File

@@ -1,27 +1,4 @@
# 开发简介
开发本项目需要安装部署 PHP 环境,以及一些 PHP 项目常用的扩展和 Composer
项目的开发环境和运行环境几乎完全一致。你可以参照 **手动构建** 部分安装系统 PHP 或使用本项目预构建的静态 PHP 作为环境。这里不再赘述。
抛开用途,本项目本身其实就是一个 `php-cli` 程序,你可以将它当作一个正常的 PHP 项目进行编辑和开发,同时你需要了解不同系统的 Shell 命令行。
本项目目前的目的就是为了编译静态编译的独立 PHP但主体部分也包含编译很多依赖库的静态版本所以你可以复用这套编译逻辑用于构建其他程序的独立二进制版本例如 Nginx 等。
## 环境准备
开发本项目需要 PHP 环境。你可以使用系统自带的 PHP也可以使用本项目构建的静态 PHP。
无论是使用哪种 PHP在开发环境你需要安装这些扩展
```
curl,dom,filter,mbstring,openssl,pcntl,phar,posix,sodium,tokenizer,xml,xmlwriter
```
static-php-cli 项目本身不需要这么多扩展,但在开发过程中,你会用到 Composer 和 PHPUnit 等工具,它们需要这些扩展。
> 对于 static-php-cli 自身构建的 micro 自执行二进制,仅需要 `pcntl,posix,mbstring,tokenizer,phar`。
## 开始开发
继续向下查看项目结构文档,你可以学习 `static-php-cli` 是如何工作的。
<!-- TODO: 开发者简介、环境准备、所需 PHP 扩展
Vendor 模式链接到 vendor-mode/,代码贡献链接到 contributing/。 -->

View File

@@ -0,0 +1,5 @@
# Package 模型
<!-- TODO: 统一 Package 模型说明library / php-extension / target 类型。
config/pkg/ 下 per-package YAML 格式、depends 字段、平台覆盖(@windows / @unix 写法)。
artifact.source 和 artifact.binary 字段。附注释的 library 和 extension YAML 示例。 -->

View File

@@ -1,51 +1,3 @@
# 对 PHP 源码的修改
由于 static-php-cli 在静态编译过程中为了实现良好的兼容性、性能和安全性,对 PHP 源码进行了一些修改。下面是目前对 PHP 源码修改的说明。
## micro 相关补丁
基于 phpmicro 项目提供的补丁static-php-cli 对 PHP 源码进行了一些修改,以适应静态编译的需求。[补丁列表](https://github.com/easysoft/phpmicro/tree/master/patches) 包含:
目前 static-php-cli 在编译时用到的补丁有:
- static_opcache
- static_extensions_win32
- cli_checks
- disable_huge_page
- vcruntime140
- win32
- zend_stream
- cli_static
- macos_iconv
- phar
## PHP <= 8.1 libxml 补丁
因为 PHP 官方仅对 8.1 进行安全更新,旧版本停止更新,所以 static-php-cli 对 PHP 8.1 及以下版本应用了在新版本 PHP 中已经应用的 libxml 编译补丁。
## gd 扩展 Windows 补丁
在 Windows 下编译 gd 扩展需要大幅改动 `config.w32` 文件static-php-cli 对 gd 扩展进行了一些修改,使其在 Windows 下编译更加方便。
## yaml 扩展 Windows 补丁
yaml 扩展在 Windows 下编译需要修改 `config.w32` 文件static-php-cli 对 yaml 扩展进行了一些修改,使其在 Windows 下编译更加方便。
## static-php-cli 版本信息插入
static-php-cli 在编译时会在 PHP 版本信息中插入 static-php-cli 的版本信息,以便于识别。
## 加入硬编码 INI 的选项
在使用 `-I` 参数硬编码 INI 到静态 PHP 的功能中static-php-cli 会修改 PHP 源码以插入硬编码内容。
## Linux 系统修复补丁
部分编译环境可能缺少一些头文件或库static-php-cli 会在编译时自动修复这些问题,如:
- HAVE_STRLCAT missing problem
- HAVE_STRLCPY missing problem
## Windows 系统下 Fiber 问题修复补丁
在 Windows 下编译 PHP 时Fiber 扩展会出现一些问题static-php-cli 会在编译时自动修复这些问题(修改 php-src 的 `config.w32`)。
<!-- TODO: 从 v2 php-src-changes.md 迁移并更新。补充 v3 新增 patchFrankenPHP embed、Windows fiber 修复等)。 -->

View File

@@ -0,0 +1,5 @@
# Registry 与插件系统
<!-- TODO: spc.registry.yml 结构说明。
通过 SPC_REGISTRIES 环境变量添加外部 Registry。
Vendor 特定配置、覆盖核心包。Registry 解析顺序与冲突规则。 -->

View File

@@ -1,350 +1,5 @@
# 资源模块
static-php-cli 的下载资源模块是一个主要的功能它包含了所依赖的库、外部扩展、PHP 源码的下载方式和资源解压方式
下载的配置文件主要涉及 `source.json``pkg.json` 文件,这个文件记录了所有可下载的资源的下载方式
下载功能主要涉及的命令有 `bin/spc download``bin/spc extract`。其中 `download` 命令是一个下载器,它会根据配置文件下载资源;
`extract` 命令是一个解压器,它会根据配置文件解压资源。
一般来说下载资源可能会比较慢因为这些资源来源于各个官网、GitHub 等不同位置,同时它们也占用了较大空间,所以你可以在一次下载资源后,可重复使用。
下载器的配置文件是 `source.json`,它包含了所有资源的下载方式,你可以在其中添加你需要的资源下载方式,也可以修改已有的资源下载方式。
每个资源的下载配置结构如下,下面是 `libevent` 扩展对应的资源下载配置:
```json
{
"libevent": {
"type": "ghrel",
"repo": "libevent/libevent",
"match": "libevent.+\\.tar\\.gz",
"provide-pre-built": true,
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
这里最主要的字段是 `type`,目前它支持的类型有:
- `url`: 直接使用 URL 下载,例如:`https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`
- `pie`: 使用 PIEPHP Installer for Extensions标准从 Packagist 下载 PHP 扩展。
- `ghrel`: 使用 GitHub Release API 下载,即从 GitHub 项目发布的最新版本中上传的附件下载。
- `ghtar`: 使用 GitHub Release API 下载,与 `ghrel` 不同的是,`ghtar` 是从项目的最新 Release 中找 `source code (tar.gz)` 下载的。
- `ghtagtar`: 使用 GitHub Release API 下载,与 `ghtar` 相比,`ghtagtar` 可以从 `tags` 列表找最新的,并下载 `tar.gz` 格式的源码(因为有些项目只使用了 `tag` 发布版本)。
- `bitbuckettag`: 使用 BitBucket API 下载,基本和 `ghtagtar` 相同,只是这个适用于 BitBucket。
- `git`: 直接从一个 Git 地址克隆项目来下载资源,适用于任何公开 Git 仓库。
- `filelist`: 使用爬虫爬取提供文件索引的 Web 下载站点,并获取最新版本的文件名并下载。
- `custom`: 如果以上下载方式都不能满足,你可以编写 `custom` 后,在 `src/SPC/store/source/` 下新建一个类,并继承 `CustomSourceBase`,自己编写下载脚本。
## source.json 通用参数
source.json 中每个源文件拥有以下字段:
- `license`: 源代码的开源许可证,见下方 **开源许可证** 章节
- `type`: 必须为上面提到的类型之一
- `path`(可选): 释放源码到指定目录而非 `source/{name}`
- `provide-pre-built`(可选): 是否提供预编译的二进制文件,如果为 `true`,则会在 `bin/spc download` 时尝试自动下载预编译的二进制文件
::: tip
`source.json` 中的 `path` 参数可指定相对路径或绝对路径。当指定为相对路径时,路径基于 `source/`
:::
## 下载类型 - url
url 类型的资源指的是从 URL 直接下载文件。
包含的参数有:
- `url`: 文件的下载地址,如 `https://example.com/file.tgz`
- `filename`(可选): 保存到本地的文件名,如不指定,则使用 url 的文件名
例子(下载 imagick 扩展,并解压缩到 php 源码的扩展存放路径):
```json
{
"ext-imagick": {
"type": "url",
"url": "https://pecl.php.net/get/imagick",
"path": "php-src/ext/imagick",
"filename": "imagick.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## 下载类型 - pie
PIEPHP Installer for Extensions类型的资源是从 Packagist 下载遵循 PIE 标准的 PHP 扩展。
该方法会自动从 Packagist 仓库获取扩展信息,并下载相应的分发文件。
包含的参数有:
- `repo`: Packagist 的 vendor/package 名称,如 `vendor/package-name`
例子(使用 PIE 从 Packagist 下载 PHP 扩展):
```json
{
"ext-example": {
"type": "pie",
"repo": "vendor/example-extension",
"path": "php-src/ext/example",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
::: tip
PIE 下载类型会自动从 Packagist 元数据中检测扩展信息,包括下载 URL、版本和分发类型。
扩展必须在其 Packagist 包定义中标记为 `type: php-ext` 或包含 `php-ext` 元数据。
:::
## 下载类型 - ghrel
ghrel 会从 GitHub Release 中上传的 Assets 下载文件。首先使用 GitHub Release API 获取最新版本,然后根据正则匹配方式下载相应的文件。
包含的参数有:
- `repo`: GitHub 仓库名称
- `match`: 匹配 Assets 文件的正则表达式
- `prefer-stable`: 是否优先下载稳定版本(默认为 `false`
例子(下载 libsodium 库,匹配 Release 中的 libsodium-x.y.tar.gz 文件):
```json
{
"libsodium": {
"type": "ghrel",
"repo": "jedisct1/libsodium",
"match": "libsodium-\\d+(\\.\\d+)*\\.tar\\.gz",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## 下载类型 - ghtar
ghtar 会从 GitHub Release Tag 下载文件,与 `ghrel` 不同的是,`ghtar` 是从项目的最新 Release 中找 `source code (tar.gz)` 下载的。
包含的参数有:
- `repo`: GitHub 仓库名称
- `prefer-stable`: 是否优先下载稳定版本(默认为 `false`
例子brotli 库):
```json
{
"brotli": {
"type": "ghtar",
"repo": "google/brotli",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## 下载类型 - ghtagtar
使用 GitHub Release API 下载,与 `ghtar` 相比,`ghtagtar` 可以从 `tags` 列表找最新的,并下载 `tar.gz` 格式的源码(因为有些项目只使用了 `tag` 发布版本)。
包含的参数有:
- `repo`: GitHub 仓库名称
- `prefer-stable`: 是否优先下载稳定版本(默认为 `false`
例子gmp 库):
```json
{
"gmp": {
"type": "ghtagtar",
"repo": "alisw/GMP",
"license": {
"type": "text",
"text": "EXAMPLE LICENSE"
}
}
}
```
## 下载类型 - bitbuckettag
使用 BitBucket API 下载,基本和 `ghtagtar` 相同,只是这个适用于 BitBucket。
包含的参数有:
- `repo`: BitBucket 仓库名称
## 下载类型 - git
直接从一个 Git 地址克隆项目来下载资源,适用于任何公开 Git 仓库。
包含的参数有:
- `url`: Git 链接(仅限 HTTPS
- `rev`: 分支名称
```json
{
"imap": {
"type": "git",
"url": "https://github.com/static-php/imap.git",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## 下载类型 - filelist
使用爬虫爬取提供文件索引的 Web 下载站点,并获取最新版本的文件名并下载。
注意该方法仅限于镜像站、GNU 官网等具有页面 index 功能的静态站点使用。
包含的参数有:
- `url`: 要爬取文件最新版本的页面 URL
- `regex`: 匹配文件名及下载链接的正则表达式
例子(从 GNU 官网下载 libiconv 库):
```json
{
"libiconv": {
"type": "filelist",
"url": "https://ftp.gnu.org/gnu/libiconv/",
"regex": "/href=\"(?<file>libiconv-(?<version>[^\"]+)\\.tar\\.gz)\"/",
"license": {
"type": "file",
"path": "COPYING"
}
}
}
```
## 下载类型 - custom
如果以上下载方式都不能满足,你可以编写 `custom` 后,在 `src/SPC/store/source/` 下新建一个类,并继承 `CustomSourceBase`,自己编写下载脚本。
这里不再赘述,你可以查看 `src/SPC/store/source/PhpSource.php``src/SPC/store/source/PostgreSQLSource.php` 作为例子。
## pkg.json 通用参数
pkg.json 存放的是非源码类型的文件资源,例如 musl-toolchain、UPX 等预编译的工具。它的使用包含:
- `type`: 与 `source.json` 相同的类型及不同种类的参数。
- `extract`(可选): 下载后解压缩的路径,默认为 `pkgroot/{pkg_name}`
- `extract-files`(可选): 下载后仅解压指定的文件到指定位置。
需要注意的是,`pkg.json` 不涉及源代码的编译和修改分发,所以没有 `license` 开源许可证字段。并且你不能同时使用 `extract``extract-files` 参数。
例子(下载 nasm 到本地,并只提取程序文件到 PHP SDK
```json
{
"nasm-x86_64-win": {
"type": "url",
"url": "https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-2.16.01-win64.zip",
"extract-files": {
"nasm-2.16.01/nasm.exe": "{php_sdk_path}/bin/nasm.exe",
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
}
}
}
```
`extract-files` 中的键名为源文件夹下的文件,键值为存放的路径。存放的路径可以使用以下变量:
- `{php_sdk_path}`: (仅限 WindowsPHP SDK 路径
- `{pkg_root_path}`: `pkgroot/`
- `{working_dir}`: 当前工作目录
- `{download_path}`: 下载目录
- `{source_path}`: 源码解压缩目录
`extract-files` 不使用变量且为相对路径时,相对路径的目录为 `{working_dir}`
## 开源许可证
对于 `source.json` 而言,每个源文件都应包含开源许可证。`license` 字段存放了开源许可证的信息。
每个 `license` 包含的参数有:
- `type`: `file``text`
- `path`: 源代码目录中的许可证文件(当 `type``file` 时,此项必填)
- `text`: 许可证文本(当 `type``text` 时,此项必填)
例子yaml 扩展的源代码中带有 LICENSE 文件):
```json
{
"yaml": {
"type": "git",
"path": "php-src/ext/yaml",
"rev": "php7",
"url": "https://github.com/php/pecl-file_formats-yaml",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
当开源项目拥有多个许可证时,可指定多个文件:
```json
{
"libuv": {
"type": "ghtar",
"repo": "libuv/libuv",
"license": [
{
"type": "file",
"path": "LICENSE"
},
{
"type": "file",
"path": "LICENSE-extra"
}
]
}
}
```
当一个开源项目的许可证在不同版本间使用不同的文件,`path` 参数可以使用数组将可能的许可证文件列出:
```json
{
"redis": {
"type": "git",
"path": "php-src/ext/redis",
"rev": "release/6.0.2",
"url": "https://github.com/phpredis/phpredis",
"license": {
"type": "file",
"path": [
"LICENSE",
"COPYING"
]
}
}
}
```
<!-- TODO: 从 v2 source-module.md 迁移并更新
记录 v3 source 类型url、ghrel、ghtar、ghtagtar、git、pecl新增、filelist、custom
per-package YAML source 块格式。并行下载(--parallel N。 -->

View File

@@ -1,163 +1,4 @@
# 项目结构简介
# 项目结构
static-php-cli 主要包含三种逻辑组件:资源、依赖库、扩展。这三种组件四个配置文件:`source.json``lib.json``ext.json``pkg.json`
一个完整的构建静态 PHP 流程是:
1. 使用资源下载模块 `Downloader` 下载指定或所有资源,这些资源包含 PHP 源码、依赖库源码、扩展源码。
2. 使用资源解压模块 `SourceExtractor` 解压下载的资源到编译目录。
3. 使用依赖工具计算出当前加入的扩展的依赖扩展、依赖库,然后对每个需要编译的依赖库进行编译,按照依赖顺序。
4. 使用对应操作系统下的 `Builder` 构建每个依赖库后,将其安装到 `buildroot` 目录。
5. 如果包含外部扩展(源码没有包含在 PHP 内的扩展),将外部扩展拷贝到 `source/php-src/ext/` 目录。
6. 使用 `Builder` 构建 PHP 源码,将其安装到 `buildroot` 目录。
项目主要分为几个文件夹:
- `bin/`: 用于存放程序入口文件,包含 `bin/spc``bin/spc-alpine-docker``bin/setup-runtime`
- `config/`: 包含了所有项目支持的扩展、依赖库以及这些资源下载的地址、下载方式等,:`lib.json``ext.json``source.json``pkg.json``pre-built.json`
- `src/SPC/`: 项目的核心代码,包含了整个框架以及编译各种扩展和库的命令。
- `src/globals/`: 项目的全局方法和常量、运行时需要的测试文件(例如:扩展的可用性检查代码)。
- `vendor/`: Composer 依赖的目录,你无需对它做出任何修改。
其中运行原理就是启动一个 `symfony/console``ConsoleApplication`,然后解析用户在终端输入的命令。
## 基本命令行结构
`bin/spc` 是一个 PHP 代码入口文件,包含了 Unix 通用的 `#!/usr/bin/env php` 用来让系统自动以系统安装好的 PHP 解释器执行。
在项目执行了 `new ConsoleApplication()` 后,框架会自动使用反射的方式,解析 `src/SPC/command` 目录下的所有类,并将其注册成为命令。
项目并没有直接使用 Symfony 推荐的 Command 注册方式和命令执行方式,这里做出了一点小变动:
1. 每个命令都使用 `#[AsCommand()]` Attribute 来注册名称和简介。
2.`execute()` 抽象化,让所有命令基于 `BaseCommand`(它基于 `Symfony\Component\Console\Command\Command`),每个命令本身的执行代码写到了 `handle()` 方法中。
3. `BaseCommand` 添加了变量 `$no_motd`,用于是否在该命令执行时显示 Figlet 欢迎词。
4. `BaseCommand``InputInterface``OutputInterface` 保存为成员变量,你可以在命令的类内使用 `$this->input``$this->output`
## 基本源码结构
项目的源码位于 `src/SPC` 目录,支持 PSR-4 标准的自动加载,包含以下子目录和类:
- `src/SPC/builder/`: 用于不同操作系统下构建依赖库、PHP 及相关扩展的核心编译命令代码,还包含了一些编译的系统工具方法。
- `src/SPC/command/`: 项目的所有命令都在这里。
- `src/SPC/doctor/`: Doctor 模块,它是一个较为独立的用于检查系统环境的模块,可使用命令 `bin/spc doctor` 进入。
- `src/SPC/exception/`: 异常类。
- `src/SPC/store/`: 有关存储、文件和资源的类都在这里。
- `src/SPC/util/`: 一些可以复用的工具方法都在这里。
- `src/SPC/ConsoleApplication.php`: 命令行程序入口文件。
如果你阅读过源码,你可能会发现还有一个 `src/globals/` 目录,它是用于存放一些全局变量、全局方法、构建过程中依赖的非 PSR-4 标准的 PHP 源码,例如测试扩展代码等。
## Phar 应用目录问题
和其他 php-cli 项目一样spc 自身对路径有额外的考虑。
因为 spc 可以在 `php-cli directly``micro SAPI``php-cli with Phar``vendor with Phar` 等多种模式下运行,各类根目录存在歧义。这里会进行一个完整的说明。
此问题一般常见于 PHP 项目中存取文件的基类路径选择问题,尤其是在配合 `micro.sfx` 使用时容易出现路径问题。
注意,此处仅对你在开发 Phar 项目或 PHP 框架时可能有用。
> 接下来我们都将 `static-php-cli`(也就是 spc当作一个普通的 `php` 命令行程序来看,你可以将 spc 理解为你自己的任何 php-cli 应用以参考。
下面主要有三个基本的常量理论值,我们建议你在编写 php 项目时引入这三种常量:
- `WORKING_DIR`:执行 PHP 脚本时的工作目录
- `SOURCE_ROOT_DIR``ROOT_DIR`:项目文件夹的根目录,一般为 `composer.json` 所在目录
- `FRAMEWORK_ROOT_DIR`:使用框架的根目录,自行开发的框架可能会用到,一般框架目录为只读
你可以在你的框架或者 cli 应用程序入口中定义这些常量,以方便在你的项目中使用路径。
下面是 PHP 内置的常量值,在 PHP 解释器内部已被定义:
- `__DIR__`:当前执行脚本的文件所在目录
- `__FILE__`:当前执行脚本的文件路径
### Git 项目模式source
Git 项目模式指的是一个框架或程序本身在当前文件夹以纯文本形式存放,运行通过 `php path/to/entry.php` 方式。
假设你的项目存放在 `/home/example/static-php-cli/` 目录下,或你的项目就是框架本身,里面包含 `composer.json` 等项目文件:
```
composer.json
src/App/MyCommand.app
vendor/*
bin/entry.php
```
我们假设从 `src/App/MyCommand.php` 中获取以上常量:
| Constant | Value |
|----------------------|------------------------------------------------------|
| `WORKING_DIR` | `/home/example/static-php-cli` |
| `SOURCE_ROOT_DIR` | `/home/example/static-php-cli` |
| `FRAMEWORK_ROOT_DIR` | `/home/example/static-php-cli` |
| `__DIR__` | `/home/example/static-php-cli/src/App` |
| `__FILE__` | `/home/example/static-php-cli/src/App/MyCommand.php` |
这种情况下,`WORKING_DIR``SOURCE_ROOT_DIR``FRAMEWORK_ROOT_DIR` 的值是完全一致的:`/home/example/static-php-cli`
框架的源码和应用的源码都在当前路径下。
### Vendor 库模式vendor
Vendor 库模式一般是指你的项目为框架类或者被其他应用作为 composer 依赖项安装到项目中,存放位置在 `vendor/author/XXX` 目录。
假设你的项目是 `crazywhalecc/static-php-cli`,你或其他人在另一个项目使用 `composer require` 安装了这个项目。
我们假设 static-php-cli 中包含同 `Git 模式` 的除 `vendor` 目录外的所有文件,并从 `src/App/MyCommand` 中获取常量值,
目录常量应该是:
| Constant | Value |
|----------------------|--------------------------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/another-app` |
| `SOURCE_ROOT_DIR` | `/home/example/another-app` |
| `FRAMEWORK_ROOT_DIR` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli` |
| `__DIR__` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli/src/App` |
| `__FILE__` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli/src/App/MyCommand.php` |
这里的 `SOURCE_ROOT_DIR` 就指的是使用 `static-php-cli` 的项目的根目录。
### Git 项目 Phar 模式source-phar
Git 项目 Phar 模式指的是将 Git 项目模式的项目目录打包为一个 `phar` 文件的模式。我们假设 `/home/example/static-php-cli` 将打包为一个 Phar 文件,目录有以下文件:
```
composer.json
src/App/MyCommand.app
vendor/*
bin/entry.php
```
打包为 `app.phar` 并存放到 `/home/example/static-php-cli` 目录下时,此时执行 `app.phar`,假设执行了 `src/App/MyCommand` 代码,常量在该文件内获取:
| Constant | Value |
|----------------------|----------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/static-php-cli` |
| `SOURCE_ROOT_DIR` | `phar:///home/example/static-php-cli/app.phar/` |
| `FRAMEWORK_ROOT_DIR` | `phar:///home/example/static-php-cli/app.phar/` |
| `__DIR__` | `phar:///home/example/static-php-cli/app.phar/src/App` |
| `__FILE__` | `phar:///home/example/static-php-cli/app.phar/src/App/MyCommand.php` |
因为在 phar 内读取自身 phar 的文件需要 `phar://` 协议进行,所以项目根目录和框架目录将会和 `WORKING_DIR` 不同。
### Vendor 库 Phar 模式vendor-phar
Vendor 库 Phar 模式指的是你的项目作为框架安装在其他项目内,存储于 `vendor` 目录下。
我们假设你的项目目录结构如下:
```
composer.json # 当前项目的 Composer 配置文件
box.json # 打包 Phar 的配置文件
another-app.php # 另一个项目的入口文件
vendor/crazywhalecc/static-php-cli/* # 你的项目被作为依赖库
```
将该目录 `/home/example/another-app/` 下的这些文件打包为 `app.phar` 时,对于你的项目而言,下面常量的值应为:
| Constant | Value |
|----------------------|------------------------------------------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/another-app` |
| `SOURCE_ROOT_DIR` | `phar:///home/example/another-app/app.phar/` |
| `FRAMEWORK_ROOT_DIR` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli` |
| `__DIR__` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli/src/App` |
| `__FILE__` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli/src/App/MyCommand.php` |
<!-- TODO: v3 目录结构说明bin/、config/pkg/、src/StaticPHP/、src/Package/ 等)
每个顶层目录的职责说明。内部类结构简述,深入内容见核心概念页。 -->

View File

@@ -1,204 +1,4 @@
# 系统编译工具
# 编译工具
static-php-cli 在构建静态 PHP 时使用了许多系统编译工具,这些工具主要包括:
- `autoconf`: 用于生成 `configure` 脚本。
- `make`: 用于执行 `Makefile`
- `cmake`: 用于执行 `CMakeLists.txt`
- `pkg-config`: 用于查找依赖库的安装路径。
- `gcc`: 用于在 Linux 下编译 C/C++ 语言代码。
- `clang`: 用于在 macOS 下编译 C/C++ 语言代码。
对于 Linux 和 macOS 操作系统,这些工具通常可以通过包管理安装,这部分在 doctor 模块中编写了。
理论上我们也可以通过编译和手动下载这些工具,但这样会增加编译的复杂度,所以我们不推荐这样做。
## Linux 环境编译工具
对于 Linux 系统来说,不同发行版的编译工具安装方式不同。而且对于静态编译来说,某些发行版的包管理无法安装用于纯静态编译的库和工具,
所以对于 Linux 平台及其不同发行版,我们目前提供了多种编译环境的部署措施。
### glibc 环境
glibc 环境指的是系统底层的 `libc` 库(即所有 C 语言编写的程序动态链接的 C 标准库)使用的是 `glibc`,这是大多数发行版的默认环境。
例如Ubuntu、Debian、CentOS、RHEL、openSUSE、Arch Linux 等。
而 glibc 环境下,我们使用的包管理、编译器都是默认指向 glibc 的glibc 不能被良好地静态链接。它不能被静态链接的原因之一是它的网络库 `nss` 无法静态编译。
对于 glibc 环境,在 2.0 RC8 及以后的 static-php-cli 及 spc 中,你可以选择两种方式来构建静态 PHP
1. 使用 Docker 构建,这是最简单的方式,你可以使用 `bin/spc-alpine-docker` 来构建,它会在 Alpine Linux 环境下构建。
2. 使用 `bin/spc doctor` 安装 musl-wrapper 和 musl-cross-make 套件,然后直接正常构建。([相关源码](https://github.com/crazywhalecc/static-php-cli/blob/main/src/SPC/doctor/item/LinuxMuslCheck.php)
一般来说,这两种构建方式的构建结果是一致的,你可以根据实际需求选择。
在 doctor 模块中static-php-cli 会先检测当前的 Linux 发行版。如果当前发行版是 glibc 环境,会提示需要安装 musl-wrapper 和 musl-cross-make 套件。
在 glibc 环境下安装 musl-wrapper 的过程如下:
1. 从 musl 官网下载特定版本的 [musl-wrapper 源码](https://musl.libc.org/releases/)。
2. 使用从包管理安装的 `gcc` 编译 musl-wrapper 源码,生成 `musl-libc` 等库:`./configure --disable-gcc-wrapper && make -j && sudo make install`
3. musl-wrapper 相关库将被安装在 `/usr/local/musl` 目录。
在 glibc 环境下安装 musl-cross-make 的过程如下:
1. 从 dl.static-php.dev 下载预编译好的 [musl-cross-make](https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/) 压缩包。
2. 解压到 `/usr/local/musl` 目录。
::: tip
在 glibc 环境下,静态编译可以通过直接安装 musl-wrapper 来实现,但是 musl-wrapper 仅包含了 `musl-gcc`,而没有 `musl-g++`,这也就意味着无法编译 C++ 代码。
所以我们需要 musl-cross-make 来提供 `musl-g++`
而 musl-cross-make 套件无法在本地直接编译的原因是它的编译环境要求比较高(需要 36GB 以上内存Alpine Linux 下编译),所以我们提供了预编译好的二进制包,可用于所有 Linux 发行版。
同时,部分发行版的包管理提供了 musl-wrapper但 musl-cross-make 需要匹配对应的 musl-wrapper 版本,所以我们不使用包管理安装 musl-wrapper。
对于如何编译 musl-cross-make将在本章节内的 **编译 musl-cross-make** 小节中介绍。
:::
### musl 环境
musl 环境指的是系统底层的 `libc` 库使用的是 `musl`,这是一种轻量级的 C 标准库,它的特点是可以被良好地静态链接。
对于目前流行的 Linux 发行版Alpine Linux 使用的就是 musl 环境,所以 static-php-cli 在 Alpine Linux 下可以直接构建静态 PHP仅需直接从包管理安装基础编译工具如 gcc、cmake 等)即可。
对于其他发行版,如果你的发行版使用的是 musl 环境,那么你也可以在安装必要的编译工具后直接使用 static-php-cli 构建静态 PHP。
::: tip
在 musl 环境下static-php-cli 会自动跳过 musl-wrapper 和 musl-cross-make 的安装。
:::
### Docker 环境
Docker 环境指的是使用 Docker 容器来构建静态 PHP你可以使用 `bin/spc-alpine-docker` 来构建。
执行这个命令前需要先安装 Docker然后在项目根目录执行 `bin/spc-alpine-docker` 即可。
在执行 `bin/spc-alpine-docker`static-php-cli 会自动下载 Alpine Linux 镜像,然后构建一个 `cwcc-spc-x86_64``cwcc-spc-aarch64` 的镜像。
然后一切的构建都在这个镜像内进行,相当于在 Alpine Linux 内编译。总的来说Docker 环境就是 musl 环境。
## musl-cross-make 工具链编译
在 Linux 中,尽管你不需要手动编译 musl-cross-make 工具,但是如果你想了解它的编译过程,可以参考这里。
还有一个重要的原因就是,这个可能无法使用 CI、Actions 等自动化工具编译,因为现有的 CI 服务编译环境不满足 musl-cross-make 的编译要求,满足要求的配置价格太高。
musl-cross-make 的编译过程如下:
准备一个 Alpine Linux 环境(直接安装或使用 Docker 均可),编译的过程需要 36GB 以上内存,所以你需要在内存较大的机器上编译。如果没有这么多内存,可能会导致编译失败。
然后将以下内容写入 `config.mak` 文件内:
```makefile
STAT = -static --static
FLAG = -g0 -Os -Wno-error
ifneq ($(NATIVE),)
COMMON_CONFIG += CC="$(HOST)-gcc ${STAT}" CXX="$(HOST)-g++ ${STAT}"
else
COMMON_CONFIG += CC="gcc ${STAT}" CXX="g++ ${STAT}"
endif
COMMON_CONFIG += CFLAGS="${FLAG}" CXXFLAGS="${FLAG}" LDFLAGS="${STAT}"
BINUTILS_CONFIG += --enable-gold=yes --enable-gprofng=no
GCC_CONFIG += --enable-static-pie --disable-cet --enable-default-pie
#--enable-default-pie
CONFIG_SUB_REV = 888c8e3d5f7b
GCC_VER = 13.2.0
BINUTILS_VER = 2.40
MUSL_VER = 1.2.4
GMP_VER = 6.2.1
MPC_VER = 1.2.1
MPFR_VER = 4.2.0
LINUX_VER = 6.1.36
```
同时,你需要新建一个 `gcc-13.2.0.tar.xz.sha1` 文件,文件内容如下:
```
5f95b6d042fb37d45c6cbebfc91decfbc4fb493c gcc-13.2.0.tar.xz
```
如果你使用的是 Docker 构建,新建一个 `Dockerfile` 文件,写入以下内容:
```dockerfile
FROM alpine:edge
RUN apk add --no-cache \
gcc g++ git make curl perl \
rsync patch wget libtool \
texinfo autoconf automake \
bison tar xz bzip2 zlib \
file binutils flex \
linux-headers libintl \
gettext gettext-dev icu-libs pkgconf \
pkgconfig icu-dev bash \
ccache libarchive-tools zip
WORKDIR /opt
RUN git clone https://git.zv.io/toolchains/musl-cross-make.git
WORKDIR /opt/musl-cross-make
COPY config.mak /opt/musl-cross-make
COPY gcc-13.2.0.tar.xz.sha1 /opt/musl-cross-make/hashes
RUN make TARGET=x86_64-linux-musl -j || :
RUN sed -i 's/poison calloc/poison/g' ./gcc-13.2.0/gcc/system.h
RUN make TARGET=x86_64-linux-musl -j
RUN make TARGET=x86_64-linux-musl install -j
RUN tar cvzf x86_64-musl-toolchain.tgz output/*
```
如果你使用的是非 Docker 环境的 Alpine Linux可以直接执行 Dockerfile 中的命令,例如:
```bash
apk add --no-cache \
gcc g++ git make curl perl \
rsync patch wget libtool \
texinfo autoconf automake \
bison tar xz bzip2 zlib \
file binutils flex \
linux-headers libintl \
gettext gettext-dev icu-libs pkgconf \
pkgconfig icu-dev bash \
ccache libarchive-tools zip
git clone https://git.zv.io/toolchains/musl-cross-make.git
# 将 config.mak 拷贝到 musl-cross-make 的工作目录内,你需要将 /path/to/config.mak 替换为你的 config.mak 文件路径
cp /path/to/config.mak musl-cross-make/
cp /path/to/gcc-13.2.0.tar.xz.sha1 musl-cross-make/hashes
make TARGET=x86_64-linux-musl -j || :
sed -i 's/poison calloc/poison/g' ./gcc-13.2.0/gcc/system.h
make TARGET=x86_64-linux-musl -j
make TARGET=x86_64-linux-musl install -j
tar cvzf x86_64-musl-toolchain.tgz output/*
```
::: tip
以上所有脚本都适用于 x86_64 架构的 Linux。如果你需要构建 ARM 环境的 musl-cross-make只需要将上方所有 `x86_64` 替换为 `aarch64` 即可。
:::
这个编译过程可能会因为内存不足、网络问题等原因导致编译失败,你可以多尝试几次,或者使用更大内存的机器来编译。
如果遇到了问题,或者你有更好的改进方案,可以在 [讨论](https://github.com/crazywhalecc/static-php-cli-hosted/issues/1) 中提出。
## macOS 环境编译工具
对于 macOS 系统来说,我们使用的编译工具主要是 `clang`,它是 macOS 系统默认的编译器,同时也是 Xcode 的编译器。
在 macOS 下编译,主要依赖于 Xcode 或 Xcode Command Line Tools你可以在 App Store 下载 Xcode或者在终端执行 `xcode-select --install` 来安装 Xcode Command Line Tools。
此外,在 `doctor` 环境检查模块中static-php-cli 会检查 macOS 系统是否安装了 Homebrew、编译工具等如果没有会提示你安装这里不再赘述。
## FreeBSD 环境编译工具
FreeBSD 也是 Unix 系统,它的编译工具和 macOS 类似,你可以直接使用包管理 `pkg` 安装 `clang` 等编译工具,通过 `doctor` 命令。
## pkg-config 编译
如果你在使用 static-php-cli 构建静态 PHP 时仔细观察编译的日志,你会发现无论编译什么,都会先编译 `pkg-config`,这是因为 `pkg-config` 是一个用于查找依赖库的工具。
在早期的 static-php-cli 版本中,我们直接使用了包管理安装的 `pkg-config` 工具,但是这样会导致一些问题,例如:
- 即使指定了 `PKG_CONFIG_PATH``pkg-config` 也会尝试从系统路径中查找依赖包。
- 由于 `pkg-config` 会从系统路径中查找依赖包,所以如果系统中存在同名的依赖包,可能会导致编译失败。
为了避免以上问题,我们将 `pkg-config` 编译到用户态的 `buildroot/bin` 内并使用,使用了 `--without-sysroot` 等参数来避免从系统路径中查找依赖包。
<!-- TODO: 从 v2 system-build-tools.md 迁移并更新。
涵盖 v3 新增内容WindowsCMakeExecutor、vswhere.exe 检测、FrankenPHP 的 LLVM/Clang 支持。 -->

View File

@@ -0,0 +1,6 @@
# 注解参考
<!-- TODO: 所有 v3 PHP 注解的完整参考。
#[Library]、#[Extension]、#[BuildFor]、#[BeforeStage]、#[AfterStage]、
#[PatchBeforeBuild]、#[CustomPhpConfigureArg]、#[AsCheckItem]、#[AsFixItem]。
每个注解:参数、类型、允许的 target、示例。 -->

View File

@@ -0,0 +1,5 @@
# 依赖注入
<!-- TODO: v3 中 PHP-DI 自动装配的工作方式。
ApplicationContext::get() 用法。注册自定义服务。
向命令类、构建类和阶段方法注入依赖。 -->

View File

@@ -0,0 +1,6 @@
# Vendor 模式
<!-- TODO: Vendor 模式是什么,适用场景。
安装:`composer require crazywhalecc/static-php-cli`
注册指向自定义包类的外部 Registry。
最小工作示例:一个 Library 类 + 一个配置 YAML + 运行 spc。 -->

View File

@@ -0,0 +1,6 @@
# 生命周期 Hook
<!-- TODO: Hook 执行顺序与方法签名的详细说明。
#[BeforeStage('lib-name', 'build')]、#[AfterStage(...)]、#[PatchBeforeBuild]。
不同包的 Hook 如何合并与排序。
常见模式:修补 config.m4、注入编译标志、安装后修复。 -->

View File

@@ -0,0 +1,6 @@
# 编写 Package 类
<!-- TODO: 编写 Library 类和 Extension 类的分步指南。
使用 #[Library]、#[Extension]、#[BuildFor] 的完整注释代码示例。
UnixAutoconfExecutor / UnixCmakeExecutor / WindowsCMakeExecutor 用法。
文件放置位置src/Package/Library/、src/Package/Extension/)与自动加载。 -->

View File

@@ -1,96 +1,10 @@
# 常见问题
这里将会编写一些你容易遇到的问题。目前有很多,但是我需要花时间来整理一下
## php.ini 的路径是什么?
在 Linux、macOS 和 FreeBSD 上,`php.ini` 的路径是 `/usr/local/etc/php/php.ini`
在 Windows 中,路径是 `C:\windows\php.ini``php.exe` 所在的当前目录。
可以在 *nix 系统中使用手动构建选项 `--with-config-file-path` 来更改查找 `php.ini` 的目录。
此外,在 Linux、macOS 和 FreeBSD 上,`/usr/local/etc/php/conf.d` 目录中的 `.ini` 文件也会被加载。
在 Windows 中,该路径默认为空。
可以使用手动构建选项 `--with-config-file-scan-dir` 更改该目录。
PHP 默认也会从 [其他标准位置](https://www.php.net/manual/zh/configuration.file.php) 中搜索 `php.ini`
## 静态编译的 PHP 可以安装扩展吗?
因为传统架构下的 PHP 安装扩展的原理是使用 `.so` 类型的动态链接的库方式安装新扩展,而使用本项目编译的静态链接的 PHP。但是静态链接在不同操作系统有不同的定义。
首先,对于 Linux 系统,静态链接的二进制文件不会链接系统的动态链接库。纯静态链接的二进制文件(`-all-static`)无法加载动态库,因此无法添加新扩展。
同时,在纯静态模式下,你也不能使用 `ffi` 等扩展来加载外部 `.so` 模块。
你可以使用命令 `ldd buildroot/bin/php` 来检查你在 Linux 下构建的二进制文件是否为纯静态链接。
如果你 [构建基于 GNU libc 的 PHP](../guide/build-with-glibc),你可以使用 `ffi` 扩展来加载外部 `.so` 模块,并加载具有相同 ABI 的 `.so` 扩展。
例如,你可以使用以下命令构建一个与 glibc 动态链接的静态 PHP 二进制文件,支持 FFI 扩展并加载相同 PHP 版本和相同 TS 类型的 `xdebug.so` 扩展:
```bash
bin/spc-gnu-docker download --for-extensions=ffi,xml --with-php=8.4
bin/spc-gnu-docker build ffi,xml --build-cli --debug
buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" --ri xdebug
```
对于 macOS 平台macOS 下的几乎所有二进制文件都无法真正纯静态链接,几乎所有二进制文件都会链接 macOS 系统库:`/usr/lib/libresolv.9.dylib``/usr/lib/libSystem.B.dylib`
因此,在 macOS 上,你可以**直接**使用 SPC 构建具有动态链接扩展的静态编译 PHP 二进制文件:
1. 使用 `--build-shared=XXX` 选项构建共享扩展 `xxx.so`。例如:`bin/spc build bcmath,zlib --build-shared=xdebug --build-cli`
2. 你将获得 `buildroot/modules/xdebug.so``buildroot/bin/php`
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
对于 Windows 平台,由于官方构建的扩展(如 `php_yaml.dll`)强制使用了 `php8.dll` 动态库作为链接,静态构建的 PHP 不包含任何系统库以外的动态库,
所以 Windows 下无法加载官方构建的动态扩展。 由于 static-php-cli 还暂未支持构建动态扩展,所以目前还没有让 static-php 加载动态扩展的方法。
不过Windows 可以正常使用 `FFI` 扩展加载其他的 dll 文件并调用。
## 可以支持 Oracle 数据库扩展吗?
部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`
这些扩展无法使用源码的形式编译到 static-php-cli 中,所以本项目可能永远也不会支持这些扩展。不过,理论上你可以根据上面的问题在 macOS 和 Linux 下接入和使用这类扩展。
如果你对此类扩展有需求,或者大部分人都对这些闭源扩展使用有需求,
可以看看有关 [standalone-php-cli](https://github.com/crazywhalecc/static-php-cli/discussions/58) 的讨论。欢迎留言。
## 支持 Windows 吗?
该项目目前支持 Windows但支持的扩展数量较少。Windows 支持并不完美。主要有以下问题:
1. Windows 的编译过程与 *nix 不同,使用的工具链也不同。用于编译每个扩展依赖库的编译工具也几乎完全不同。
2. Windows 版本的需求也会根据所有使用本项目的人的需求推进。如果很多人需要,我会尽快支持相关扩展。
## 我可以使用 micro 保护我的源代码吗?
不可以。micro.sfx 本质上是将 php 和 php 代码合并为一个文件,没有编译或加密 PHP 代码的过程。
首先php-src 是 PHP 代码的官方解释器,市场上没有与主流分支兼容的 PHP 编译器。
我在网上看到一个名为 BPCBinary PHP Compiler的项目可以将 PHP 编译为二进制,但有很多限制。
加密和保护代码的方向与编译不同。编译后,也可以通过逆向工程等方法获得代码。真正的保护仍然通过打包和加密代码等手段进行。
因此本项目static-php-cli和相关项目lwmbs、swoole-cli都提供了 php-src 源代码的便捷编译工具。
本项目和相关项目引用的 phpmicro 只是 PHP 的 sapi 接口封装,而不是 PHP 代码的编译工具。
PHP 代码的编译器是一个完全不同的项目,因此不考虑额外的情况。
如果你对加密感兴趣,可以考虑使用现有的加密技术,如 Swoole Compiler、Source Guardian 等。
## 无法使用 ssl
**更新:该问题已在最新版本的 static-php-cli 中修复,现在默认读取系统的证书文件。如果你仍然遇到问题,请尝试下面的解决方案。**
使用 curl、pgsql 等请求 HTTPS 网站或建立 SSL 连接时,可能会出现 `error:80000002:system library::No such file or directory` 错误。
此错误是由于静态编译的 PHP 未通过 `php.ini` 指定 `openssl.cafile` 导致的。
你可以通过在使用 PHP 前指定 `php.ini` 并在 INI 中添加 `openssl.cafile=/path/to/your-cert.pem` 来解决此问题。
对于 Linux 系统,你可以从 curl 官方网站下载 [cacert.pem](https://curl.se/docs/caextract.html) 文件,也可以使用系统自带的证书文件。
有关不同发行版的证书位置,请参考 [Golang 文档](https://go.dev/src/crypto/x509/root_linux.go)。
> INI 配置 `openssl.cafile` 不能使用 `ini_set()` 函数动态设置,因为 `openssl.cafile` 是 `PHP_INI_SYSTEM` 类型的配置,只能在 `php.ini` 文件中设置。
## 为什么不支持旧版本的 PHP
因为旧版本的 PHP 有很多问题,如安全问题、性能问题和功能问题。此外,许多旧版本的 PHP 与最新的依赖库不兼容,这也是不支持旧版本 PHP 的原因之一。
你可以使用 static-php-cli 早期编译的旧版本,如 PHP 8.0,但不会明确支持早期版本。
<!-- TODO: 分类整理的 FAQ
章节:
- 构建问题(常见编译错误、工具缺失)
- 扩展动态加载、闭源依赖、oci8
- Windows图标嵌入、DLL 加载、FFI
- 版本兼容PHP 版本、glibc vs musl
- 源码保护micro、加密
从 v2 faq/index.md 迁移并扩充。 -->

View File

@@ -1,28 +0,0 @@
# Action 构建
Action 构建指的是直接使用 GitHub Action 进行编译。
如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact也可以从自托管的服务器下载[进入](https://dl.static-php.dev/static-php-cli/common/)
> 自托管的二进制也是由 Action 构建而来,[项目仓库地址](https://github.com/static-php/static-php-cli-hosted)。
> 包含的扩展有bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip
## 构建方法
使用 GitHub Action 可以方便地构建一个静态编译的 PHP 和 phpmicro同时可以自行定义要编译的扩展。
1. Fork 本项目。
2. 进入项目的 Actions选择 CI 开头的 Workflow根据你需要的操作系统选择
3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring`
4. 如果需要共享扩展(例如 `xdebug`),请设置 `shared-extensions`(使用英文逗号分割,例如 `xdebug`)。
5. 如果需要 FrankenPHP请启用 `build-frankenphp`,同时也需要启用 `enable-zts`
6. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts`
如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。
> 如果你需要在其他环境构建,可以使用 [手动构建](./manual-build)。
## 扩展选择
你可以到 [扩展列表](./extensions) 中查看目前你需要的扩展是否均支持,
然后到 [在线命令生成](./cli-generator) 中选择你需要编译的扩展,复制扩展字符串到 Action 的 `extensions` 中,编译即可。

View File

@@ -1,204 +0,0 @@
# 在 Windows 上构建
因为 Windows 系统是 NT 内核,与类 Unix 的操作系统使用的编译工具及操作系统接口几乎完全不同,所以在 Windows 上的构建流程会与 Unix 系统有些许不同。
## GitHub Actions 构建
现在已支持从 Actions 构建 Windows 版本的 static-php 了。
和 Linux、macOS 一样,你需要先 Fork static-php-cli 仓库到你的 GitHub 账户中,然后你可以进入 [扩展列表](./extensions) 选择要编译的扩展,然后进入自己仓库的 `CI on Windows` 选择 PHP 版本、填入扩展列表(逗号分割),点击 Run 即可。
如果你要在本地开发或构建,请继续向下阅读。
## 环境准备
在 Windows 上构建静态 PHP 所需要的工具与 PHP 官方的 Windows 构建工具是相同的。你可以阅读 [官方文档](https://wiki.php.net/internals/windows/stepbystepbuild_sdk_2)。
总结下来,你需要以下环境及工具:
- Windows 10需要 build 17063 或以后的更新)
- Visual Studio 2019/2022推荐 2022
- Visual Studio 的 C++ 桌面开发
- Git for Windows
- static-php-cli 仓库
- PHP 和 Composerstatic-php-cli 需要它们,可使用 `bin/setup-runtime` 自动安装)
- [php-sdk-binary-tools](https://github.com/php/php-sdk-binary-tools)(可使用 doctor 自动安装)
- strawberry-perl可使用 doctor 自动安装)
- nasm可使用 doctor 自动安装)
::: tip
static-php-cli 在 Windows 上的构建指的是使用 MSVC 构建 PHP不基于 MinGW、Cygwin、WSL 等环境。
如果你更倾向使用 WSL请参考在 Linux 上构建的章节。
:::
在安装 Visual Studio 后,选择 C++ 桌面开发的工作负荷后,可能会下载 8GB 左右的编译工具,下载速度取决于你的网络状况。
### 安装 Git
Git for Windows 可以从 [这里](https://git-scm.com/download/win) 下载并安装 `Standalone Installer 64-bit` 版本,安装在默认位置(`C:\Program Files\Git\`)。
如果不想手动下载和安装,你也可以使用 Visual Studio Installer在**单个组件**的选择列表中,勾选 Git。
### 准备 static-php-cli
static-php-cli 项目的下载方式很简单,只需要使用 git clone 即可。推荐将项目放在 `C:\spc-build\` 或类似目录,路径最好不要有空格。
```shell
mkdir "C:\spc-build"
cd C:\spc-build
git clone https://github.com/crazywhalecc/static-php-cli.git
cd static-php-cli
```
static-php-cli 自身需要 PHP 环境,是有点奇怪,但现在可以通过脚本快速安装 PHP 环境。
一般你的电脑不会安装 Windows 版本的 PHP所以我们建议你在下载 static-php-cli 后,直接使用 `bin/setup-runtime`,在当前目录安装 PHP 和 Composer。
```shell
# 安装 PHP 和 Composer 到 ./runtime/ 目录
bin/setup-runtime
# 安装后,如需在全局命令中使用 PHP 和 Composer使用下面的命令将 runtime/ 目录添加到 PATH
bin/setup-runtime -action add-path
# 删除 PATH 中的 runtime/ 目录
bin/setup-runtime -action remove-path
```
在准备好 PHP 和 Composer 环境后,使用 `composer` 安装 static-php-cli 的依赖:
```shell
cd C:\spc-build\static-php-cli
runtime/composer install --no-dev
```
### 自动安装其他依赖
对于 `php-sdk-binary-tools``strawberry-perl``nasm`,我们更建议你直接使用命令 `bin/spc doctor --auto-fix` 检查并安装。
如果 doctor 成功自动安装,请**跳过**下方手动安装上述工具的步骤。
如果自动安装无法成功的话,再参考下方手动安装的方式。
### 手动安装 php-sdk-binary-tools
```bat
cd C:\spc-build\static-php-cli
git clone https://github.com/php/php-sdk-binary-tools.git
```
> 你也可以在 Windows 设置中设置全局变量 `PHP_SDK_PATH`,并将该项目克隆至变量对应的路径。一般情况下,默认即可。
### 手动安装 strawberry-perl
> 如果你不需要编译 openssl 扩展,可不安装 perl。
1. 从 [GitHub](https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/) 下载 strawberry-perl 最新版。
2. 安装到 `C:\spc-build\static-php-cli\pkgroot\perl\` 目录。
> 你可以下载 `-portable` 版本,并直接解压到上述目录。
> 最后的 `perl.exe` 应该位于 `C:\spc-build\static-php-cli\pkgroot\perl\perl\bin\perl.exe`。
### 手动安装 nasm
> 如果你不需要编译 openssl 扩展,可不安装 nasm。
1. 从 [官网](https://www.nasm.us/pub/nasm/releasebuilds/) 下载 nasm 工具x64
2.`nasm.exe``ndisasm.exe` 放在 `C:\spc-build\static-php-cli\php-sdk-binary-tools\bin\` 目录。
## 下载源码
见 [本地构建 - download](./manual-build.html#命令-download-下载依赖包)
## 编译 PHP
使用 build 命令可以开始构建静态 php 二进制,在执行 `bin/spc build` 命令前,务必先使用 `download` 命令下载资源,建议使用 `doctor` 检查环境。
### 基本用法
你需要先到 [扩展列表](./extensions) 或 [命令生成器](./cli-generator) 选择你要加入的扩展,然后使用命令 `bin/spc build` 进行编译。你需要指定编译目标,从如下参数中选择:
- `--build-cli`: 构建一个 cli sapi命令行界面可在命令行执行 PHP 代码)
- `--build-micro`: 构建一个 micro sapi用于构建一个包含 PHP 代码的独立可执行二进制)
```shell
# 编译 PHP附带 bcmath,openssl,zlib 扩展,编译目标为 cli
bin/spc build "bcmath,openssl,zlib" --build-cli
# 编译 PHP附带 bcmath,openssl,zlib 扩展,编译目标为 micro 和 cli
bin/spc build "bcmath,openssl,zlib" --build-micro --build-cli
```
::: warning
在Windows中最好使用双引号包裹包含逗号的参数例如 `"bcmath,openssl,mbstring"`
:::
### 调试
如果你在编译过程中遇到了问题,或者想查看每个执行的 shell 命令,可以使用 `--debug` 开启 debug 模式,查看所有终端日志:
```shell
bin/spc build "openssl" --build-cli --debug
```
### 编译运行选项
在编译过程中,有些特殊情况需要对编译器、编译目录的内容进行干预,可以尝试使用以下命令:
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能
- `--with-config-file-scan-dir=XXX` 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `-I xxx=yyy`: 编译前将 INI 选项硬编译到 PHP 内(支持多个选项,别名是 `--with-hardcoded-ini`
- `--with-micro-fake-cli`: 在编译 micro 时,让 micro 的 SAPI 伪装为 `cli`(用于兼容一些检查 `PHP_SAPI` 的程序)
- `--disable-opcache-jit`: 禁用 opcache jit默认启用
- `--without-micro-ext-test`: 在构建 micro.sfx 后,禁用测试不同扩展在 micro.sfx 的运行结果
- `--with-suggested-exts`: 编译时将 `ext-suggests` 也作为编译依赖加入
- `--with-suggested-libs`: 编译时将 `lib-suggests` 也作为编译依赖加入
- `--with-upx-pack`: 编译后使用 UPX 减小二进制文件体积(需先使用 `bin/spc install-pkg upx` 安装 upx
- `--with-micro-logo=XXX.ico`: 自定义 micro 构建组合后的 `exe` 可执行文件的图标(格式为 `.ico`
有关硬编码 INI 选项,下面是一个简单的例子,我们预设一个更大的 `memory_limit`,并且禁用 `system` 函数:
```shell
bin/spc build "bcmath,openssl" --build-cli -I "memory_limit=4G" -I "disable_functions=system"
```
另一个例子:自定义 micro 构建后的 `exe` 程序图标:
```shell
bin/spc build "ffi,bcmath" --build-micro --with-micro-logo=mylogo.ico --debug
bin/spc micro:combine hello.php
# Then we got `my-app.exe` with custom logo!
my-app.exe
```
## 使用 php.exe
php.exe 编译后位于 `buildroot\bin\` 目录,你可以将其拷贝到任意位置使用。
```shell
.\php -v
```
## 使用 micro
> phpmicro 是一个提供自执行二进制 PHP 的项目,本项目依赖 phpmicro 进行编译自执行二进制。详见 [dixyes/phpmicro](https://github.com/dixyes/phpmicro)。
最后编译结果会输出一个 `./micro.sfx` 的文件,此文件需要配合你的 PHP 源码使用。
该文件编译后会存放在 `buildroot/bin/` 目录中。
使用时应准备好你的项目源码文件,可以是单个 PHP 文件,也可以是 Phar 文件。
> 如果要结合 phar 文件,编译时必须包含 phar 扩展!
```shell
# code.php "<?php echo 'Hello world' . PHP_EOL;"
bin/spc micro:combine code.php -O my-app.exe
# Run it!!! Copy it to another computer!!!
./my-app.exe
```
如果打包 PHAR 文件,仅需把 code.php 更换为 phar 文件路径即可。
你可以使用 [box-project/box](https://github.com/box-project/box) 将你的 CLI 项目打包为 Phar
然后将它与 phpmicro 结合,生成独立可执行的二进制文件。
有关 `micro:combine` 命令的更多细节,请参考 Unix 系统上的 [命令](./manual-build)。

View File

@@ -1,54 +0,0 @@
# 构建 glibc 兼容的 Linux 二进制
## 为什么要构建 glibc 兼容的二进制
目前static-php-cli 在默认条件下在 Linux 系统构建的二进制都是基于 musl-libc静态链接的。
musl-libc 是一个轻量级的 libc 实现,它的目标是与 glibc 兼容,并且提供良好的纯静态链接支持。
这意味着,编译出来的静态 PHP 可执行文件在几乎任何 Linux 发行版都可以使用,而不需要考虑 libc、libstdc++ 等库的版本问题。
但是Linux 系统的纯静态链接 musl-libc 二进制文件存在以下问题:
- 无法使用 PHP 的 `dl()` 函数加载动态链接库和外部 PHP 扩展。
- 无法使用 PHP 的 FFI 扩展。
- 部分极端情况下,可能会出现性能问题,参见 [musl-libc 的性能问题](https://github.com/php/php-src/issues/13648)。
对于不同的 Linux 发行版,它们使用的默认 libc 可能不同,比如 Alpine Linux 使用 musl libc而大多数 Linux 发行版使用 glibc。
但即便如此,我们也不能直接使用任意的发行版和 glibc 构建便携的静态二进制文件,因为 glibc 有一些问题:
- 基于新版本的发行版在使用 gcc 等工具构建的二进制,无法在旧版本的发行版上运行。
- glibc 不推荐被静态链接,因为它的一些特性需要动态链接库的支持。
但是,我们可以使用 Docker 容器来解决这个问题,最终输出的结果是一个动态链接 glibc 和一些必要库的二进制,但它静态链接所有其他依赖。
1. 使用一个旧版本的 Linux 发行版(如 CentOS 7.x它的 glibc 版本比较旧,但是可以在大多数现代 Linux 发行版上运行。
2. 在这个容器中构建 PHP 的静态二进制文件,这样就可以在大多数现代 Linux 发行版上运行了。
> 使用 glibc 的静态二进制文件,可以在大多数现代 Linux 发行版上运行,但是不能在 musl libc 的发行版上运行,如 CentOS 6、Alpine Linux 等。
## 构建 glibc 兼容的 Linux 二进制
最新版的 static-php-cli 内置了 `bin/spc-gnu-docker` 脚本,可以一键创建一个 CentOS 7.x (glibc-2.17) 的 Docker 容器,并在容器中构建 glibc 兼容的 PHP 静态二进制文件。
然后,先运行一次以下命令。首次运行时时间较长,因为需要下载 CentOS 7.x 的镜像和一些编译工具。
```bash
bin/spc-gnu-docker
```
构建镜像完成后,你将看到和 `bin/spc` 一样的命令帮助菜单,这时说明容器已经准备好了。
在容器准备好后,你可以参考 [本地构建](./manual-build) 章节的内容,构建你的 PHP 静态二进制文件。仅需要把 `bin/spc``./spc` 替换为 `bin/spc-gnu-docker` 即可。
```bash
bin/spc-gnu-docker build bcmath,ctype,openssl,pdo,phar,posix,session,tokenizer,xml,zip --build-cli --debug
```
## 注意事项
极少数情况下,基于 glibc 的静态 PHP 可能会出现 segment fault 等错误,但目前例子较少,如果遇到问题请提交 issue。
glibc 构建为扩展的特性,不属于默认 static-php 的支持范围。如果有相关问题或需求,请在提交 Issue 时注明你是基于 glibc 构建的。
如果你需要不使用 Docker 构建基于 glibc 的二进制,请参考 `bin/spc-gnu-docker` 脚本,手动构建一个类似的环境。
请注意,我们仅支持使用 bin/spc-gnu-docker 构建的 glibc 版本。已在 RHEL 9 和 10 上进行了编译测试并验证其稳定性,但如果您遇到问题,我们可能不会进行修复。

View File

@@ -2,14 +2,6 @@
aside: false
---
<script setup lang="ts">
import CliGenerator from "../../.vitepress/components/CliGenerator.vue";
</script>
# 编译命令生成器
# CLI 编译命令生成器
::: tip
下面选择扩展可能包含所选操作系统不支持的扩展,这可能导致编译失败。请先查阅 [支持的扩展](./extensions)。
:::
<cli-generator lang="zh" />
<!-- TODO: 嵌入 CliGenerator Vue 组件。 -->

View File

@@ -0,0 +1,6 @@
# 命令行参考
<!-- TODO: 所有 spc 命令和选项的完整参考。
每个命令一个 ## 小节download、build、craft、doctor、check-update、dev:*。
每个选项:类型、默认值、说明、示例。
涵盖平台专属选项(如 Windows 下的 --embed-icon。 -->

View File

@@ -1,22 +1,3 @@
---
outline: 'deep'
---
# 依赖关系图
# 依赖关系图表
在编译 PHP 时,每个扩展、库都有依赖关系,这些依赖关系可能是必需的,也可能是可选的。在编译 PHP 时,可以选择是否包含这些可选的依赖关系。
例如,在 Linux 下编译 `gd` 扩展时,会强制编译 `zlib,libpng` 库和 `zlib` 扩展,而 `libavif,libwebp,libjpeg,freetype` 库都是可选的库,默认不会编译,除非通过 `--with-libs=avif,webp,jpeg,freetype` 选项指定。
- 对于可选扩展(扩展的可选特性),需手动在编译时指定,例如启用 Redis 的 igbinary 支持:`bin/spc build redis,igbinary`
- 对于可选库,需通过 `--with-libs=XXX` 选项编译指定。
- 如果想启用所有的可选扩展,可以使用 `bin/spc build redis --with-suggested-exts` 参数。
- 如果想启用所有的可选库,可以使用 `--with-suggested-libs` 参数。
## 扩展的依赖图
<!--@include: ../../deps-map-ext.md-->
## 库的依赖表
<!--@include: ../../deps-map-lib.md-->
<!-- TODO: 由 `bin/spc dev:gen-ext-dep-docs``dev:gen-lib-dep-docs` 自动生成。v3 命令实现后填充。 -->

View File

@@ -1,112 +1,4 @@
# 环境变量
本页面的环境变量列表中所提到的所有环境变量都具有默认值,除非另有说明。你可以通过设置这些环境变量来覆盖默认值
## 环境变量列表
在 2.3.5 版本之后,我们将环境变量集中到了 `config/env.ini` 文件中,你可以通过修改这个文件来设置环境变量。
我们将 static-php-cli 支持的环境变量分为三种:
- 全局内部环境变量:在 static-php-cli 启动后即声明,你可以在 static-php-cli 的内部使用 `getenv()` 来获取他们,也可以在启动 static-php-cli 前覆盖。
- 固定环境变量:在 static-php-cli 启动后声明,你仅可使用 `getenv()` 获取,但无法通过 shell 脚本对其覆盖。
- 配置文件环境变量:在 static-php-cli 构建前声明,你可以通过修改 `config/env.ini` 文件或通过 shell 脚本来设置这些环境变量。
你可以阅读 [config/env.ini](https://github.com/crazywhalecc/static-php-cli/blob/main/config/env.ini) 中每项参数的注释来了解其作用(仅限英文版)。
## 自定义环境变量
一般情况下,你不需要修改任何以下环境变量,因为它们已经被设置为最佳值。
但是,如果你有特殊需求,你可以通过设置这些环境变量来满足你的需求(比如你需要调试不同编译参数下的 PHP 性能表现)。
如需使用自定义环境变量,你可以在终端中使用 `export` 命令或者在命令前直接设置环境变量,例如:
```shell
# export 方式
export SPC_CONCURRENCY=4
bin/spc build mbstring,pcntl --build-cli
# 直接设置方式
SPC_CONCURRENCY=4 bin/spc build mbstring,pcntl --build-cli
```
或者,如果你需要长期修改某个环境变量,你可以通过修改 `config/env.ini` 文件来实现。
`config/env.ini` 分为三段,其中 `[global]` 全局有效,`[windows]``[macos]``[linux]` 仅对应的操作系统有效。
例如,你需要修改编译 PHP 的 `./configure` 命令,你可以在 `config/env.ini` 文件中找到 `SPC_CMD_PREFIX_PHP_CONFIGURE` 环境变量,然后修改其值即可。
但如果你的构建条件比较复杂,需要多种 env.ini 进行切换,我们推荐你使用 `config/env.custom.ini` 文件,这样你可以在不修改默认的 `config/env.ini` 文件的情况下,
通过写入额外的重载项目指定你的环境变量。
```ini
; This is an example of `config/env.custom.ini` file,
; we modify the `SPC_CONCURRENCY` and linux default CFLAGS passing to libs and PHP
[global]
SPC_CONCURRENCY=4
[linux]
SPC_DEFAULT_C_FLAGS="-O3"
```
## 编译依赖库的环境变量(仅限 Unix 系统)
从 2.2.0 开始static-php-cli 对所有 macOS、Linux、FreeBSD 等 Unix 系统的编译依赖库的命令均支持自定义环境变量。
这样你就可以随时通过环境变量来调整编译依赖库的行为。例如你可以通过 `xxx_CFLAGS=-O0` 来设置编译 xxx 库的优化参数。
当然,不是每个依赖库都支持注入环境变量,我们目前提供了三个通配的环境变量,后缀分别为:
- `_CFLAGS`: C 编译器的参数
- `_LDFLAGS`: 链接器的参数
- `_LIBS`: 额外的链接库
前缀为依赖库的名称,具体依赖库的名称以 `lib.json` 为准。其中,带有 `-` 的依赖库名称需要将 `-` 替换为 `_`
下面是一个替换 openssl 库编译的优化选项示例:
```shell
openssl_CFLAGS="-O0"
```
库名称使用同 `lib.json` 中列举的名称,区分大小写。
::: tip
当未指定相关环境变量时,除以下变量外,其余值均默认为空:
| var name | var default value |
|-----------------------|-------------------------------------------------------------------------------------------------|
| `pkg_config_CFLAGS` | macOS: `$SPC_DEFAULT_C_FLAGS -Wimplicit-function-declaration -Wno-int-conversion`, Other: empty |
| `pkg_config_LDFLAGS` | Linux: `--static`, Other: empty |
| `imagemagick_LDFLAGS` | Linux: `-static`, Other: empty |
| `imagemagick_LIBS` | macOS: `-liconv`, Other: empty |
| `ldap_LDFLAGS` | `-L$BUILD_LIB_PATH` |
| `openssl_CFLAGS` | Linux: `$SPC_DEFAULT_C_FLAGS`, Other: empty |
| others... | empty |
:::
下表是支持自定义以上三种变量的依赖库名称列表:
| lib name |
|-------------|
| brotli |
| bzip |
| curl |
| freetype |
| gettext |
| gmp |
| imagemagick |
| ldap |
| libargon2 |
| libavif |
| libcares |
| libevent |
| openssl |
::: tip
因为给每个库适配自定义环境变量是一项特别繁琐的工作,且大部分情况下你都不需要这些库的自定义环境变量,所以我们目前只支持了部分库的自定义环境变量。
如果你需要自定义环境变量的库不在上方列表,可以通过 [GitHub Issue](https://github.com/crazywhalecc/static-php-cli/issues)
来提出需求。
:::
<!-- TODO: config/env.ini 和 config/env.custom.ini 支持的所有环境变量完整表格
从 v2 env-vars.md 迁移并更新。 -->

View File

@@ -1,158 +1,3 @@
# 扩展注意事项
因为是静态编译,扩展不会 100% 完美编译,而且不同扩展对 PHP、环境都有不同的要求这里将一一列举。
## curl
HTTP3 支持默认未启用,需在编译时添加 `--with-libs="nghttp2,nghttp3,ngtcp2"` 以启用 PHP 8.4 及以上版本的 HTTP3 支持。
使用 curl 请求 HTTPS 时,可能存在 `error:80000002:system library::No such file or directory` 错误,
解决办法详见 [FAQ - 无法使用 ssl](../faq/#无法使用-ssl)。
## phpmicro
1. phpmicro SAPI 仅支持 PHP >= 8.0 版本。
## swoole
1. swoole >= 5.0 版本仅支持 PHP >= 8.0 版本。
2. swoole 目前不支持 PHP 8.0 版本 curl 的 hook后续有可能会修复
3. 编译时只包含 `swoole` 扩展时不会完整开启支持的 Swoole 数据库协程 hook如需使用请加入对应的 `swoole-hook-xxx` 扩展。
4. swoole 在部分扩展组合下可能出现 `zend_mm_heap corrupted` 问题,暂未找到是什么原因导致的。
## swoole-hook-pgsql
swoole-hook-pgsql 不是一个扩展,而是 Swoole 的 Hook 特性。
如果你在编译时添加了 `swoole,swoole-hook-pgsql`,你将启用 Swoole 的 PostgreSQL 客户端和 `pdo_pgsql` 扩展的协程模式。
swoole-hook-pgsql 与 `pdo_pgsql` 扩展冲突。如需使用 Swoole 和 `pdo_pgsql`,请删除 pdo_pgsql 扩展,启用 `swoole``swoole-hook-pgsql` 即可。
该扩展包含了 `pdo_pgsql` 的协程环境的实现。
在 macOS 系统,`pdo_pgsql` 可能无法正常连接到 postgresql 服务器,请谨慎使用。
## swoole-hook-mysql
swoole-hook-mysql 不是一个扩展,而是 Swoole 的 Hook 特性。
如果你在编译时添加了 `swoole,swoole-hook-mysql`,你将启用 Swoole 的 `mysqlnd``pdo_mysql` 的协程模式。
## swoole-hook-sqlite
swoole-hook-sqlite 不是一个扩展,而是 Swoole 的 Hook 特性。
如果你在编译时添加了 `swoole,swoole-hook-sqlite`,你将启用 Swoole 的 `pdo_sqlite` 的协程模式Swoole 必须为 5.1 以上)。
swoole-hook-sqlite 与 `pdo_sqlite` 扩展冲突。如需使用 Swoole 和 `pdo_sqlite`,请删除 pdo_sqlite 扩展,启用 `swoole``swoole-hook-sqlite` 即可。
该扩展包含了 `pdo_sqlite` 的协程环境的实现。
## swoole-hook-odbc
swoole-hook-odbc 不是一个扩展,而是 Swoole 的 Hook 特性。
如果你在编译时添加了 `swoole,swoole-hook-odbc`,你将启用 Swoole 的 `odbc` 扩展的协程模式。
swoole-hook-odbc 与 `pdo_odbc` 扩展冲突。如需使用 Swoole 和 `pdo_odbc`,请删除 `pdo_odbc` 扩展,启用 `swoole``swoole-hook-odbc` 即可。
该扩展包含了 `pdo_odbc` 的协程环境的实现。
## swow
1. swow 仅支持 PHP 8.0+ 版本。
## imagick
1. OpenMP 支持已被禁用,这是维护者推荐的做法,系统软件包也是如此配置。
## imap
1. 该扩展目前不支持 Kerberos。
2. 由于底层的 c-client、ext-imap 不是线程安全的。 无法在 `--enable-zts` 构建中使用它。
3. 该扩展已在 PHP 8.4 中被移除,因此我们建议您寻找替代实现,例如 [Webklex/php-imap](https://github.com/Webklex/php-imap)。
## gd
1. gd 扩展依赖了较多的额外图形库,默认情况下,直接使用 `bin/spc build gd` 不会引入和支持部分图形库,例如 `libjpeg``libavif` 等,
需要使用 `--with-libs` 参数补全。目前支持 `freetype,libjpeg,libavif,libwebp` 四个库的支持,所以这里可以使用以下命令来让 gd 库引入它们:
```bash
bin/spc build gd --with-libs=freetype,libjpeg,libavif,libwebp --build-cli
```
## mcrypt
1. 目前未支持,未来也不计划支持此扩展。[#32](https://github.com/crazywhalecc/static-php-cli/issues/32)
## oci8
1. oci8 是 Oracle 数据库的扩展,因为 Oracle 提供的扩展所依赖的库未提供静态编译版本(`.a`)或源代码,无法使用静态链接的方式将此扩展编译到 php 内,故无法支持。
## xdebug
1. Xdebug 只能作为共享扩展进行构建。您需要使用除了 `musl-static` 外的其他 `SPC_TARGET` 构建目标。
2. 使用 Linux/glibc 或 macOS 时,您可以使用 `--build-shared=xdebug` 将 Xdebug 编译为共享扩展。
编译后的 `./php` 二进制文件可以通过指定 INI 文件进行配置和运行,例如 `./php -d 'zend_extension=/path/to/xdebug.so' your-code.php`
## xml
1. xml包括 xmlreader、xmlwriter、dom、simplexml 等,添加 xml 扩展时最好同时启用这些扩展。
2. libxml 包含在 xml 扩展中。 启用 xml 相当于启用 libxml。
## glfw
1. glfw 扩展依赖 OpenGL在 Linux 平台还依赖 X11 等环境,这些库都无法被轻易地动态链接。
2. 在 macOS 系统下,我们可以动态链接系统的 OpenGL 和一些相关的库。
## rar
1. rar 扩展目前在 macOS x86_64 环境下与 `common` 扩展集合编译 phpmicro 存在问题。
## pgsql
~~pgsql ssl 连接与 openssl 3.2.0 不兼容。相关链接:~~
- ~~<https://github.com/Homebrew/homebrew-core/issues/155651>~~
- ~~<https://github.com/Homebrew/homebrew-core/pull/155699>~~
- ~~<https://github.com/postgres/postgres/commit/c82207a548db47623a2bfa2447babdaa630302b9>~~
pgsql 16.2 修复了这个 Bug现在正常工作了。
在 pgsql 使用 SSL 连接时,可能存在 `error:80000002:system library::No such file or directory` 错误,
解决办法详见 [FAQ - 无法使用 ssl](../faq/#无法使用-ssl)。
## openssl
使用基于 openssl 的扩展(如 curl、pgsql 等网络库)时,可能存在 `error:80000002:system library::No such file or directory` 错误,
解决办法详见 [FAQ - 无法使用 ssl](../faq/#无法使用-ssl)。
## password-argon2
1. password-argon2不是一个标准的扩展。`password_hash` 函数的 `PASSWORD_ARGON2ID` 算法需要 libsodium 或 libargon2 才能工作。
2. 使用 password-argon2 可以为此启用多线程支持。
## ffi
1. 由于 musl libc 静态链接的限制,无法加载动态库,因此无法使用 ffi。
如果您需要使用 ffi 扩展,请参阅 [使用 GNU libc 编译 PHP](./build-with-glibc)。
2. macOS 支持 ffi 扩展,但某些内核不包含调试符号时会出现错误。
3. Windows x64 支持 ffi 扩展。
## xhprof
xhprof 扩展包含三部分:`xhprof_extension``xhprof_html``xhprof_libs`。编译的二进制中只包含 `xhprof_extension`
如果需要使用 xhprof请到 [pecl.php.net/package/xhprof](http://pecl.php.net/package/xhprof) 下载源码,指定 `xhprof_libs``xhprof_html` 路径来使用。
## event
event 扩展在 macOS 系统下编译后暂无法使用 `openpty` 特性。相关 Issue
- [static-php-cli#335](https://github.com/crazywhalecc/static-php-cli/issues/335)
## parallel
parallel 扩展只支持 PHP 8.0 及以上版本,并只支持 ZTS 构建(`--enable-zts`)。
## spx
1. SPX 目前不支持 Windows且官方仓库也不支持静态编译static-php-cli 使用了 [修改版本](https://github.com/static-php/php-spx)。
## mimalloc
1. 从技术上讲,这不是扩展,而是一个库。
2. 在 Linux 或 macOS 上使用 `--with-libs="mimalloc"` 进行构建将覆盖默认分配器。
3. 目前,这还处于实验阶段,但建议在线程环境中使用。
<!-- TODO: 从 v2 extension-notes.md 迁移并更新。各扩展编译特殊说明。 -->

View File

@@ -1,22 +1,3 @@
<script setup>
import SearchTable from "../../.vitepress/components/SearchTable.vue";
</script>
# 支持的扩展列表
# 扩展列表
> - `yes`: 已支持
> - 空白: 目前还不支持,或正在支持中
> - `no` with issue link: 确定不支持或无法支持
> - `partial` with issue link: 已支持,但是无法完美工作
<search-table />
::: tip
如果缺少您需要的扩展,您可以创建 [功能请求](https://github.com/crazywhalecc/static-php-cli/issues)。
有些扩展或扩展依赖的库会有一些可选的特性,例如 gd 库可选支持 libwebp、freetype 等。
如果你只使用 `bin/spc build gd --build-cli` 是不会包含它们static-php-cli 默认为最小依赖原则)。
有关编译可选库,请参考 [扩展、库的依赖关系图表](./deps-map)。对于可选的库,你也可以从 [编译命令生成器](./cli-generator) 中选择扩展后展开选择可选库。
:::
<!-- TODO: 由 `bin/spc dev:gen-ext-docs` 自动生成。v3 命令实现后填充。 -->

View File

@@ -0,0 +1,187 @@
# 第一次构建
本页通过完整的示例演示如何从零开始构建一个静态 PHP 二进制。
::: tip
如果你采用的是 spc 二进制方式安装,请将本章节中的所有 `spc` 替换为 `./spc``.\spc.exe`
如果你采用的是源码安装,请将 `spc` 替换为 `bin/spc`
:::
## 两种构建方式
StaticPHP 提供两种构建方式,根据使用场景选择:
| 方式 | 适合场景 |
|--------------|--------------------------|
| `craft` 一键构建 | 日常使用、快速上手 |
| 分步构建 | CI/CD 流水线、需要拆分下载与编译阶段的场景 |
## 方式一:`craft` 一键构建(推荐)
`craft` 命令读取一个 `craft.yml` 配置文件自动完成依赖下载、库编译、PHP 构建的全流程。
### 编写 craft.yml
在当前目录创建 `craft.yml`,声明要编译的 PHP 版本、扩展和目标 SAPI
```yaml
php-version: 8.4
extensions: bcmath,posix,phar,zlib,openssl,curl,fileinfo,tokenizer
sapi:
- cli
- micro
```
不想手动编写?试试[命令行生成器](./cli-generator)自动生成配置。
### 开始构建
```bash
spc craft
```
构建过程依次执行:下载依赖 → 编译依赖库 → 编译 PHP。全程无需人工干预。
如需查看详细日志,加上 `-v``-vv``-vvv` 参数:
```bash
spc craft -v
```
### 查看产物
构建成功后,产物均位于 `buildroot/bin/`
| SAPI | 产物路径 |
|------------|------------------------------------------------------|
| cli | `buildroot/bin/php`Windows`buildroot/bin/php.exe` |
| fpm | `buildroot/bin/php-fpm` |
| micro | `buildroot/bin/micro.sfx` |
| embed | `buildroot/lib/libphp.a` |
| frankenphp | `buildroot/bin/frankenphp` |
验证一下 cli 是否可用:
```bash
./buildroot/bin/php -v
./buildroot/bin/php -m
```
## 方式二:分步构建
分步方式适合需要将下载与编译拆分为独立阶段的场景,例如在 CI 中缓存下载内容以加速后续构建。
### 第一步:下载依赖
v3 版本中你可以省略这一步骤直接构建想要的内容StaticPHP 会自动下载所需的依赖库和扩展源码。
但如果你想提前下载,或在网络环境较差的情况下分阶段构建,可以使用 `download` 命令:
```bash
# 按扩展列表下载(推荐,只下载实际需要的内容)
spc download --for-extensions="bcmath,posix,phar,zlib,openssl,curl,fileinfo,tokenizer" --with-php=8.4
# 按依赖包列表下载
spc download "curl,openssl" --with-php=8.4
```
下载内容缓存在 `downloads/` 目录,重复构建时会直接复用。
```bash
# 网络较慢时,可增大并发数和重试次数
spc download --for-extensions=bcmath,openssl,curl --parallel 10 --retry=3
# 优先使用预编译的二进制依赖,跳过源码编译(大幅加速构建)
spc download --for-extensions=bcmath,openssl,curl --prefer-binary
```
### 第二步:构建 PHP
```bash
# 构建 cli SAPI
spc build:php bcmath,phar,zlib,openssl,curl,fileinfo,tokenizer --build-cli
# 同时构建多个 SAPI
spc build:php bcmath,phar,zlib,openssl,curl --build-cli --build-micro
```
#### 常用构建选项
| 选项 | 说明 |
|----------------------|--------------------------------------|
| `--build-cli` | 构建 cli SAPI |
| `--build-fpm` | 构建 php-fpm不支持 Windows |
| `--build-micro` | 构建 micro.sfx |
| `--build-embed` | 构建嵌入式 SAPI |
| `--build-frankenphp` | 构建 FrankenPHP |
| `--enable-zts` | 启用线程安全ZTS版本 |
| `--no-strip` | 保留调试符号,不精简二进制 |
| `-I key=value` | 硬编译 INI 选项到 PHP 中 |
| `--with-upx-pack` | 用 UPX 压缩产物(需先 `spc install-pkg upx` |
硬编译 INI 的例子——预设更大的内存限制,并禁用 `system` 函数:
```bash
spc build:php bcmath,pcntl,posix --build-cli -I "memory_limit=4G" -I "disable_functions=system"
```
## 打包 micro 应用
构建 `micro.sfx` 后,用 `micro:combine` 将你的 PHP 代码打包进去,生成一个完全独立的可执行文件:
```bash
echo "<?php echo 'Hello, World!' . PHP_EOL;" > hello.php
spc micro:combine hello.php --output=hello
./hello
```
也支持打包 `.phar` 文件,以及注入 INI 配置:
```bash
# 打包 phar
spc micro:combine your-app.phar --output=your-app
# 打包时注入 INI
spc micro:combine your-app.phar --output=your-app -I "memory_limit=512M"
# 从 ini 文件注入配置
spc micro:combine your-app.phar --output=your-app -N /path/to/custom.ini
```
## 调试与重新构建
构建失败,或想查看详细过程,使用 `-v` / `-vv` / `-vvv`
- `-v` 将显示 `INFO` 级别的日志,包含执行到的模块和执行的编译命令等。
- `-vv` 将显示 `DEBUG` 级别的日志,包含所有 StaticPHP 中调试级别的日志。
- `-vvv` 将显示 `DEBUG` 级别的日志,并将其他 shell 命令执行的 STDOUT 输出到终端。
```bash
spc build:php bcmath,openssl --build-cli -vv
```
或者,你也可以查看 `log/spc.shell.log``log/spc.output.log` 获取终端输出和 StaticPHP 日志。
如需清理编译中间产物、从头重新构建(不重新下载),使用 `reset`
```bash
spc reset
# 然后重新构建
spc build:php bcmath,openssl --build-cli
```
::: tip
`reset` 只清理 `buildroot/``source/` 目录,不会删除 `downloads/` 缓存。
如需同时清理下载缓存,加上 `--with-download` 参数。
:::
如果问题持续无法解决,欢迎提交 [Issue](https://github.com/crazywhalecc/static-php-cli/issues),并附上 `craft.yml`(如有)和 `log/` 目录的压缩包。
## 接下来
- [命令行参考](./cli-reference) — 所有命令与选项的完整说明
- [扩展列表](./extensions) — 查看支持的扩展及其依赖关系
- [常见问题](./troubleshooting) — 构建失败时的排查指南

View File

@@ -1,48 +1,53 @@
# 指南
# 构建指南
static-php-cli 是一个用于构建静态编译的 PHP 二进制的工具,目前支持 Linux 和 macOS 系统。
## StaticPHP 是什么
在指南章节中,你将了解到如何使用 static-php-cli 构建独立的 php 程序
StaticPHP 是一个构建工具,能够将 PHP 解释器与你所需的扩展一起编译成一个独立的二进制文件,无需在目标系统上预先安装 PHP 或任何依赖库
构建产物可以直接分发和运行,适用于 Linux、macOS 和 Windows 平台。
- [本地构建](./manual-build)
- [Action 构建](./action-build)
- [扩展列表](./extensions)
## 为什么要构建静态 PHP
## 编译环境
普通 PHP 安装依赖系统环境:你需要先安装 PHP、再装扩展、再处理各个发行版之间的差异。
将 PHP 构建为静态二进制之后,这些问题都不再存在——你得到的是一个单文件可执行程序,在任何相同架构的系统上开箱即用。
下面是架构支持情况,:gear: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。
典型使用场景:
| | x86_64 | aarch64 |
|---------|-------------------|-------------------|
| macOS | :gear: :computer: | :gear: :computer: |
| Linux | :gear: :computer: | :gear: :computer: |
| Windows | :gear: :computer: | |
| FreeBSD | :computer: | :computer: |
- **部署命令行工具**:把 PHP 工具(如 Composer、PHPStan、自研 CLI打包后直接分发用户无需安装 PHP。
- **容器和嵌入式环境**:用最小体积的静态 PHP 替代臃肿的基础镜像。
- **服务端应用**:构建包含 FPM 或 FrankenPHP SAPI 的静态二进制,部署更简单,不依赖宿主机环境。
当前支持编译的 PHP 版本:
## phpmicro把 PHP 和你的代码打包成一个文件
> :warning: 部分支持,对于新的测试版和旧版本可能存在问题
>
> :heavy_check_mark: 支持
>
> :x: 不支持
[phpmicro](https://github.com/easysoft/phpmicro) 是一个第三方 PHP SAPIStaticPHP 对其提供原生支持
它能将 PHP 解释器本身和你的 `.php` 源文件(或 `.phar` 打包文件)合并成单个自解压可执行文件(`sfx`)。
| PHP Version | Status | Comment |
|-------------|--------------------|---------------------------------------------------------|
| 7.2 | :x: | |
| 7.3 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
| 7.4 | :x: | phpmicro 和许多扩展不支持 7.3、7.4 版本 |
| 8.0 | :warning: | PHP 官方已停止 8.0 的维护,我们不再处理 8.0 相关的 backport 支持 |
| 8.1 | :warning: | PHP 官方仅对 8.1 提供安全更新,在 8.5 发布后我们不再处理 8.1 相关的 backport 支持 |
| 8.2 | :heavy_check_mark: | |
| 8.3 | :heavy_check_mark: | |
| 8.4 | :heavy_check_mark: | |
| 8.5 (beta) | :warning: | PHP 8.5 目前处于 beta 阶段 |
```
micro.sfx + your-app.phar = your-app (可直接运行,无任何依赖)
```
> 这个表格的支持状态是 static-php-cli 对构建对应版本的支持情况,不是 PHP 官方对该版本的支持情况
这特别适合分发 PHP 编写的命令行工具:用户拿到的只是一个普通的可执行文件,完全感知不到背后是 PHP
## PHP 支持版本
## 改善你的项目分发与部署
目前static-php-cli 对 PHP 8.2 ~ 8.5 版本是支持的,对于 PHP 8.1 及更早版本理论上支持,只需下载时选择早期版本即可。
但由于部分扩展和特殊组件已对早期版本的 PHP 停止了支持,所以 static-php-cli 不会明确支持早期版本。
我们推荐你编译尽可能新的 PHP 版本,以获得更好的体验。
**取代臃肿的 Docker 基础镜像**
官方 `php:8.x` 镜像动辄数百 MB大多数情况下只是为了提供一个 PHP 运行环境。
改用静态 PHP 二进制配合极简基础镜像(甚至 `FROM scratch`),镜像体积可以压缩到个位数 MB启动速度也更快。
**构建可分发的 PHP CLI 工具**
用 [symfony/console](https://symfony.com/doc/current/components/console.html) 或 [Laravel Zero](https://laravel-zero.com) 写好你的 CLI 程序,
再用 [Box](https://github.com/box-project/box) 打包成 `.phar`,最后通过 phpmicro 合并为单文件可执行程序。
最终产物可以直接分发,用户无需安装任何 PHP 环境,和 Go、Rust 工具的体验完全一致。
**基于 FrankenPHP 构建单文件 Web 应用**
[FrankenPHP](https://frankenphp.dev) 是一个现代 PHP 应用服务器,内置 HTTP/2、HTTP/3 和 HTTPS 自动管理。
StaticPHP 支持将 FrankenPHP 连同所需扩展一起静态编译,
最终产物是一个包含完整 Web 服务器的单一可执行文件,无需 Nginx、PHP-FPM直接部署即可运行。
## 接下来
- [安装 SPC](./installation) — 安装 StaticPHP 构建工具
- [第一次构建](./first-build) — 完整流程演示:从下载源码到得到可执行文件
- [命令行参考](./cli-reference) — 所有命令与选项速查

View File

@@ -0,0 +1,108 @@
# 安装 StaticPHP
## 系统要求
| 平台 | 架构 | 说明 |
|---|---|---|
| Linux | x86_64、aarch64 | 支持主流发行版Alpine、Debian/Ubuntu、RHEL/CentOS 等) |
| macOS | x86_64 (Intel)、arm64 (Apple Silicon) | 需要 macOS 12 或更高版本 |
| Windows | x86_64 | 需要 Windows 10 Build 17063 或更高版本 |
::: tip
Linux 下glibc 环境Debian、Ubuntu、Arch 等)和 musl 环境Alpine均受支持。
`doctor` 命令会自动检测当前环境并在必要时引导安装合适的工具链。
:::
StaticPHP 有多种安装方式,选择适合你的场景:
| 方式 | 适合谁 |
|---|---|
| 预编译二进制 | 大多数用户,直接下载开箱即用 |
| 从源码安装 | 参与开发、或需要修改核心构建逻辑的开发者 |
| Vendor 模式 | 在已有 PHP 项目中集成 StaticPHP 能力 |
## 预编译二进制
spc 无须任何依赖,下载即可运行,支持 Linux、macOS 和 Windows。
> spc 本身是由 StaticPHP 构建的静态 PHP 二进制,幽默地说:我们用 StaticPHP 构建了 StaticPHP 的构建工具。
```shell
# Linux x86_64
curl -#fSL https://dl.static-php.dev/v3/spc-bin/latest/spc-linux-x86_64 -o spc
# Linux arm64
curl -#fSL https://dl.static-php.dev/v3/spc-bin/latest/spc-linux-aarch64 -o spc
# macOS x86_64 (Intel)
curl -#fSL https://dl.static-php.dev/v3/spc-bin/latest/spc-macos-x86_64 -o spc
# macOS arm64 (Apple Silicon)
curl -#fSL https://dl.static-php.dev/v3/spc-bin/latest/spc-macos-aarch64 -o spc
# Windows x86_64 (PowerShell)
curl.exe -#fSL https://dl.static-php.dev/v3/spc-bin/latest/spc-windows-x86_64.exe -o spc.exe
```
*nix 系统下载完成后需要赋予可执行权限:
```bash
chmod +x spc && ./spc --version
```
## 从源码安装
适合想参与开发、或需要修改核心注册表和构建脚本的开发者。需要系统已安装 PHP >= 8.4、Composer以及 `mbstring,posix,pcntl,iconv,phar,zlib` 扩展。
```bash
git clone https://github.com/static-php/static-php.git --branch v3
cd static-php-cli
composer install
```
如果系统还没有 PHP 和 Composer可以用内置脚本一键安装运行环境
::: code-group
```bash [Linux / macOS]
bin/setup-runtime
```
```powershell [Windows]
.\bin\setup-runtime.ps1
.\bin\setup-runtime.ps1 add-path # 将 runtime/ 加入 PATH
```
:::
脚本执行完成后,会在项目目录下生成 `runtime/` 子目录,其中包含 `php` 和 `composer` 两个可执行文件。安装完成后有两种使用方式:
1. **直接通过路径调用**(无需修改环境变量):
```bash
runtime/php bin/spc --help
runtime/php runtime/composer install
```
2. **将 `runtime/` 加入 PATH**(之后可直接使用 `php`、`composer`、`bin/spc`
```bash
export PATH="/path/to/static-php/runtime:$PATH"
# 建议写入 ~/.bashrc 或 ~/.zshrc 使其永久生效
```
## Vendor 模式
适合在已有 PHP 项目中直接集成 StaticPHP 能力,或通过自定义 registry 支持私有库和扩展的构建。
```bash
composer require crazywhalecc/static-php-cli
```
Vendor 模式的详细用法见 [Vendor 模式指南](../develop/vendor-mode/)。
## 验证构建环境
> **Vendor 模式用户可跳过此步骤。**
安装完成后,运行 `doctor` 检查系统构建工具链是否就绪cmake、make、编译器等
```bash
# 使用 spc 二进制
./spc doctor --auto-fix
# 使用源码安装
bin/spc doctor --auto-fix
```
检查通过后,继续阅读[第一次构建](./first-build)。

View File

@@ -1,640 +0,0 @@
---
outline: 'deep'
---
# 本地构建Linux、macOS、FreeBSD
本章节为 Linux、macOS、FreeBSD 的构建过程,如果你要在 Windows 上构建,请到 [在 Windows 上构建](./build-on-windows)。
## 手动构建(使用 SPC 二进制)(推荐)
本项目提供了一个 static-php-cli 的二进制文件,你可以直接下载对应平台的二进制文件,然后使用它来构建静态的 PHP。目前 `spc` 二进制支持的平台有 Linux 和 macOS。
使用以下命令从自托管服务器下载:
```bash
# Download from self-hosted nightly builds (sync with main branch)
# For Linux x86_64
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-x86_64
# For Linux aarch64
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-linux-aarch64
# macOS x86_64 (Intel)
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-x86_64
# macOS aarch64 (Apple)
curl -fsSL -o spc https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-macos-aarch64
# Windows (x86_64, win10 build 17063 or later)
curl.exe -fsSL -o spc.exe https://dl.static-php.dev/static-php-cli/spc-bin/nightly/spc-windows-x64.exe
# Add execute perm (Linux and macOS only)
chmod +x ./spc
# Run (Linux and macOS)
./spc --version
# Run (Windows powershell)
.\spc.exe --version
```
> 如果你使用的是打包好的 `spc` 二进制,你需要将下面所有命令中 `bin/spc` 开头替换为 `./spc`。
## 手动构建(使用源码)
如果使用 spc 二进制出现问题,或你有修改 static-php-cli 源码需求,请从源码下载 static-php-cli。
目前支持在 macOS、Linux 上构建macOS 支持最新版操作系统和两种架构Linux 支持 Debian、RHEL 及衍生发行版、Alpine Linux 等。
因为本项目本身采用 PHP 开发,所以在编译时也需要系统安装 PHP。本项目本身也提供了适用于本项目的静态二进制 php可以根据实际情况自行选择使用。
### 下载本项目
```bash
git clone https://github.com/crazywhalecc/static-php-cli.git --depth=1
cd static-php-cli
# 你需要先安装 PHP 环境后再运行 Composer 和本项目,安装方式可参考下面。
composer update
```
### 使用预编译静态 PHP 二进制运行 static-php-cli
如果你不想使用 Docker、在系统内安装 PHP可以直接下载本项目自身编译好的 php 二进制 cli 程序。使用流程如下:
使用命令部署环境,此脚本会从 [自托管的服务器](https://dl.static-php.dev/static-php-cli/) 下载一个当前操作系统的 php-cli 包,
并从 [getcomposer](https://getcomposer.org/download/latest-stable/composer.phar) 或 [Aliyun镜像](https://mirrors.aliyun.com/composer/composer.phar) 下载 Composer。
::: tip
使用预编译静态 PHP 二进制目前仅支持 Linux 和 macOS。FreeBSD 环境因为缺少自动化构建环境,所以暂不支持。
:::
```bash
bin/setup-runtime
# 对于中国大陆地区等网络环境特殊的用户,可使用镜像站加快下载速度
bin/setup-runtime --mirror china
```
此脚本总共会下载两个文件:`bin/php``bin/composer`,下载完成后,有两种使用方式:
1.`bin/` 目录添加到 PATH 路径中:`export PATH="/path/to/your/static-php-cli/bin:$PATH"`,添加路径后,相当于系统安装了 PHP可直接使用 `composer``php -v` 等命令,也可以直接使用 `bin/spc`
2. 直接调用,比如执行 static-php-cli 命令:`bin/php bin/spc --help`,执行 Composer`bin/php bin/composer update`
### 使用 Docker 环境
如果你不愿意在系统安装 PHP 和 Composer 运行环境,可以使用内置的 Docker 环境构建脚本。
```bash
# 直接使用,将所有使用的命令中 `bin/spc` 替换为 `bin/spc-alpine-docker` 即可
bin/spc-alpine-docker
```
首次执行命令会使用 `docker build` 构建一个 Docker 镜像,默认构建的 Docker 镜像为 `x86_64` 架构,镜像名称为 `cwcc-spc-x86_64`
如果你想在 `x86_64` 环境下构建 `aarch64` 的 static-php-cli可以使用 qemu 模拟 arm 镜像运行 Docker但速度会非常慢。使用参数`SPC_USE_ARCH=aarch64 bin/spc-alpine-docker`
如果运行后提示需要 sudo 才能运行,执行一次以下命令可授予 static-php-cli 执行 sudo 的权限:
```bash
export SPC_USE_SUDO=yes
```
### 使用系统 PHP 环境
下面是系统安装 PHP、Composer 的一些示例命令。具体安装方式建议自行搜索或询问 AI 搜索引擎获取答案,这里不多赘述。
```bash
# [macOS], 需要先安装 Homebrew. See https://brew.sh/
# Remember change your composer executable path. For M1/M2 Chip mac, "/opt/homebrew/bin/", for Intel mac, "/usr/local/bin/". Or add it to your own path.
brew install php wget
wget https://getcomposer.org/download/latest-stable/composer.phar -O /path/to/your/bin/composer && chmod +x /path/to/your/bin/composer
# [Debian], you need to make sure your php version >= 8.4 and composer >= 2.0
sudo apt install php-cli composer php-tokenizer
```
::: tip
目前 Ubuntu 部分版本的 apt 安装的 php 版本较旧,故不提供安装命令。如有需要,建议先添加 ppa 等软件源后,安装最新版的 PHP 以及 tokenizer、xml、phar 扩展。
较老版本的 Debian 默认安装的可能为旧版本(<= 8.3)版本的 PHP建议先升级 Debian 或使用 Docker 或自带的静态二进制环境。
:::
## 使用 craft 构建(推荐)
使用 `bin/spc craft` 可以使用一个配置文件,一个命令实现自动检查环境、下载源代码、构建依赖库、构建 PHP 及扩展等。
你需要编写一个 `craft.yml` 文件,存放在当前工作目录下。`craft.yml` 可以由 [命令生成器](./cli-generator) 生成,或者手动编写。
手动编写可参考 [craft.yml 配置](../develop/craft-yml.md) 中的注释来编写。我们下面假设你编译一个扩展组合,并选用 PHP 8.4,输出 `cli``fpm`
```yaml
# path/to/craft.yml
php-version: 8.4
extensions: bcmath,posix,phar,zlib,openssl,curl,fileinfo,tokenizer
sapi:
- cli
- fpm
```
然后使用 `bin/spc craft` 命令来编译:
```bash
bin/spc craft --debug
```
如果构建成功,你会在当前目录下看到 `buildroot/bin` 目录,里面包含了编译好的 PHP 二进制文件,或相应的 SAPI。
- cli: Windows 下构建结果为 `buildroot/bin/php.exe`,其他平台为 `buildroot/bin/php`
- fpm: 构建结果为 `buildroot/bin/php-fpm`
- micro: 构建结果为 `buildroot/bin/micro.sfx`,如需进一步与 PHP 代码打包,请查看 [打包 micro 二进制](./manual-build#命令-micro-combine-打包-micro-二进制)。
- embed: 参见 [embed 使用](./manual-build#embed-使用)。
- frankenphp: 构建结果为 `buildroot/bin/frankenphp`
如果中途构建出错,你可以使用 `--debug` 参数查看详细的错误信息,或者使用 `--with-clean` 参数清除旧的编译结果,重新编译。
如使用以上方式仍构建失败,请提交一个 issue附上你的 `craft.yml` 文件、`log/` 目录的压缩包。
## 分步构建命令
如果你有定制化需求,或分开下载、编译 PHP 和依赖库的需求,可以使用 `bin/spc` 命令分步执行。
### 命令 download - 下载依赖包
使用命令 `bin/spc download` 可以下载编译需要的源代码,包括 php-src 以及依赖的各种库的源码。
```bash
# 仅下载要编译的扩展及依赖库(使用扩展名,包含可选库)
bin/spc download --for-extensions=openssl,swoole,zip,pcntl,zstd
# 仅下载要编译的扩展及依赖库(使用扩展名,不包含可选库)
bin/spc download --for-extensions=openssl,swoole,zip,pcntl --without-suggestions
# 仅下载要编译的库(包括其依赖,使用库名,包含可选库,可以和 --for-extensions 组合使用)
bin/spc download --for-libs=liblz4,libevent --for-extensions=pcntl,rar,xml
# 仅下载要编译的库(包括其依赖,使用库名,不包含可选库)
bin/spc download --for-libs=liblz4,libevent --without-suggestions
# 下载资源时,忽略部分资源的缓存,强制下载(如切换特定 PHP 版本)
bin/spc download --for-extensions=curl,pcntl,xml --ignore-cache-sources=php-src --with-php=8.3.10
# 下载资源时,优先下载有预编译包的依赖库(减少编译依赖的时间)
bin/spc download --for-extensions="curl,pcntl,xml,mbstring" --prefer-pre-built
# 下载所有依赖包
bin/spc download --all
# 下载所有依赖包,并指定下载的 PHP 主版本可选8.18.28.38.4,也可以使用特定的版本,如 8.3.10。
bin/spc download --all --with-php=8.3
# 下载时显示下载进度条curl
bin/spc download --all --debug
# 删除旧的下载数据
bin/spc download --clean
# 仅下载指定的资源(使用资源名)
bin/spc download php-src,micro,zstd,ext-zstd
# 设置重试次数
bin/spc download --all --retry=2
```
如果你所在地区的网络不好,或者下载依赖包速度过于缓慢,可以从 GitHub Action 下载每周定时打包的 `download.zip`,并使用命令直接使用 zip 压缩包作为依赖。
依赖包可以从 [Action](https://github.com/static-php/static-php-cli-hosted/actions/workflows/download-cache.yml) 下载到本地。
进入 Action 并选择一个最新成功运行的 Workflow下载 `download-files-x.y` 即可。
```bash
bin/spc download --from-zip=/path/to/your/download.zip
```
如果某个 source 始终无法下载,或者你需要下载一些特定版本的包,例如下载测试版 PHP、旧版本库等可以使用参数 `-U``--custom-url` 重写下载链接,
让下载器强制使用你指定的链接下载此 source 的包。使用方法为 `{source-name}:{url}` 即可,可同时重写多个库的下载地址。在使用 `--for-extensions` 选项下载时同样可用。
```bash
# 例如:指定下载 Alpha 版的 PHP8.5
bin/spc download --all -U "php-src:https://downloads.php.net/~edorian/php-8.5.0alpha2.tar.xz"
# 指定下载旧版本的 curl 库
bin/spc download --all -U "curl:https://curl.se/download/curl-7.88.1.tar.gz"
```
如果你下载的资源不是链接,而是一个 Git 仓库,你可以使用 `-G``--custom-git` 重写下载链接,让下载器强制使用你指定的 Git 仓库下载此 source 的包。
使用方法为 `{source-name}:{branch}:{url}` 即可,可同时重写多个库的下载地址。在使用 `--for-extensions` 选项下载时同样可用。
```bash
# 例如:下载 master 分支的 php-src
bin/spc download --for-extensions=redis,phar -G "php-src:master:https://github.com/php/php-src.git"
# 从 swoole-src 仓库下载 master 分支的最新代码,而不是发行版
bin/spc download --for-extensions=swoole -G "swoole:master:https://github.com/swoole/swoole-src.git"
```
### 命令 doctor - 环境检查
如果你可以正常运行 `bin/spc` 但无法正常编译静态的 PHP 或依赖库,可以先运行 `bin/spc doctor` 检查系统自身是否缺少依赖。
```bash
# 快速检查
bin/spc doctor
# 快速检查,并在可以自动修复的时候修复(使用包管理安装依赖包,仅支持上述提到的操作系统及发行版)
bin/spc doctor --auto-fix
```
### 命令 build - 编译 PHP
使用 build 命令可以开始构建静态 php 二进制,在执行 `bin/spc build` 命令前,务必先使用 `download` 命令下载资源,建议使用 `doctor` 检查环境。
#### 基本用法
你需要先到 [扩展列表](./extensions) 或 [命令生成器](./cli-generator) 选择你要加入的扩展,然后使用命令 `bin/spc build` 进行编译。你需要指定一个编译目标,从如下参数中选择:
- `--build-cli`: 构建一个 cli sapi命令行界面可在命令行执行 PHP 代码)
- `--build-fpm`: 构建一个 fpm sapiphp-fpm用于和其他传统的 fpm 架构的软件如 nginx 配合使用)
- `--build-cgi`: 构建一个 cgi sapicgi可用于传统的 cgi 架构的软件如 apache 配合使用)
- `--build-micro`: 构建一个 micro sapi用于构建一个包含 PHP 代码的独立可执行二进制)
- `--build-embed`: 构建一个 embed sapi用于嵌入到其他 C 语言程序中)
- `--build-frankenphp`: 构建一个 [frankenphp](https://github.com/php/frankenphp) 二进制
- `--build-all`: 构建以上所有 sapi
```bash
# 编译 PHP附带 bcmath,curl,openssl,ftp,posix,pcntl 扩展,编译目标为 cli
bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
# 编译 PHP附带 phar,curl,posix,pcntl,tokenizer 扩展,编译目标为 micro
bin/spc build phar,curl,posix,pcntl,tokenizer --build-micro
```
::: tip
如果你需要重复构建、调试,你可以删除 `buildroot/``source/` 两个目录,这样你可以从已下载的源码压缩包重新解压并构建:
```shell
# remove
rm -rf buildroot source
# build again
bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
```
:::
::: tip
如果你想构建多个版本的 PHP且不想每次都重复构建其他依赖库可以使用 `switch-php-version` 在编译好一个版本后快速切换至另一个版本并编译:
```shell
# switch to 8.4
bin/spc switch-php-version 8.4
# build
bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
# switch to 8.1
bin/spc switch-php-version 8.1
# build
bin/spc build bcmath,curl,openssl,ftp,posix,pcntl --build-cli
```
:::
#### 编译运行选项
在编译过程中,有些特殊情况需要对编译器、编译目录的内容进行干预,可以尝试使用以下命令:
- `--cc=XXX`: 指定 C 语言编译器的执行命令Linux 默认 `musl-gcc``gcc`macOS 默认 `clang`
- `--cxx=XXX`: 指定 C++ 语言编译器的执行命令Linux 默认 `g++`macOS 默认 `clang++`
- `--with-clean`: 编译 PHP 前先清理旧的 make 产生的文件
- `--enable-zts`: 让编译的 PHP 为线程安全版本(默认为 NTS 版本)
- `--no-strip`: 编译 PHP 库后不运行 `strip` 裁剪二进制文件缩小体积
- `--with-libs=XXX,YYY`: 编译 PHP 前先编译指定的依赖库,激活部分扩展的可选功能(例如 gd 库的 libavif 等)
- `--with-config-file-path=XXX` 查找 `php.ini` 的路径(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `--with-config-file-scan-dir=XXX` 读取 `php.ini` 后扫描 `.ini` 文件的目录(在 [这里](../faq/index.html#php-ini-的路径是什么) 查看默认路径)
- `-I xxx=yyy`: 编译前将 INI 选项硬编译到 PHP 内(支持多个选项,别名是 `--with-hardcoded-ini`
- `--with-micro-fake-cli`: 在编译 micro 时,让 micro 的 SAPI 伪装为 `cli`(用于兼容一些检查 `PHP_SAPI` 的程序)
- `--disable-opcache-jit`: 禁用 opcache jit默认启用
- `-P xxx.php`: 在 static-php-cli 编译过程中注入外部脚本(详见下方 **注入外部脚本**
- `--without-micro-ext-test`: 在构建 micro.sfx 后,禁用测试不同扩展在 micro.sfx 的运行结果
- `--with-suggested-exts`: 编译时将 `ext-suggests` 也作为编译依赖加入
- `--with-suggested-libs`: 编译时将 `lib-suggests` 也作为编译依赖加入
- `--with-upx-pack`: 编译后使用 UPX 减小二进制文件体积(需先使用 `bin/spc install-pkg upx` 安装 upx
- `--build-shared=XXX,YYY`: 编译时将指定的扩展编译为共享库(默认编译为静态库)
硬编码 INI 选项适用于 cli、micro、embed。有关硬编码 INI 选项,下面是一个简单的例子,我们预设一个更大的 `memory_limit`,并且禁用 `system` 函数:
```bash
bin/spc build bcmath,pcntl,posix --build-all -I "memory_limit=4G" -I "disable_functions=system"
```
## 命令 micro:combine - 打包 micro 二进制
使用 `micro:combine` 命令可以将上面编译好的 `micro.sfx` 和你的代码(`.php``.phar` 文件)构建为一个可执行二进制。
你也可以使用该命令直接构建一个注入了 ini 配置的 micro 自执行二进制文件。
::: tip
注入 ini 配置指的是,在将 micro.sfx 和 PHP 源码结合前,在 micro.sfx 后追加一段特殊的结构用于保存 ini 配置项。
micro.sfx 可通过特殊的字节来标识 INI 文件头,通过 INI 文件头可以实现 micro 带 INI 启动。
此特性的原说明地址在 [phpmicro - Wiki](https://github.com/easysoft/phpmicro/wiki/INI-settings),这个特性也有可能在未来发生变化。
:::
下面是常规用法,直接打包 php 源码到一个文件中:
```bash
# 在做打包流程前,你应该先使用 `build --build-micro` 编译好 micro.sfx
echo "<?php echo 'hello';" > a.php
bin/spc micro:combine a.php
# 使用
./my-app
```
你可以使用以下参数指定要输出的文件名,你也可以指定其他路径的 micro.sfx 进行打包。
```bash
# 指定输出文件名
bin/spc micro:combine a.php --output=custom-bin
# 使用绝对路径,也可以使用简化参数名
bin/spc micro:combine a.php -O /tmp/my-custom-app
# 指定其他位置的 micro.sfx 进行打包
bin/spc micro:combine a.app --with-micro=/path/to/your/micro.sfx
```
如果想注入 ini 配置项,可以使用下面的参数,从文件或命令行选项添加 ini 到可执行文件中。
```bash
# 使用命令行选项指定(-I 是 --with-ini-set 的简写)
bin/spc micro:combine a.php -I "a=b" -I "foo=bar"
# 使用 ini 文件指定(-N 是 --with-ini-file 的简写)
bin/spc micro:combine a.php -N /path/to/your/custom.ini
```
::: warning
注意,请不要直接使用 PHP 源码或系统安装的 PHP 中的 `php.ini` 文件,最好手动编写一个自己需要的参数配置文件,例如:
```ini
; custom.ini
curl.cainfo=/path/to/your/cafile.pem
memory_limit=1G
```
该命令的注入 ini 是通过在 micro.sfx 后追加一段特殊的结构来实现的,和编译时插入硬编码 INI 的功能不同。
:::
如果要打包 phar只需要将 `a.php` 替换为打包好的 phar 文件即可。但要注意phar 下的 micro.sfx 需要额外注意路径问题,见 [Developing - Phar 路径问题](../develop/structure#phar-应用目录问题)
## 调试
如果你在编译过程中遇到了问题,或者想查看每个执行的 shell 命令,可以使用 `--debug` 开启 debug 模式,查看所有终端日志:
```bash
bin/spc build mysqlnd,pdo_mysql --build-all --debug
```
## 命令 extract - 手动解压某个库
使用命令 `bin/spc extract` 可以解包和拷贝编译需要的源代码,包括 php-src 以及依赖的各种库的源码(需要自己指定要解包的库名)。
例如,我们在下载好资源后,想分布执行构建流程,手动解包和拷贝包到指定位置,可以使用命令。
```bash
# 解压 php-src 和 libxml2 的下载压缩包,解压的源码存放在 source 目录
bin/spc extract php-src,libxml2
```
## 命令 dump-extensions - 导出项目扩展依赖
使用命令 `bin/spc dump-extensions` 可以导出当前项目的扩展依赖。
```bash
# 打印项目的扩展列表传入项目包含composer.json的根目录
bin/spc dump-extensions /path/to/your/project/
# 打印项目的扩展列表,不包含开发依赖
bin/spc dump-extensions /path-to/tour/project/ --no-dev
# 输出为 spc 命令可接受的扩展列表格式(逗号分割)
bin/spc dump-extensions /path-to/tour/project/ --format=text
# 输出为 JSON 列表
bin/spc dump-extensions /path-to/tour/project/ --format=json
# 当项目没有任何扩展时,输出指定扩展组合,而不是返回失败
bin/spc dump-extensions /path-to/your/project/ --no-ext-output=mbstring,posix,pcntl,phar
# 输出时不排除 spc 不支持的扩展
bin/spc dump-extensions /path/to/your/project/ --no-spc-filter
```
需要注意的是,项目的目录下必须包含 `vendor/installed.json``composer.lock` 文件,否则无法正常获取。
## 调试命令 dev - 调试命令集合
调试命令指的是你在使用 static-php-cli 构建 PHP 或改造、增强 static-php-cli 项目本身的时候,可以辅助输出一些信息的命令集合。
- `dev:extensions`: 输出目前所有支持的扩展信息,或者输出指定的扩展信息
- `dev:php-version`: 输出当前编译的 PHP 版本(通过读取 `php_version.h` 实现)
- `dev:sort-config`: 对 `config/` 目录下的配置文件的列表按照字母表排序
- `dev:lib-ver <lib-name>`: 从依赖库的源码中读取版本(仅特定依赖库可用)
- `dev:ext-ver <ext-name>`: 从扩展的源码中读取对应版本(仅特定扩展可用)
- `dev:pack-lib <lib-name>`: 打包指定的依赖库(仅发布者可用)
- `dev:gen-ext-docs`: 生成扩展文档(仅发布者可用)
```bash
# 输出所有扩展
bin/spc dev:extensions
# 输出指定扩展的信息
bin/spc dev:extensions mongodb,curl,openssl
# 输出指定列可选lib-depends, lib-suggests, ext-depends, ext-suggests, unix-only, type
bin/spc dev:extensions --columns=lib-depends,type,ext-depends
# 输出当前编译的 PHP 版本(需要先将下载好的 PHP 源码解压到 source 目录,你可以使用 `bin/spc extract php-src` 单独解压缩源码)
bin/spc dev:php-version
# 排序配置文件 ext.json也可以排序 lib、source
bin/spc dev:sort-config ext
```
## 命令 install-pkg - 下载二进制包
使用命令 `bin/spc install-pkg` 可以下载一些预编译或闭源的工具,并将其安装到 `pkgroot` 目录中。
`bin/spc doctor` 自动修复 Windows 环境时会下载 nasm、perl 等工具,使用的也是 `install-pkg` 的安装过程。
下面是安装工具的示例:
- 下载安装 UPX仅限 Linux 和 Windows: `bin/spc install-pkg upx`
- 下载安装 nasm仅限 Windows: `bin/spc install-pkg nasm`
- 下载安装 go-xcaddy: `bin/spc install-pkg go-xcaddy`
## 命令 del-download - 删除已下载的资源
一些情况下,你需要删除单个或多个指定的下载源文件,并重新下载他们,例如切换 PHP 版本,`2.1.0-beta.4` 版本后提供了 `bin/spc del-download` 命令,可以删除指定源文件。
删除已下载的源文件包含预编译的包以及源代码,名称是 `source.json``pkg.json` 中的键名。下面是一些例子:
- 删除 PHP 8.2 源码并切换下载为 8.3 版本: `bin/spc del-download php-src && bin/spc download php-src --with-php=8.3`
- 删除 redis 扩展的下载文件: `bin/spc del-download redis`
- 删除下载好的 musl-toolchain x86_64: `bin/spc del-download musl-toolchain-x86_64-linux`
## 注入外部脚本
注入外部脚本指的是在 static-php-cli 编译过程中插入一个或多个脚本,用于更灵活地支持不同环境下的参数修改、源代码补丁。
一般情况下,该功能主要解决使用 `spc` 二进制进行编译时无法通过修改 static-php-cli 代码来实现修改补丁的功能。
还有一种情况:你的项目直接依赖了 `crazywhalecc/static-php-cli` 仓库并同步,但因为项目特性需要做出一些专有的修改,而这些特性并不适合合并到主分支。
鉴于以上情况,在 2.0.1 正式版本中static-php-cli 加入了多个事件的触发点,你可以通过编写外部的 `xx.php` 脚本,并通过命令行参数 `-P` 传入并执行。
在编写注入外部脚本时,你一定会用到的方法是 `builder()``patch_point()`。其中,`patch_point()` 获取的是当前正在执行的事件名称,`builder()` 获取的是 BuilderBase 对象。
因为传入的注入点不区分事件,所以你必须将你要执行的代码写在 `if(patch_point() === 'your_event_name')` 中,否则会重复在其他事件中执行。
下面是支持的 patch_point 事件名称及对应位置:
| 事件名称 | 事件描述 |
|------------------------------|-----------------------------------------------------------|
| before-libs-extract | 在编译的依赖库解压前触发 |
| after-libs-extract | 在编译的依赖库解压后触发 |
| before-php-extract | 在 PHP 源码解压前触发 |
| after-php-extract | 在 PHP 源码解压后触发 |
| before-micro-extract | 在 phpmicro 解压前触发 |
| after-micro-extract | 在 phpmicro 解压后触发 |
| before-exts-extract | 在要编译的扩展解压到 PHP 源码目录前触发 |
| after-exts-extract | 在要编译的扩展解压到 PHP 源码目录后触发 |
| before-library[*name*]-build | 在名称为 `name` 的库编译前触发(如 `before-library[postgresql]-build` |
| after-library[*name*]-build | 在名称为 `name` 的库编译后触发 |
| after-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译后触发(如 `after-shared-ext[redis]-build` |
| before-shared-ext[*name*]-build | 在名称为 `name` 的共享扩展编译前触发 |
| before-php-buildconf | 在编译 PHP 命令 `./buildconf` 前触发 |
| before-php-configure | 在编译 PHP 命令 `./configure` 前触发 |
| before-php-make | 在编译 PHP 命令 `make` 前触发 |
| before-sanity-check | 在编译 PHP 后,运行扩展检查前触发 |
下面是一个简单的临时修改 PHP 源码的例子,开启 CLI 下在当前工作目录查找 `php.ini` 配置的功能:
```php
// a.php
<?php
if (patch_point() === 'before-php-buildconf') {
// replace php source code
\SPC\store\FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/sapi/cli/php_cli.c',
'sapi_module->php_ini_ignore_cwd = 1;',
'sapi_module->php_ini_ignore_cwd = 0;'
);
}
```
```bash
bin/spc build mbstring --build-cli -P a.php
echo 'memory_limit=8G' > ./php.ini
```
```
$ buildroot/bin/php -i | grep Loaded
Loaded Configuration File => /Users/jerry/project/git-project/static-php-cli/php.ini
$ buildroot/bin/php -i | grep memory
memory_limit => 8G => 8G
```
对于 static-php-cli 支持的对象、方法及接口,可以阅读源码,大部分的方法和对象都有相应的注释。
一般使用 `-P` 功能常用的对象及函数有:
- `SPC\store\FileSystem`: 文件管理类
- `::replaceFileStr(string $filename, string $search, $replace)`: 替换文件字符串内容
- `::replaceFileStr(string $filename, string $pattern, $replace)`: 正则替换文件内容
- `::replaceFileUser(string $filename, $callback)`: 用户自定义函数替换文件内容
- `::copyDir(string $from, string $to)`: 递归拷贝某个目录到另一个位置
- `::convertPath(string $path)`: 转换路径的分隔符为当前系统分隔符
- `::scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false)`: 遍历目录文件
- `SPC\builder\BuilderBase`: 构建对象
- `->getPatchPoint()`: 获取当前的注入点名称
- `->getOption(string $key, $default = null)`: 获取命令行和编译时的选项
- `->getPHPVersionID()`: 获取当前编译的 PHP 版本 ID
- `->getPHPVersion()`: 获取当前编译的 PHP 版本号
- `->setOption(string $key, $value)`: 设定选项
- `->setOptionIfNotExists(string $key, $value)`: 如果选项不存在则设定选项
::: tip
static-php-cli 开放的方法非常多,文档中无法一一列举,但只要是 `public function` 并且不被标注为 `@internal`,均可调用。
:::
## 多次构建
如果你在本地要多次构建,以下方法可以为你节省下载资源、编译的时间。
- 仅切换 PHP 版本,不更换依赖库版本时,可以使用 `bin/spc switch-php-version` 快速切换 PHP 版本,然后重新运行同样的 `build` 命令。
- 如果你想重新构建一次,但不重新下载源码,可以先 `rm -rf buildroot source` 删除编译目录和源码目录,然后重新构建。
- 如果你想更新某个依赖的版本,可以使用 `bin/spc del-download <source-name>` 删除指定的源码,然后使用 `download <source-name>` 重新下载。
- 如果你想更新所有依赖的版本,可以使用 `bin/spc download --clean` 删除所有下载的源码,然后重新下载。
## embed 使用
如果你想将 static-php 嵌入到其他 C 语言程序中,可以使用 `--build-embed` 构建一个 embed 版本的 PHP。
```bash
bin/spc build {your extensions} --build-embed --debug
```
在通常的情况下PHP embed 编译后会生成 `php-config`。对于 static-php我们提供了 `spc-config`,用于获取编译时的参数。
另外,在使用 embed SAPIlibphp.a你需要使用和编译 libphp 相同的编译器,否则会出现链接错误。
下面是 spc-config 的基本用法:
```bash
# output all flags and options
bin/spc spc-config curl,zlib,phar,openssl
# output libs
bin/spc spc-config curl,zlib,phar,openssl --libs
# output includes
bin/spc spc-config curl,zlib,phar,openssl --includes
```
默认情况下static-php 在不同系统使用的编译器分别是:
- macOS: `clang`
- Linux (Alpine Linux): `gcc`
- Linux (glibc based distros, x86_64): `/usr/local/musl/bin/x86_64-linux-musl-gcc`
- Linux (glibc based distros, aarch64): `/usr/local/musl/bin/aarch64-linux-musl-gcc`
- FreeBSD: `clang`
下面是一个使用 embed SAPI 的例子:
```c
// embed.c
#include <sapi/embed/php_embed.h>
int main(int argc,char **argv){
PHP_EMBED_START_BLOCK(argc,argv)
zend_file_handle file_handle;
zend_stream_init_filename(&file_handle,"embed.php");
if(php_execute_script(&file_handle) == FAILURE){
php_printf("Failed to execute PHP script.\n");
}
PHP_EMBED_END_BLOCK()
return 0;
}
```
```php
<?php
// embed.php
echo "Hello world!\n";
```
```bash
# compile in debian/ubuntu x86_64
/usr/local/musl/bin/x86_64-linux-musl-gcc embed.c $(bin/spc spc-config bcmath,zlib) -static -o embed
# compile in macOS/FreeBSD
clang embed.c $(bin/spc spc-config bcmath,zlib) -o embed
./embed
# out: Hello world!
```

View File

@@ -1,31 +1,5 @@
# 故障排除
使用 static-php-cli 过程中可能会碰到各种各样的故障,这里将讲述如何自行查看错误并反馈 Issue
## 下载失败问题
下载资源问题是 spc 最常见的问题之一。主要是由于 spc 下载资源使用的地址一般均为对应项目的官方网站或 GitHub 等,而这些网站可能偶尔会宕机、屏蔽 IP 地址。
在遇到下载失败后,可以多次尝试调用下载命令。
当下载资源时,你可能最终会看到类似 `curl: (56) The requested URL returned error: 403` 的错误,这通常是由于 GitHub 限制导致的。
你可以通过在命令中添加 `--debug` 来验证,会看到类似 `[DEBU] Running command (no output) : curl -sfSL "https://api.github.com/repos/openssl/openssl/releases"` 的输出。
要解决这个问题,可以在 GitHub 上 [创建](https://github.com/settings/tokens) 一个个人访问令牌,并将其设置为环境变量 `GITHUB_TOKEN=<XXX>`
如果确认地址确实无法正常访问,可以提交 Issue 或 PR 更新地址或下载类型。
## Doctor 无法修复某些问题
在绝大部分情况下doctor 模块都可以对缺失的系统环境进行自动修复和安装,但也存在特殊的环境无法正常使用自动修复功能。
由于系统限制例如Windows 下无法自动安装 Visual Studio 等软件),自动修复功能无法用于某些项目。
在遇到无法自动修复功能时,如果遇到 `Some check items can not be fixed` 字样,则表明无法自动修复。
请根据终端显示的方法提交 Issue 或自行修复环境。
## 编译错误
遇到编译错误时,如果没有开启 `--debug` 日志,请先开启调试日志,然后确定报错的命令。
报错的终端输出对于修复编译错误非常重要。
在提交 Issue 时,请上传终端日志的最后报错片段(或整个终端日志输出),并且包含使用的 `spc` 命令和参数。
如果你是重复构建,请参考 [本地构建 - 多次构建](./manual-build#多次构建) 章节。
<!-- TODO: 按类别整理常见构建失败及解决方法
章节:下载问题 / 编译错误 / 扩展冲突 / Windows 专项 / glibc Linux 专项。
从 v2 troubleshooting.md 迁移并扩充。 -->