
用 FrankenPHP 运行 Symfony 应用Docker 部署、Worker 模式、热重载与独立二进制打包全指南【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp本指南以 FrankenPHP 仓库的 docs/symfony.md 为核心系统讲解在 Symfony 项目中使用 FrankenPHP 的完整方案从官方推荐的 Symfony Docker 一键环境到本地 Caddyfile 配置、常驻内存的 Worker 模式、开发期热重载、AssetMapper 资源预压缩、X-Sendfile 大文件下发再到把 Symfony 应用整体打包成独立静态二进制。读完本文你将能独立完成 Symfony 应用在 FrankenPHP 上的开发、部署与分发。FrankenPHP 与 Symfony 的契合点FrankenPHP 是一个基于 Caddy 构建的现代 PHP 应用服务器内置了自动 HTTPS、HTTP/2、HTTP/3、Brotli/Zstandard 压缩、Mercure 实时推送等能力。对 Symfony 开发者而言其核心价值在于三点Worker 模式让 PHP 应用常驻内存、只启动一次请求处理达到毫秒级热重载PHP 代码、模板、前端资源变更后浏览器自动更新接近现代前端工具链的 HMR 体验独立二进制把应用、PHP 解释器、Caddy 服务器打包成单个可执行文件直接分发。下面依次介绍每条实践路径。方式一用 Symfony Docker 快速搭建完整环境对于 Symfony 项目官方推荐直接使用Symfony Docker——这是由 FrankenPHP 作者维护的 Symfony 官方 Docker 方案。它开箱即用地提供了基于 Docker 的完整运行环境包含FrankenPHP 作为 Web 服务器与 PHP 运行时自动 HTTPS 证书本地默认自签名HTTP/2、HTTP/3 协议支持Worker 模式支持开箱即用的热重载默认开启。你只需在项目根目录运行docker compose up -d即可获得完整的开发环境无需手工编写 Caddyfile。这是开发 Symfony 应用时成本最低的入门路径。方式二在本地机器上直接运行 Symfony如果你不想依赖 Docker也可以在本机直接运行。步骤如下安装 FrankenPHP参考仓库根目录 README.md 的安装章节下载预编译二进制、使用 Docker 镜像或自行编译均可。编写 Caddyfile在 Symfony 项目根目录创建名为Caddyfile的文件内容如下# Caddyfile # 服务器域名 localhost root public/ php_server { # 可选启用 worker 模式以获得更好的性能 worker ./public/index.php }启动服务在 Symfony 项目根目录执行frankenphp run从源码实现看php_server指令见 caddy/php-server.go会自动生成一整套路由把请求重写到index.php、将.php路径交给 PHP 处理器、其余静态文件交给file_server并默认叠加 zstd/br/gzip 压缩编码——这也解释了为什么一个极简的php_server块就能跑起完整的 Symfony 应用。更进一步的性能调优可参考 性能文档。Symfony 的 Worker 模式原理与版本要求Worker 模式的核心思想是应用只启动一次并常驻内存之后的每个请求由 FrankenPHP 直接喂给这个常驻进程处理从而省去每次请求的引导bootstrap开销。更完整的原理说明见 Worker 模式文档。由于 Worker 模式下进程跨请求存活框架必须负责在请求间重置状态。从 Symfony 7.4 开始FrankenPHP Worker 模式得到原生支持无需任何额外依赖。如果使用更早的 Symfony 版本则需要安装 PHP Runtime 项目提供的 FrankenPHP Symfony 运行时包composer require runtime/frankenphp-symfony通过 Docker 启动 Worker设置FRANKENPHP_CONFIG环境变量并指定入口脚本即可docker run \ -e FRANKENPHP_CONFIGworker ./public/index.php \ -e APP_RUNTIMERuntime\\FrankenPhpSymfony\\Runtime \ -v $PWD:/app \ -p 80:80 -p 443:443 -p 443:443/udp \ dunglas/frankenphp这里有两个关键点FRANKENPHP_CONFIGworker ./public/index.php告诉 FrankenPHP 以 Worker 模式运行入口为public/index.phpAPP_RUNTIMERuntime\FrankenPhpSymfony\Runtime让 Symfony 使用 FrankenPHP 专用运行时来接管请求循环。在反斜杠被 shell 解析前务必正确转义如\\。Worker 数量与生命周期控制从 Worker 模式文档 可以看到几个与 Symfony 部署直接相关的默认行为默认启动 2 个 worker / CPU如需调整可在FRANKENPHP_CONFIG中追加数字例如worker ./public/index.php 42表示启动 42 个 worker按请求数重启由于 PHP 并非为长驻进程设计部分库与旧代码存在内存泄漏可通过环境变量MAX_REQUESTS限制单个 worker 处理的最大请求数达到阈值后由脚本自行退出、由 FrankenPHP 拉起新进程手动重启启用 Caddy 管理接口后可 POST 到http://localhost:2019/frankenphp/workers/restart优雅重启所有 worker失败保护worker 以非零码退出时 FrankenPHP 会按指数退避策略重启若短时间连续失败次数过多例如脚本存在语法错误会以too many consecutive failures错误退出。最大连续失败次数可通过 Caddyfile 全局配置max_consecutive_failures调整对应源码见 caddy/workerconfig.go。使用命令行方式启动 Worker使用独立二进制时可直接用php-server命令的--worker选项frankenphp php-server --worker ./public/index.php配合--watch可在文件变化时自动重启 workerfrankenphp php-server --worker ./public/index.php --watch/path/to/your/app/**/*.php--watch的 glob 模式匹配到.php文件变更即触发 worker 重启这一能力常用于开发期与热重载配合使用。Worker 模式下的状态管理要点Worker 模式下静态变量、类静态属性、全局变量以及内存缓存都会跨请求持久化这是其高性能的来源但也是风险所在。Superglobals 方面$_GET、$_POST、$_COOKIE、$_FILES、$_SERVER、$_REQUEST会在请求间自动重置但$_ENV目前不会在请求间重置因此不要把请求相关的敏感数据塞进$_ENV。对于 Symfony 开发者正确做法是持有请求态的服务应实现Symfony\Contracts\Service\ResetInterface这样 Symfony kernel 会在每个请求结束后调用其reset()方法完成状态清理。框架自身的大部分状态会自动重置但你自己业务代码中的服务仍需按此约定实现。审计 Worker 兼容性Igor PHP在把 Worker 模式推向生产前建议先用Igor PHP做静态审计。它是一款专门扫描 Symfony 项目状态泄漏问题的静态检查器能发现缺少ResetInterface的服务未重置的有状态属性可变的局部静态变量exit()/die()调用对 superglobals 的写入。它既审计你的应用代码也审计vendor/中声明的服务。安装与使用composer require --dev igor-php/igor-php vendor/bin/igor-php .为 Symfony 启用热重载什么是热重载FrankenPHP 内置热重载功能完整说明见 热重载文档它监听工作目录的文件变化PHP、模板、JS、CSS 等通过内置的 Mercure hub 向浏览器推送更新。浏览器端若加载了 Idiomorph则会做 DOM 变形保留滚动位置与输入状态否则退化为整页刷新。注意该功能仅用于开发环境切勿在生产环境开启——它既不安全会暴露内部细节也会拖慢应用。在 Symfony Docker 中使用Symfony Docker 中热重载默认已启用开箱即用无需任何配置。不使用 Symfony Docker 时手动启用需要在 Caddyfile 中同时启用 Mercure 与hot_reload子指令localhost mercure { anonymous } root public/ php_server { hot_reload worker ./public/index.php }然后在 Symfony 的templates/base.html.twig中加入如下代码{# templates/base.html.twig #} {% if app.request.server.has(FRANKENPHP_HOT_RELOAD) %} meta namefrankenphp-hot-reload:url content{{ app.request.server.get(FRANKENPHP_HOT_RELOAD) }} script srchttps://cdn.jsdelivr.net/npm/idiomorph/script script srchttps://cdn.jsdelivr.net/npm/frankenphp-hot-reload/esm typemodule/script {% endif %}其中FRANKENPHP_HOT_RELOAD环境变量由 FrankenPHP 注入指向可供浏览器订阅的 Mercure Hub URL前端库frankenphp-hot-reload负责订阅、后台抓取最新页面并做 DOM 变形。最后回到项目根目录运行frankenphp run与 Worker 模式的组合使用如果同时使用 Worker 模式需要注意PHP 代码常驻内存意味着单纯刷新浏览器看不到代码变更。正确组合是hot_reload文件变化时刷新浏览器worker块内的watch子指令文件变化时重启 worker以加载新代码。两者配合才能获得完整的开发体验localhost mercure { anonymous } root public/ php_server { hot_reload worker { file ./public/index.php watch } }关于浏览器端保留特定 DOM 节点如果页面中存在需要跨刷新保留的元素例如 Symfony Web Debug 工具栏这类调试工具可给该元素加上data-frankenphp-hot-reload-preserve属性。预压缩静态资源AssetMapper Brotli/ZstandardSymfony 的 AssetMapper 组件可以在部署阶段对资源做 Brotlibr与 Zstandardzstd预压缩。FrankenPHP 通过 Caddy 的file_server可以直接下发这些预压缩文件从而省去请求时的实时压缩开销。编译并压缩资源php bin/console asset-map:compile更新 Caddyfile让/assets/*路径使用预压缩文件# Caddyfile localhost assets path /assets/* file_server assets { precompressed zstd br gzip } root public/ php_server { worker ./public/index.php }precompressed指令的作用是当客户端声明支持对应编码时优先查找并直接下发app.css.zst、app.css.br这类预压缩版本否则回退到原文件。zstd/br/gzip 的优先级按列出顺序生效这正好与 caddy/php-server.go 中默认压缩编码的优先级zstd → br → gzip保持一致。高效服务大文件X-Sendfile / X-Accel-Redirect有些场景必须先执行 PHP 代码访问控制、统计、自定义响应头等再下发大文件但用 PHP 流式读取大文件既不高效又吃内存。FrankenPHP 支持在 PHP 执行完毕后把静态文件的下发委托给 Web 服务器完成——这就是 Apache 生态的X-Sendfile、NGINX 生态的X-Accel-Redirect完整说明见 X-Sendfile 文档。以下示例假设 Symfony 项目文档根为public/而受保护的文件存放在public/之外的private-files/目录中。1. 配置 Caddyfile在php_server前加入如下配置在 FrankenPHP 的 Caddyfile 中启用 X-Accel-Redirectroot public/ # ... # Symfony、Laravel 等使用 Symfony HttpFoundation 组件的项目需要这两行 request_header X-Sendfile-Type x-accel-redirect request_header X-Accel-Mapping ../private-files/private-files intercept { accel header X-Accel-Redirect * handle_response accel { root private-files/ rewrite * {resp.header.X-Accel-Redirect} method * GET # 移除 PHP 设置的 X-Accel-Redirect 头以增强安全性 header -X-Accel-Redirect file_server } } php_server这里X-Accel-Mapping把内部路径../private-files映射为公开别名/private-filesintercept块则拦截带X-Accel-Redirect头的响应并转交给file_server真正下发文件。2. 在 Symfony 控制器中返回文件Symfony HttpFoundation 组件原生支持该特性。配置好上面的 Caddyfile 后HttpFoundation 会自动确定X-Accel-Redirect头的正确值并加入响应use Symfony\Component\HttpFoundation\BinaryFileResponse; BinaryFileResponse::trustXSendfileTypeHeader(); $response new BinaryFileResponse(__DIR__./../private-files/file.txt); // ...调用trustXSendfileTypeHeader()后BinaryFileResponse会根据请求头中的X-Sendfile-Type即上面配置的x-accel-redirect和X-Accel-Mapping生成正确的转发头从而把大文件的下发交给 CaddyPHP 进程不承担文件传输负担。把 Symfony 应用打包成独立二进制借助 FrankenPHP 的应用嵌入能力详见 应用嵌入文档可以把 Symfony 应用连同 PHP 解释器、Caddy Web 服务器一起打包成单个静态自包含二进制直接分发到服务器运行无需在目标机器上安装 PHP 或 Web 服务器。1. 准备应用# 导出项目去掉 .git/ 等目录 mkdir $TMPDIR/my-prepared-app git archive HEAD | tar -x -C $TMPDIR/my-prepared-app cd $TMPDIR/my-prepared-app # 设置正确的环境变量 echo APP_ENVprod .env.local echo APP_DEBUG0 .env.local # 删除测试等非必要文件以减小体积 # 也可以在 .gitattributes 中为这些文件配置 export-ignore 属性 rm -Rf tests/ # 安装生产依赖 composer install --ignore-platform-reqs --no-dev -a # 优化 .env composer dump-env prod2. 创建静态构建 Dockerfile在应用仓库中创建static-build.Dockerfile# static-build.Dockerfile FROM --platformlinux/amd64 dunglas/frankenphp:static-builder-gnu # 如果打算在 musl-libc 系统上运行改用 static-builder-musl # 复制你的应用 WORKDIR /go/src/app/dist/app COPY . . # 构建静态二进制 WORKDIR /go/src/app/ RUN EMBEDdist/app/ ./build-static.sh[!CAUTION]注意部分.dockerignore文件例如 Symfony Docker 默认自带的.dockerignore会忽略vendor/目录和.env文件导致它们无法进入构建镜像。构建前务必调整或删除.dockerignore。3. 构建并提取二进制docker build -t static-symfony-app -f static-build.Dockerfile .docker cp $(docker create --name static-symfony-app-tmp static-symfony-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-symfony-app-tmp4. 启动服务./my-app php-server打包后可用的能力还包括见 应用嵌入文档在应用根目录放置自定义Caddyfile与php.ini二进制启动时会自动加载——php-server命令会检测并加载内嵌应用目录中的这两个文件对应实现见 caddy/php-server.go带 Worker 入口启动./my-app php-server --worker public/index.php指定域名以启用自动 HTTPSLets Encrypt、HTTP/2、HTTP/3./my-app php-server --domain localhost直接运行内嵌的 PHP CLI 脚本./my-app php-cli bin/console分发优化Linux 下构建时可设COMPRESS1使用 UPX 压缩二进制macOS 下推荐用xz压缩后分发。更完整的选项说明及为其他操作系统构建二进制的方法见 应用嵌入文档。小结围绕 Symfony 与 FrankenPHP 的组合本文覆盖了五条核心实践路径场景推荐方案关键配置开箱即用的完整环境Symfony Docker自带 Worker、HTTPS、HTTP/2/3、热重载本地运行Caddyfile frankenphp runphp_serverworker ./public/index.php生产性能Worker 模式FRANKENPHP_CONFIG/--workerResetInterface状态清理开发体验热重载mercurehot_reload模板注入热重载脚本静态资源与文件AssetMapper 预压缩 X-Sendfileprecompressed、interceptBinaryFileResponse分发部署独立静态二进制static-builder-gnuEMBEDdist/app/相关仓库证据索引docs/symfony.md、Worker 模式文档、热重载文档、X-Sendfile 文档、应用嵌入文档、性能文档、php-server 命令实现、worker 配置实现。按上述步骤实践即可完成从开发到生产再到分发的完整闭环。【免费下载链接】frankenphp The modern PHP app server项目地址: https://gitcode.com/GitHub_Trending/fr/frankenphp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考