FrankenPHP 已知问题、不兼容扩展与故障排查完全指南
【免费下载链接】frankenphp🧟 The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp
本指南以 FrankenPHP 官方《既知の問題》(docs/ja/known-issues.md)为骨架,汇总了 FrankenPHP 当前已知的扩展兼容性问题、musl libc 静态构建带来的环境陷阱、Docker 下
https://127.0.0.1的 TLS 配置方案、Composer 中@php脚本的绕过方法,以及静态二进制下 TLS/SSL 证书问题的定位与修复。读者可以据此判断自己的 PHP 扩展组合是否安全、复现官方推荐的 Docker 网络配置,并快速定位生产环境中常见的 HTTPS 握手失败与证书校验错误。
不支持的 PHP 扩展
FrankenPHP 基于 Caddy 构建,采用 PHP 的 ZTS(线程安全)运行模式,并为每个请求复用线程。因此,非线程安全的 PHP 扩展无法与 FrankenPHP 兼容。官方文档明确确认了以下扩展不可用:
| 名称 | 原因 | 替代方案 |
|---|---|---|
| imap | 非线程安全 | javanile/php-imap2、webklex/php-imap |
| newrelic | 非线程安全 | 无 |
从仓库的构建脚本(如 Dockerfile)可以看出,FrankenPHP 的镜像基于 PHP 的 ZTS 版本构建(对应CGO_CFLAGS中引用的 PHP 官方ztsDockerfile),这一架构决定了线程安全问题属于硬性约束而非配置层面的问题。遇到上述扩展时,应优先迁移到列表中的纯 PHP 替代库,或改用其他应用服务器方案。
存在已知 Bug 的 PHP 扩展
除了完全不兼容的扩展外,还有一类扩展在 FrankenPHP 下存在已知 Bug 或异常行为。英文版文档(docs/known-issues.md)还补充了以下案例:
| 名称 | 问题描述 |
|---|---|
| ext-openssl | 使用 musl libc 构建的 FrankenPHP 静态二进制时,高负载下 OpenSSL 扩展可能崩溃。建议改用动态链接构建(官方 Docker 镜像即采用动态链接)。该问题在 PHP 上游追踪中。 |
| datadog | 对 FrankenPHP 进行 profiling 时不稳定,由 DataDog 官方追踪。 |
| blackfire | 对 FrankenPHP 的支持处于 beta 阶段,功能尚不完整。 |
| imagick | ImageMagick 的 OpenMP 线程与 FrankenPHP 的线程冲突,可能导致崩溃。可通过\Imagick::setResourceLimit(\Imagick::RESOURCETYPE_THREAD, 1)禁用线程,或以--disable-openmp重新编译 ImageMagick 缓解。静态二进制及官方apt/apk/rpm包已禁用 OpenMP,仅 Docker 镜像与 Homebrew 安装受影响。 |
实操建议:在引入任何新扩展之前,先对照以上两类表格核查兼容性;对 openssl 场景,若必须使用静态二进制,请优先切换到官方 Docker 镜像(基于 Debian、动态链接)部署。
get_browser()性能退化
官方确认 get_browser() 在持续使用后会出现性能退化。原因是该函数针对每个 User Agent 的解析结果本质上是静态的,反复计算纯属浪费。
推荐做法:按 User Agent 维度缓存结果,例如使用 APCu:
function cached_browser(string $userAgent): array|false { $key = 'browser:' . md5($userAgent); if (apcu_exists($key)) { return apcu_fetch($key); } $result = get_browser($userAgent); apcu_store($key, $result, 3600); return $result; }由于结果静态不变,缓存命中率极高,可显著缓解该函数的性能问题。
静态二进制与 Alpine 镜像的 musl libc 兼容性
FrankenPHP 的完全静态二进制以及Alpine 基础镜像(dunglas/frankenphp:*-alpine)使用 musl libc 而非 glibc,以控制二进制体积。这带来一些兼容性差异,最典型的是:
- PHP 的
glob()函数中GLOB_BRACE标志不受支持({a,b}风格的花括号展开会失效)。
// musl 环境下不可用 $files = glob('/app/public/*.{php,html}', GLOB_BRACE); // 兼容写法:分别调用后合并 $files = array_merge( glob('/app/public/*.php') ?: [], glob('/app/public/*.html') ?: [] );建议:官方英文文档还明确提示——遇到问题时优先使用GNU 变体静态二进制或Debian 基础镜像。因此,当业务代码依赖 glibc 特性(如GLOB_BRACE、特定 NSS 行为)时,应直接切换运行载体,而不是在代码里逐一打补丁。
Docker 下使用https://127.0.0.1的 TLS 配置
问题根源
默认情况下 FrankenPHP 只为localhost生成 TLS 证书,这也是本地开发最简单且官方推荐的方式。若坚持使用127.0.0.1作为主机名,可以把服务器名设为127.0.0.1以生成对应证书;但由于Docker 的网络系统(NAT/bridge 网络下容器 IP 与宿主机回环地址不一致),仅设置服务器名不够,访问时会报类似错误:
curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error方案一:Linux 使用 host 网络驱动
Linux 上最简单的方式是使用 host 网络驱动,让容器直接共享宿主机网络栈:
docker run \ -e SERVER_NAME="127.0.0.1" \ -v $PWD:/app/public \ --network host \ dunglas/frankenphp注意:host 网络驱动在macOS 与 Windows 上不受支持,这两种平台需使用下面的方案二。
方案二:macOS / Windows 上把容器 IP 加入 SERVER_NAME
查看 bridge 网络中已分配的容器 IP:
docker network inspect bridge在返回的 JSON 中查看
Containers键下各容器的IPv4Address,取当前最后分配的 IP并+1(若无容器在运行,首个分配地址通常是172.17.0.2)。将预测的 IP 追加到
SERVER_NAME环境变量,同时显式映射 80/443 端口:docker run \ -e SERVER_NAME="127.0.0.1, 172.17.0.3" \ -v $PWD:/app/public \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp[!CAUTION]
172.17.0.3部分必须替换为你的容器实际将被分配的 IP,切勿照抄。
完成上述配置后,即可从宿主机通过https://127.0.0.1访问。
排错:开启调试模式
如果仍然无法访问,可以启用 Caddy 调试模式定位问题:
docker run \ -e CADDY_GLOBAL_OPTIONS="debug" \ -e SERVER_NAME="127.0.0.1" \ -v $PWD:/app/public \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphpCADDY_GLOBAL_OPTIONS会被注入到 Caddy 全局配置块(caddy/frankenphp/Caddyfile中可看到 FrankenPHP 默认使用该配置机制加载全局选项),调试日志会输出 TLS 握手、证书签发与重定向的详细过程,帮助判断是证书问题还是网络问题。
Composer 脚本中的@php引用
Composer 脚本 的@php artisan package:discover --ansi。在 FrankenPHP 环境下这目前会失败,原因有两个:
- Composer 不知道如何调用 FrankenPHP 二进制(它不是标准
php可执行文件); - Composer 有时会通过
-d标志注入 PHP 配置项,而 FrankenPHP 的 CLI 模式目前不支持-d参数。
从源码看,FrankenPHP 提供了php-cli子命令(注册于 caddy/php-cli.go),其本质是调用 cli.go 中的ExecuteScriptCLI,按 CLI SAPI 语义执行 PHP 脚本——因此需要一个包装脚本把-d之类的参数剥离后再转发。
官方推荐的包装脚本(保存为/usr/local/bin/php):
#!/usr/bin/env bash # /usr/local/bin/php args=("$@") index=0 for i in "$@" do if [ "$i" == "-d" ]; then unset 'args[$index]' unset 'args[$index+1]' fi index=$((index+1)) done /usr/local/bin/frankenphp php-cli ${args[@]}然后通过PHP_BINARY环境变量让 Composer 使用这个包装脚本:
export PHP_BINARY=/usr/local/bin/php composer install说明:若你的 FrankenPHP 二进制不在
/usr/local/bin/,请将脚本最后一行的路径替换为实际安装位置。php-cli子命令的完整用法可参考其注册定义:Usage: script.php [args ...](见 caddy/php-cli.go)。
静态二进制的 TLS/SSL 问题排查
典型错误
使用静态二进制时,例如通过 STARTTLS 发送邮件,可能出现如下 TLS 错误:
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages: error:80000002:system library::No such file or directory error:80000002:system library::No such file or directory error:80000002:system library::No such file or directory error:0A000086:SSL routines::certificate verify failed原因与解决步骤
根本原因:静态二进制不打包任何 TLS 证书,OpenSSL 找不到本地 CA 证书库,导致证书链校验失败。
确认 CA 证书的期望位置:执行 openssl_get_cert_locations(),查看
default_cert_file与default_cert_dir指向的路径,并把 CA 证书安装到对应位置。[!WARNING]
Web 上下文与 CLI 上下文的环境变量/配置可能不同,务必在出问题的那个上下文(Web 请求或 CLI 命令)中分别执行
openssl_get_cert_locations()确认。获取 CA 证书:
- 从 cURL 站点下载 Mozilla 提取的 CA 证书包;
- 或直接安装发行版提供的
ca-certificates包(Debian、Ubuntu、Alpine 等均提供)。
通过环境变量指定证书位置(无需安装证书文件的快速方案):
# 设置 TLS 证书环境变量 export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt export SSL_CERT_DIR=/etc/ssl/certs其中
SSL_CERT_FILE指向 PEM 格式的 CA 证书文件,SSL_CERT_DIR指向 OpenSSL 哈希命名的证书目录(通常由发行版自动维护)。在容器或 systemd 单元中部署时,可将这两个变量写入启动环境,避免每次手动 export。
小结
FrankenPHP 的核心优势(ZTS 线程模型、Caddy 深度集成、静态分发)也带来了特定的生态约束。总结本指南的要点:
- 选扩展前先查兼容性表:imap / newrelic 不可用,openssl / datadog / blackfire / imagick 存在已知问题并各有缓解路径;
- musl 静态构建:注意
GLOB_BRACE不可用,遇到 glibc 依赖优先换 GNU 静态二进制或 Debian 镜像; - Docker +
https://127.0.0.1:Linux 用--network host;macOS/Windows 需把容器预测 IP 追加进SERVER_NAME,失败时用CADDY_GLOBAL_OPTIONS="debug"定位; - Composer
@php:用剥离-d参数的包装脚本 +PHP_BINARY环境变量解决; - 静态二进制 TLS:静态包不含 CA 证书,用
openssl_get_cert_locations()定位、安装ca-certificates,或设置SSL_CERT_FILE/SSL_CERT_DIR。
这些已知问题及其官方解法均记录于 docs/ja/known-issues.md 与 docs/known-issues.md,可作为日常开发与生产排障的速查清单。
【免费下载链接】frankenphp🧟 The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考