
FrankenPHP 贡献者开发指南从源码编译 PHP、构建 Caddy 模块到 GDB 调试的完整工作流【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp本文是 FrankenPHP 项目贡献者与二次开发者的实战指南完整梳理了 docs/ru/CONTRIBUTING.md 中定义的四条核心开发链路带调试符号编译 PHPDocker 与原生两种方式、运行 Go 测试套件、构建并运行内置 Caddy 模块与最小测试服务器、以及针对段错误segmentation fault的本地与 CI 调试方法论。读完本文你将掌握一套可直接复制的开发环境搭建流程并能借助 GDB、strace、docker buildx bake等工具定位 FrankenPHP 这类 Go Ccgo 胶水层 PHP 内核混合项目的疑难问题。开发环境总览dev.Dockerfile 里有什么FrankenPHP 在仓库根目录提供了面向开发者的专用 Docker 镜像定义 dev.Dockerfile。与用于生产发布的标准 Dockerfile 不同该镜像以golang:1.26为基础除了 PHP 编译链autoconf、gcc、re2c、bison等外还预装了整套调试工具gdb、valgrind、clang、cmake、llvm以及开发常用编辑器neovim与zsh。镜像内还做了两处针对调试的默认配置在/root/.gdbinit写入set auto-load safe-path /允许 GDB 加载任意路径下的自动装载脚本后续调试 PHP 段错误时需要在/etc/security/limits.conf放开 core dump 限制保证进程崩溃时能留下可供分析的核心转储文件。镜像还会自动克隆php-src的PHP-8.5分支以--enable-zts线程安全FrankenPHP 的 worker 模式依赖 ZTS 构建、--enable-debug等标志编译 PHP并将结果安装到固定位置见下文“PHP 配置路径”小节最后通过../../go.sh build -buildvcsfalse预构建好带 CGO 标志的 FrankenPHP 二进制。编译 PHP两条路径与关键配置位置方式一使用 DockerLinux构建并进入开发镜像docker build -t frankenphp-dev -f dev.Dockerfile . docker run --cap-addSYS_PTRACE --security-opt seccompunconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev参数含义说明--cap-addSYS_PTRACE允许容器内的 GDB 对进程执行 ptrace是容器内调试的必要能力--security-opt seccompunconfined关闭 seccomp 限制避免调试工具的系统调用被拦截-p 8080:8080 -p 443:443 -p 443:443/udp映射 HTTP/HTTPS 测试端口443 的 UDP 对应 HTTP/3-v $PWD:/go/src/app将仓库根目录挂载为/go/src/app与 dev.Dockerfile 中WORKDIR /go/src/app保持一致源码改动即时生效。容器内 PHP 的固定配置位置如下与 dev.Dockerfile 中--with-config-file-path、--with-config-file-scan-dir、EXTENSION_DIR三处编译参数一一对应用途路径主配置文件 php.ini/etc/frankenphp/php.ini默认即提供一份带开发预设的 php.ini附加配置片段/etc/frankenphp/php.d/*.iniPHP 扩展模块/usr/lib/frankenphp/modules/镜像构建时会把php.ini-development复制为/etc/frankenphp/php.ini并追加zend_extensionopcache.so与opcache.enable1见 dev.Dockerfile因此开箱即用的开发环境就已启用 OPcache。已知陷阱如果你的 Docker 版本低于 23.0构建可能因 dockerignore 模式处理问题而失败。解决方法是在.dockerignore中补充以下两行确保caddy与internal目录被打包进构建上下文!testdata/*.php !testdata/*.txt !caddy !internal方式二不使用 DockerLinux 与 macOS按 docs/compile.md 中的源码编译指引操作并务必在 PHP 的./configure阶段传入--debug标志以生成调试符号、关闭优化便于后续在 GDB 中获得可读的调用栈。运行测试套件进入仓库根目录后直接执行go test -race -v ./...-race启用 Go 竞态检测器race detector-v输出每个用例的详细结果。需要说明的是仓库根目录英文版 CONTRIBUTING.md 建议在执行前先通过php-config导出 cgo 编译/链接标志export CGO_CFLAGS-O0 -g $(php-config --includes) CGO_LDFLAGS$(php-config --ldflags) $(php-config --libs) go test -race -v ./...这是因为 FrankenPHP 通过 cgo 直接嵌入 PHP 内核见 frankenphp.go测试编译期必须能找到 PHP 头文件与库文件。仓库里的 go.sh 脚本正是这些标志的封装——它读取PHP_CONFIG环境变量默认php-config自动拼接--includes、--ldflags、--libs输出并追加-tagsnobadger,nomysql,nopgx构建标签排除 Badger/MySQL/pgx 等可选依赖go.mod 中可见这些依赖均为按需引入因此日常开发优先使用./go.sh build或./go.sh test即可避免手工维护 cgo 标志。构建并运行 Caddy 模块FrankenPHP 以 Caddy 模块的形式对外提供 PHP 服务能力。构建该模块cd caddy/frankenphp/ go build -tags nobadger,nomysql,nopgx cd ../../生成的二进制入口在 caddy/frankenphp/main.go它注册了 Caddy 标准模块、FrankenPHP 的caddy包提供php_server指令注册逻辑见 caddy/caddy.go 中的RegisterDirective(php_server, ...)并顺带捆绑了 Mercure 与 Vulcain 两个 Caddy 模块。运行方式在仓库根目录执行cd testdata/ ../caddy/frankenphp/frankenphp runtestdata/Caddyfile 是随仓库分发的调试用配置全局块开启了debug定义了http://站点通过route实现了目录请求 308 重定向、try_files索引文件解析、encode zstd br gzip压缩以及php phpFiles的 PHP 路由。启动后可用以下命令验证 PHP 是否正常工作curl -vk https://localhost/phpinfo.php实际监听端口与协议以你本地的 Caddyfile 为准测试配置也可按需调整为 HTTP 与 8080 端口。若在 Docker 中运行需要将容器端口绑定到宿主机或直接在容器内执行。除run之外模块还注册了php-cli与php-server两个子命令caddy/php-cli.go 中php-cli模拟 PHP CLI SAPI 执行脚本最终调用frankenphp.ExecuteScriptCLI见 cli.go其用法为frankenphp php-cli script.php [args ...]php-server则是一个极简 PHP 服务模式caddy/php-server.go。最小测试服务器不依赖 Caddy 的验证路径当问题与 Caddy 本身无关时可以使用仓库自带的极简 HTTP 服务器快速复现。它位于 internal/testserver/main.go整个逻辑只有几十行调用frankenphp.Init初始化嵌入的 PHP 运行时在每个请求上通过frankenphp.NewRequestWithContext包装请求并交给frankenphp.ServeHTTP处理监听端口由PORT环境变量指定默认8080。构建与运行cd internal/testserver/ go build cd ../../cd testdata/ ../internal/testserver/testserver验证curl -v http://127.0.0.1:8080/phpinfo.php由于测试服务器直接使用frankenphp包的公开 APIInit/ServeHTTP/NewRequestWithContext它同时也是理解 FrankenPHP 嵌入 API 用法的最小可运行示例。本地构建 Docker 镜像docker buildx bakeFrankenPHP 的镜像发布由 docker-bake.hcl 定义支持多架构矩阵构建。首先查看完整的构建计划docker buildx bake -f docker-bake.hcl --print本地构建 amd64 镜像--load将镜像导入本地 Docker--set *.platform...指定目标平台docker buildx bake -f docker-bake.hcl --pull --load --set *.platformlinux/amd64本地构建 arm64 镜像docker buildx bake -f docker-bake.hcl --pull --load --set *.platformlinux/arm64构建全部架构并推送到镜像仓库--no-cache强制全量重建--push直接推送docker buildx bake -f docker-bake.hcl --pull --no-cache --push从 docker-bake.hcl 的源码结构可以看到默认 target 以「builder/runner × PHP 版本8.2、8.3、8.4、8.5× 基础系统trixie、bookworm、alpine」构成矩阵另有static-builder-musl与static-builder-gnu两个静态构建 target分别对应 static-builder-musl.Dockerfile 与 static-builder-gnu.Dockerfile并支持通过BASE_FINGERPRINT、CREATED等变量控制镜像指纹与可复现构建。调试静态构建的段错误FrankenPHP 同时运行 Go 运行时与 PHP 内核跨 cgo 边界的崩溃段错误常常难以定位。以下是文档给出的完整流程获得带调试符号的静态二进制。可以直接从发布渠道下载 debug 版也可以用 bake 构建docker buildx bake \ --load \ --set static-builder.args.DEBUG_SYMBOLS1 \ --set static-builder.platformlinux/amd64 \ static-builder docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp其中DEBUG_SYMBOLS1是static-builder目标的构建参数控制是否保留符号信息。用调试版二进制替换当前使用的frankenphp正常启动 FrankenPHP也可以直接以gdb --args frankenphp run方式启动另开终端附加到运行中的进程gdb -p pidof frankenphp如进程未停在断点先在 GDB 中执行continue触发崩溃例如发起导致崩溃的 HTTP 请求在 GDB 中输入bt打印完整回溯backtrace将回溯输出复制并附到 Issue 中。由于镜像内已配置set auto-load safe-path /GDB 可以自动加载 PHP 内核的调试符号配合 Go 侧与 C 侧的混合调用栈bt输出即可定位崩溃发生在 PHP 内核还是 cgo 胶水代码。在 GitHub Actions 中调试段错误如果段错误只在 CI 环境出现文档提供了在 GitHub Actions 工作流中临时接入调试的步骤操作对象为.github/workflows/tests.yml打开工作流文件在shivammathur/setup-phpv2步骤的env中开启 PHP 调试符号- uses: shivammathur/setup-phpv2 # ... env: phpts: ts debug: true安装 GDB 并配置其自动装载路径同时忽略 PHP ZTS 的 SIG34 信号PHP 线程调度依赖该信号GDB 默认会打断- name: Set CGO flags run: echo CGO_CFLAGS$(php-config --includes) $GITHUB_ENV - run: | sudo apt install gdb mkdir -p /home/runner/.config/gdb/ printf set auto-load safe-path /\nhandle SIG34 nostop noprint pass /home/runner/.config/gdb/gdbinit - uses: mxschmitt/action-tmatev3tmate步骤会让 CI 任务暂停并开放远程终端便于人工介入调试通过 tmate 终端连接到容器打开根目录的 frankenphp.go启用cgosymbolizer默认被注释掉见第 40 行附近的//_ github.com/ianlancetaylor/cgosymbolizer它能让 GDB 正确解析 cgo 产生的 Go 符号- //_ github.com/ianlancetaylor/cgosymbolizer _ github.com/ianlancetaylor/cgosymbolizer执行go get拉取该模块在容器内用 GDB 运行具体的测试go test -c -ldflags-w gdb --args frankenphp.test -test.run ^MyTest$go test -c会编译出独立的测试二进制frankenphp.test-ldflags-w去掉 DWARF 调试信息以加速加载随后即可用 GDB 单步调试该测试用例修复缺陷后务必回滚上述全部临时改动debug: true、tmate 步骤、cgosymbolizer等保持提交干净。调试辅助命令strace 与系统工具当问题发生在系统调用层面如文件监听、信号处理、死锁文档推荐使用 strace 跟踪进程行为。在 Alpine 等容器中先安装工具apk add strace util-linux gdb跟踪主进程PID 1的全部系统调用但过滤掉高频且噪音大的信号与同步调用strace -e trace!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn -p 1util-linux提供pidof等辅助命令gdb用于前述的调用栈分析。这套组合对排查「进程无响应」「worker 卡死」「文件监听失效」等运行时问题非常有效。文档翻译流程FrankenPHP 的文档采用多语言维护仓库内已包含 cn、es、fr、it、ja、pt-br、ru、tr 等多个语言目录。新增语言遵循以下步骤在 docs 目录下新建以该语言的两位 ISO 代码命名的目录将 docs 根目录下的所有.md文件复制到新目录翻译时始终以英文版为源因为它始终是最新的同时将根目录的README.md与CONTRIBUTING.md复制进去翻译文件内容但不要改动文件名不要翻译以 [!开头的行这是 GitHub 的特殊标记语法如 [!NOTE]、 [!WARNING]为翻译提交 Pull Request在网站仓库中将content/、data/、i18n/目录下的文件复制并翻译到新语言目录翻译新建 YAML 文件中的键值在网站仓库同样提交 Pull Request。进一步学习嵌入式 PHP 的参考实现FrankenPHP 的核心是把 PHP 作为嵌入式库embed SAPI运行贡献者指南为此整理了一批同类嵌入方案作为横向参考包括uWSGI 的 PHP 插件、NGINX Unit 的nxt_php_sapi.c、Go 语言生态中的 go-php 与 GoEmPHP、以及 C 环境下的 PHP 嵌入示例理论层面则推荐 Sara Golemon 的《Extending and Embedding PHP》与关于 TSRMLS_CC 的历史文章用于理解 Zend 线程安全机制。Docker 侧可进一步查阅 Bake 文件定义规范与docker buildx build命令文档对应本文的 docker-bake.hcl 用法。若想深入了解 FrankenPHP 自身的线程模型、状态机与 cgo 边界设计仓库根目录英文版 CONTRIBUTING.md 指引读者阅读 docs/internals.md 这一内部架构文档。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考