FrankenPHP 从源代码编译完整指南:以动态库方式集成 PHP 的构建全流程
2026/9/15 14:19:09 网站建设 项目流程

FrankenPHP 从源代码编译完整指南:以动态库方式集成 PHP 的构建全流程

【免费下载链接】frankenphp🧟 The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp

本篇指南系统讲解如何从源代码自行编译 FrankenPHP——一个基于 Caddy 构建的现代 PHP 应用服务器。核心路线是将 PHP 编译为动态库(libphp)并链接进 Go 二进制,这也是官方推荐的构建方式。读完本文,你将掌握 ZTS 版 PHP 的两种安装路径(Homebrew 与源码编译)、Linux/macOS 下configure标志的确切含义、Brotli 与文件监听等可选依赖的控制方式,以及借助 xcaddy 或纯go build产出frankenphp可执行文件的完整操作步骤。

一、两种构建路线:动态库链接 vs 静态二进制

FrankenPHP 本体是用 Go 编写的,同时通过 cgo 内嵌了 C 语言实现的 PHP 引擎。因此构建时存在两条路线:

  • 动态库方式(本文主题,推荐):先安装一个线程安全(ZTS)的libphp动态库,再编译 Go 应用并链接到该库。二进制体积更小、构建更快,且可以继续使用系统包管理器或自编译的 PHP 扩展。
  • 静态方式:借助 static-php-cli 项目把 PHP 解释器、Caddy 与 FrankenPHP 全部打进一个可移植的单文件二进制,详见 编译静态版本。静态构建适合分发到异构环境,但完全静态的二进制无法动态加载 PHP 扩展,且基于 musl libc 存在一定限制。

从仓库结构看,静态构建的专用产物定义在 static-builder-musl.Dockerfile 与 static-builder-gnu.Dockerfile 中;而本文介绍的动态库路线,其 cgo 链接参数集中在 cgo.go:Linux/macOS 下需要链接-lphp -lm -lutil,Linux 额外需要-ldl -lresolv,macOS 还需要-liconv -ldl。这些库正是由后文安装的libphp提供的。

二、前置条件:安装 PHP(要求 8.2 及以上)

FrankenPHP 支持 PHP 8.2 及更高版本。与常规的 CLI/FPM 构建不同,这里需要的是ZTS(Zend Thread Safety,线程安全)变体的嵌入式libphp,因为 FrankenPHP 会在多个 PHP 线程中并发处理请求。仓库的 Dockerfile 也印证了这一点:其构建环境直接基于 PHP 官方zts镜像系列(如 PHP 8.5 trixie/zts)。

2.1 使用 Homebrew 安装(Linux 和 macOS)

最简单的方式是使用 Homebrew PHP 提供的 ZTS 包。首先确保已安装 Homebrew,然后执行:

brew install shivammathur/php/php-zts brotli watcher brew link --overwrite --force shivammathur/php/php-zts

其中:

  • php-zts:线程安全版 PHP,构建后提供php-configlibphp,是编译 Go 应用所必需的;
  • brotli:可选,提供 Brotli 压缩支持(对应构建标签nobrotli的控制项);
  • watcher:可选,用于 worker 模式下的文件变更检测(对应构建标签nowatcher的控制项)。

brew link --overwrite --force用于确保php-config等工具在 PATH 中指向 ZTS 版本,避免与系统已有的非 ZTS PHP 冲突。

2.2 通过编译 PHP 源码安装

如果你希望精确控制 PHP 的编译选项(例如自行加入扩展),可以按以下步骤从源码构建。先获取 PHP 源代码 并解压:

tar xf php-* cd php-*/

然后为你的平台运行相应的./configure。下面的标志是必需的,在此基础上可以追加其他标志(如编译扩展或附加功能):

Linux
./configure \ --enable-embed \ --enable-zts \ --disable-zend-signals \ --enable-zend-max-execution-timers
Mac

先使用 Homebrew 安装所需与可选的依赖项:

brew install libiconv bison brotli re2c pkg-config watcher echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc

re2cbison是 PHP 构建链的解析器生成工具;新版 bison 位于/opt/homebrew/opt/bison/bin,需要手动加入PATH才能被configure找到。修改后记得重新加载 shell 配置(如source ~/.zshrc)。

然后运行./configure

./configure \ --enable-embed \ --enable-zts \ --disable-zend-signals \ --with-iconv=/opt/homebrew/opt/libiconv/

各必需标志的作用如下,理解它们有助于排错:

标志作用为什么 FrankenPHP 需要
--enable-embed构建嵌入式 SAPI,产出libphp动态库FrankenPHP 通过 cgo 以嵌入式方式调用 PHP
--enable-zts启用 Zend 线程安全FrankenPHP 在多 PHP 线程中并发处理请求
--disable-zend-signals关闭 Zend 引擎的信号处理避免与 Go 运行时的信号处理机制产生冲突
--enable-zend-max-execution-timers启用 Zend 最大执行时间定时器使max_execution_time在嵌入场景下可用
--with-iconv=/opt/homebrew/opt/libiconv/(仅 macOS)指定 iconv 库位置新版 macOS 系统自带 iconv 不可用,需指向 Homebrew 版本
编译并安装 PHP
make -j"$(getconf _NPROCESSORS_ONLN)" sudo make install

make -j"$(getconf _NPROCESSORS_ONLN)"会按 CPU 核数并行编译,缩短构建时间。安装完成后,系统中将出现php-config(输出编译链接 PHP 所需的头文件路径与库参数)和libphp

三、安装可选依赖项

部分 FrankenPHP 功能依赖系统级动态库;如果不安装对应依赖,则必须通过 Go 构建标签禁用该功能,否则编译会失败:

功能依赖项用于禁用的构建标签
Brotli 压缩Brotlinobrotli
文件更改时重启 workerWatcher Cnowatcher

构建标签在仓库源码中随处可见,是理解编译选项的一手证据:

  • caddy/frankenphp/cbrotli.go 顶部是//go:build !nobrotli,仅在该标签未启用时通过空导入注册github.com/dunglas/caddy-cbrotli压缩模块;
  • hotreload.go 的约束是//go:build !nomercure && !nowatcher,即热重载同时依赖 Mercure 与 Watcher;
  • watcher-skip.go 是//go:build nowatcher的占位实现,在禁用 Watcher 后initWatchers会直接返回errWatcherNotEnabled,防止误用。

因此,如果你在 Homebrew 一步中跳过了brotliwatcher,请务必在后续的-tags中加上对应的禁用标签。此外,社区惯例(如 go.sh 与文档命令所示)还会顺带传入nobadger,nomysql,nopgx,用以关闭 Caddy 附带数据库中不需要的 Badger/MySQL/PostgreSQL 驱动,缩小二进制体积、减少依赖。

四、编译 Go 应用

完成 PHP 与可选依赖的准备后,即可编译最终的frankenphp二进制。核心要点是:通过 cgo 让 Go 编译器找到 PHP 的头文件与库,具体由php-config提供。

4.1 使用 xcaddy(推荐)

xcaddy 是 Caddy 官方提供的构建工具,也是官方推荐的编译 FrankenPHP 的方式。它支持轻松挂载自定义 Caddy 模块 与 FrankenPHP 扩展:

CGO_ENABLED=1 \ XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \ CGO_CFLAGS=$(php-config --includes) \ CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \ xcaddy build \ --output frankenphp \ --with github.com/dunglas/frankenphp/caddy \ --with github.com/dunglas/mercure/caddy \ --with github.com/dunglas/vulcain/caddy # 在这里添加额外的 Caddy 模块和 FrankenPHP 扩展

各环境变量与参数的含义:

  • CGO_ENABLED=1:显式开启 cgo(链接 C 语言 PHP 库的前提);
  • XCADDY_GO_BUILD_FLAGS:透传给go build的额外标志。-ldflags='-w -s'去掉 DWARF 调试信息与符号表以减小体积;-tags=nobadger,nomysql,nopgx禁用不需要的数据库模块;
  • CGO_CFLAGS=$(php-config --includes):把 PHP 头文件搜索路径传给 C 编译器;
  • CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)":把 PHP 库的链接参数(如-L... -lphp)传给链接器,对应 cgo.go 中-lphp -lm -lutil等链接需求;
  • --output frankenphp:指定输出文件名;
  • --with github.com/dunglas/frankenphp/caddy:引入 FrankenPHP 的 Caddy 模块(源码见 caddy/module.go 与 caddy/app.go),这是必须包含的核心模块;
  • --with github.com/dunglas/mercure/caddy:引入 Mercure 实时推送模块;
  • --with github.com/dunglas/vulcain/caddy:引入 Vulcain HTTP 推送模块;
  • 注释行提示:如需更多 Caddy 模块或 FrankenPHP 扩展,可继续追加--with项。

上述三个--with模块与仓库内置的 caddy/frankenphp/main.go 完全一致——该入口文件通过空导入注册了caddy/modules/standardfrankenphp/caddymercure/caddyvulcain/caddy。这意味着用 xcaddy 编译时至少应保留frankenphp/caddy,否则产出的二进制将缺少 PHP 处理能力。

[!TIP] 如果你的系统基于 musl libc(Alpine Linux 默认如此)并搭配 Symfony 使用,可能需要增加默认堆栈大小。否则可能遇到如下错误:

PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression

解决办法是把XCADDY_GO_BUILD_FLAGS修改为类似下面的值(堆栈大小按应用需求调整):

XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'

这里通过-extldflags-Wl,-z,stack-size=0x80000传给系统链接器,将线程栈扩展到 512 KiB(0x80000),为 Symfony 的深层调用栈留出空间。

4.2 不使用 xcaddy

如果不希望引入 xcaddy,也可以直接使用go命令编译。先获取源码并进入 Caddy 模块目录:

curl -L https://github.com/php/frankenphp/archive/refs/heads/main.tar.gz | tar xz cd frankenphp-main/caddy/frankenphp

然后执行go build,并像 xcaddy 方式一样提供 cgo 标志与构建标签:

CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx

注意此处必须位于caddy/frankenphp目录,因为该目录下的 main.go 才是装配了 Caddy 与 FrankenPHP 模块的入口;直接构建仓库根目录只会得到不带服务器能力的 Go 库(该库的模块路径为github.com/dunglas/frankenphp,见 go.mod)。与 xcaddy 方式相比,直接go build少了动态追加--with模块的便利性,但产物等价。

五、仓库内置的构建辅助设施

如果希望参照官方 CI/镜像的做法编译,仓库中还提供了一套封装好的辅助脚本与配置:

  • go.sh:官方封装脚本,自动设置-tags=nobadger,nomysql,nopgx,并通过php-config填充CGO_CFLAGS/CGO_LDFLAGS,还额外追加$(dirname "$0")/mtls-cflags.sh的输出——该脚本会在工具链支持时追加-mtls-size=12(针对部分 AArch64 平台的本地执行 TLS 模型优化)。用法形如./go.sh build,可把脚本中的命令作为手动go build的权威参考。
  • Dockerfile:展示了完整的生产构建流水线:安装 cmake、git 及 PHP 扩展依赖(libbrotli-dev、libcurl4-openssl-dev、libsodium-dev 等),从源码编译 e-dant/watcher,随后在caddy/frankenphp目录调用go.sh install并注入版本号与-w -s优化标志。如果你想复刻官方构建环境,这是最直接的模板。
  • dev.Dockerfile/dev-alpine.Dockerfile:面向开发场景的镜像,便于在容器中快速迭代。

六、验证构建产物并投入使用

编译完成后,可以用以下命令确认版本与构建信息(Dockerfile 的 runner 阶段正是这样验证的):

./frankenphp version ./frankenphp build-info

得到可执行文件后,配合 Caddyfile 即可提供服务,例如在站点块中使用php_server指令(指令的具体解析逻辑见 caddy/module.go):

example.com { root * /path/to/app/public php_server }

更完整的配置说明参见 配置文档。此外,自编译的二进制同样支持 worker 模式、热重载与 Mercure 实时推送等特性,分别参见 worker 模式、热重载 与 Mercure;如果你希望把 PHP 应用直接嵌入二进制以分发单一文件,可阅读 embed 文档;不想自行编译的话,也可以直接使用 Docker 镜像。

总结

FrankenPHP 的源码编译分为清晰的三步:安装 ZTS 版 PHP(含libphpphp-config)→ 准备 Brotli/Watcher 等可选依赖(或用构建标签禁用)→ 通过 xcaddy 或go build链接编译 Go 二进制。整个过程中,--enable-zts--disable-zend-signals等 configure 标志与CGO_CFLAGS/CGO_LDFLAGS/-tags参数是成败关键,而 go.sh 与 Dockerfile 则提供了官方认可的参数组合可供对照。掌握这套流程后,你就可以自由定制 Caddy 模块、PHP 扩展,构建出完全贴合自身业务的 FrankenPHP 服务器。

【免费下载链接】frankenphp🧟 The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询