我得坦白说——第一次听到 FrankenPHP 这个名字时,我以为它只是 PHP 圈里又一个时髦的名词。结果完全不是这么回事。
在传统 PHP 部署环境里摸爬滚打多年,折腾 Apache 配置、与性能瓶颈拉扯,直到遇见 FrankenPHP,那感觉就像有人告诉你有一条从未公开过的捷径。
FrankenPHP 不只是另一个应用服务器。它把 PHP 带到了现代 Web 开发的语境里:基于久经考验的 Caddy Web 服务器,原生支持 HTTP/2 与 HTTP/3、自动 HTTPS、实时能力,甚至可以把整个 PHP 应用编译成一个独立可执行的二进制文件。
这篇文章会带你从零开始,用 FrankenPHP 将 PHP 应用容器化,并解释为什么这种方式可能会彻底改变你对 PHP 部署的看法。
在动手之前,先说说“为什么”。传统的 PHP 部署历来都不算省心。
想让一个最简单的应用跑起来,你需要一个 Web 服务器(Apache 或 Nginx)、PHP-FPM、成堆的配置文件、SSL 证书,以及一整套错综复杂的组件。我已经花了无数时间在调配置、解故障的路上。
FrankenPHP 用一个“全能二进制”把这些复杂度拿掉了。更进一步,它开箱即用地支持现代 Web 标准,提供传统栈难以达到的性能特性,同时在部署形态上既支持容器,也支持“打包成一个可执行文件”的极致简化方案。
开始之前,请先安装 Docker。如果还没装,可以去 Docker 官方网站下载。你需要对 PHP 和 Docker 有一点基础认识,但文中我也会边走边解释。
从最简单的起步更容易理解,先建个基础项目:
mkdir my-frankenphp-app && cd my-frankenphp-app
新建一个 index.php
:
<?php
// index.php
?>
<!DOCTYPE html>
<html>
<head>
<title>FrankenPHP Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.container { max-width: 800px; margin: 0 auto; }
.feature { background: #f4f4f4; padding: 20px; margin: 10px 0; border-radius: 5px; }
</style>
</head>
<body>
<div class="container">
<h1>Hello from FrankenPHP!</h1>
<div class="feature">
<h3>Server Information</h3>
<p><strong>PHP Version:</strong> <?= PHP_VERSION ?></p>
<p><strong>Server Software:</strong> <?= $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown' ?></p>
<p><strong>Request Time:</strong> <?= date('Y-m-d H:i:s') ?></p>
</div>
<div class="feature">
<h3>HTTP Protocol</h3>
<p><strong>Protocol:</strong> <?= $_SERVER['SERVER_PROTOCOL'] ?? 'Unknown' ?></p>
<p><strong>HTTPS:</strong> <?= isset($_SERVER['HTTPS']) ? 'Yes' : 'No' ?></p>
</div>
</div>
</body>
</html>
这段示例比“Hello, world!”更有意思,能直观展示 FrankenPHP 的一些能力。
激动人心的部分来了。在项目根目录新建 Dockerfile
:
FROM dunglas/frankenphp:latest
# 拷贝应用代码到容器
COPY . /app
# 设置工作目录
WORKDIR /app
# 暴露端口 80
EXPOSE 80
# 启动 FrankenPHP
CMD ["frankenphp", "php-server", "--root=/app", "--listen=:80"]
这个 Dockerfile 的“简洁”会让你直观感受到差别:
只需拷贝文件,然后跑起来。
构建并运行:
docker build -t my-frankenphp-app .
docker run -p 8080:80 my-frankenphp-app
打开 http://localhost:8080
,你就能看到应用在跑了。值得一提的是:在“看似平平无奇”的背后,FrankenPHP 已经为你启用了 HTTP/2、自动压缩,以及一系列通常需要手动配置才能获得的性能优化。
大多数项目都会有依赖。若你使用 Composer(大概率会),可以这样优化 Dockerfile:
FROM dunglas/frankenphp:latest
# 安装 Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# 先拷贝 composer 文件以利用 Docker 层缓存
COPY composer.json composer.lock* /app/
# 设置工作目录
WORKDIR /app
# 安装依赖
RUN composer install --no-dev --optimize-autoloader --no-interaction
# 再拷贝其余应用代码
COPY . /app
# 暴露端口
EXPOSE 80
# 启动服务
CMD ["frankenphp", "php-server", "--root=/app", "--listen=:80"]
这个顺序利用了 Docker 镜像的分层缓存:当 composer.json
没变时,composer install
的结果可以复用缓存;只有当依赖本身变化时才会重装,避免每次源代码改动都重新安装依赖。
下面这些,是 FrankenPHP 真正“拉开差距”的地方。在传统栈中要么很费劲,要么几乎做不到。
我最喜欢的一点就是 FrankenPHP 处理 HTTPS 的方式。传统配置 SSL 通常需要多步:证书生成、Web 服务器配置、自动续期脚本等。FrankenPHP 要么帮你自动搞定,要么你只需提供证书即可。
开发环境下使用自签证书:
FROM dunglas/frankenphp:latest
COPY . /app
WORKDIR /app
# 拷贝你准备好的证书
COPY certs/cert.pem certs/key.pem /certs/
EXPOSE 443
CMD ["frankenphp", "php-server", "--root=/app", "--listen=:443", "--tls-cert=/certs/cert.pem", "--tls-key=/certs/key.pem"]
运行:
docker run -p 8443:443 my-frankenphp-app
生产环境要自动 HTTPS(真的很“魔法”):
frankenphp php-server --domain yourdomain.com
就这一个命令。FrankenPHP 会自动申请和续期 Let’s Encrypt 证书。我第一次看它跑起来的时候,直呼“这也太省心了”。
真正有意思的地方在这。传统 PHP 的请求-响应是“短生命周期”的,每次请求都会销毁上下文。而 FrankenPHP 的 Worker 模式会在请求之间保留应用内存,这对 Symfony、Laravel 一类框架能显著提速。
创建一个 worker.php
:
<?php
// worker.php
require_once 'vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// 下面的代码在 Worker 启动时只执行一次
$app = new MyApplication();
$cache = new SomeExpensiveCache();
// 在循环中处理请求
while ($request = \FrankenPHP\workerRequest()) {
try {
// 每个请求都会执行到这里,但 $app 和 $cache 会持久化保留
$response = $app->handle($request);
\FrankenPHP\workerResponse($response);
} catch (\Throwable $e) {
\FrankenPHP\workerResponse(new Response('Error: ' . $e->getMessage(), 500));
}
}
更新 Dockerfile:
CMD ["frankenphp", "php-server", "--root=/app", "--worker=/app/worker.php", "--listen=:80"]
性能影响非常可观。根据我的经验,启用 Worker 模式后,很多应用能获得 2–3 倍的吞吐提升,因为那些昂贵的初始化只需执行一次。
FrankenPHP 同样支持利用 PHP Fibers 来做“准异步”。尽管 PHP 传统上并不以异步见长,但 FrankenPHP 让它变得可行:
<?php
// async-example.php
function asyncOperation($id) {
return \FrankenPHP\async(function() use ($id) {
// 模拟一些异步工作
sleep(2);
return "Result for operation $id";
});
}
// 启动多个异步任务
$operations = [];
for ($i = 1; $i <= 5; $i++) {
$operations[] = asyncOperation($i);
}
// 等待全部完成
foreach ($operations as $operation) {
echo $operation() . "\n";
}
这为 PHP 打开了不少原本难以企及的场景。
我认为 FrankenPHP 最具革命性的能力,是把你的 PHP 应用(连同 PHP 运行时与所有依赖)编译进一个静态二进制里。
想想这意味着什么——“在我机器上能跑”的老问题不再出现;没有运行时依赖;部署脚本也大幅简化。
构建二进制前,先做一些生产化处理:
# 复制一份干净的构建目录
mkdir build-app
cp -r . build-app/
cd build-app
# 设置生产环境
echo "APP_ENV=prod" > .env.local
echo "APP_DEBUG=false" >> .env.local
# 安装生产依赖
composer install --no-dev --optimize-autoloader --ignore-platform-reqs
# 移除无关文件
rm -rf tests/ .git/ node_modules/ *.md
# 若是 Symfony 应用,预热缓存
php bin/console cache:warmup --env=prod
创建 static-build.Dockerfile
:
FROM dunglas/frankenphp:static-builder
# 拷贝准备好的应用
COPY . /go/src/app/dist/app
# 设置工作目录
WORKDIR /go/src/app/
# 将应用嵌入,构建静态二进制
RUN EMBED=dist/app/ ./build-static.sh
构建并导出:
docker build -t my-static-app -f static-build.Dockerfile .
docker create --name temp-container my-static-app
docker cp temp-container:/go/src/app/dist/frankenphp-linux-x86_64 ./my-app
docker rm temp-container
现在你已经有一个自包含的二进制:
# 赋予执行权限
chmod +x my-app
# 以 Web 服务器模式启动
./my-app php-server
# 或启用自动 HTTPS
./my-app php-server --domain localhost
# 执行 CLI 命令
./my-app php-cli bin/console list
第一次用这种方式部署时,我真有点惊讶。一个 50MB 左右的文件,取代了曾经需要多个服务、配置文件和依赖管理的复杂流程。
以实战观察,基于 FrankenPHP 的应用通常具有:
需要注意的是:Worker 模式要求你关注内存泄漏与请求间的状态清理。并非所有 PHP 应用的代码都天然适合持久化进程模型。
在生产环境中,我常使用如下 Docker Compose:
version: '3.8'
services:
app:
build: .
ports:
- "80:80"
- "443:443"
environment:
- FRANKENPHP_CONFIG=
{
"apps": {
"http": {
"servers": {
"srv0": {
"listen": [":80", ":443"],
"routes": [{
"handle": [{
"handler": "php_server",
"root": "/app"
}]
}]
}
}
}
}
}
volumes:
- ./certs:/certs
restart: unless-stopped
对于 Kubernetes,这个“单一二进制”的方案尤其诱人:不再需要 init 容器来拉依赖,也不必搞复杂的卷挂载。
FrankenPHP 代表着我们对 PHP 部署与性能认知的一次“重构”。它让 PHP 以现代 Web 的姿态出现,同时保留了 PHP 一直以来的简洁与易用。
无论你选择容器化方案以获取灵活性,还是选择独立二进制以达成部署极简,FrankenPHP 都提供了传统方案难以匹敌的路径。现代协议栈、性能优化与多样化的交付方式组合起来,使它既适合新项目,也适合为既有项目“现代化”。
站在“传统 PHP 部署”的多年老兵视角,我可以很有把握地说:FrankenPHP 不是小修小补,而是对 PHP 开发可能性的重新想象。
动手试试吧。很可能你会跟我一样,发现回不去了。
—— 2025-08