flex 通过监听 post-package-install/update 事件拦截 composer require,检测匹配 recipe 后下载 zip、校验冲突、按 manifest.json 复制文件并执行命令;不修改包代码,只管理配置。

Flex 怎么拦截并修改 composer require 行为
Flex 本质是一个 Composer 插件,不是 Symfony 框架的一部分。它通过注册 post-package-install 和 post-package-update 事件监听器,在你执行 composer require 或 composer update 后立刻介入。
关键点在于:它不改包本身的代码,只管“配”。一旦检测到安装的包在 flex.symfony.com 上有对应 recipe(比如 symfony/mailer:6.4),就触发三步操作:
- 下载该 recipe 的 ZIP 包(含
manifest.json和模板文件) - 比对目标路径是否存在且未被修改;若已存在且内容不同,跳过写入,仅标为 ⚠️ 冲突
- 按
manifest.json中的copy-from/copy-to规则复制文件,并执行install字段定义的命令(如php bin/console assets:install)
recipe 没生成 config/packages/xxx.yaml?先查这三处
装了 symfony/notifier 却没出现 config/packages/notifier.yaml,常见原因不是 Flex 失效,而是 recipe 被拦住了:
-
composer config extra.symfony.allow-contrib设为false→ 社区包(contrib类型 recipe)全被屏蔽,删掉这行或设为true -
composer recipes输出里该包状态是 ❌ → 说明当前包版本没匹配到任何 recipe(比如用了 dev-main 或太新的 alpha 版,官方 recipe 还没收录) - 手动建过同名文件(如提前写了
config/packages/notifier.yaml)→ Flex 认为“你已有配置”,直接跳过,状态显示 ⚠️
注意:composer recipes 是唯一可信的状态看板,别靠文件是否存在来判断 recipe 是否生效。
Flex 如何把 symfony/webapp-pack “解开”成十几个具体包
当你运行 composer require symfony/webapp-pack,Flex 并不真的安装这个包,而是启动 Unpacker 流程:
- 识别
type: symfony-pack→ 进入解锁逻辑 - 读取该 pack 的
composer.json中的require列表(如symfony/framework-bundle、symfony/twig-bundle等) - 根据项目当前的
symfony/flex版本和extra.symfony.require配置,自动约束各依赖的版本范围(例如转成^6.4而非*) - 把 unpack 后的包列表写回你项目的
composer.json,再触发一次真正的composer install
所以你看到的 composer.json 里最终不会留 symfony/webapp-pack,只有它展开后的实际依赖 —— 这就是“元包不落地”的设计意图。
本地覆盖 recipe 时路径和占位符必须严格匹配
想改掉 symfony/mailer 默认生成的 mailer.yaml?别直接编辑,否则下次 composer update 可能被冲掉。正确做法是放一个本地 recipe:
- 路径必须为:
config/recipes/symfony/mailer/6.4/manifest.json(大小写、斜杠、版本号缺一不可) -
manifest.json至少要包含:{"copy-from": ".", "copy-to": "."} - 同目录下放你定制的
config/packages/mailer.yaml模板,支持%%APP_ENV%%、%%MAILER_DSN%%这类占位符,Flex 会自动替换 - 注意:本地 recipe 优先级高于远程,但 Flex 不校验内容合法性 —— 写错路径或漏字段会导致静默失败
recipe 不是黑盒魔法,它是可提交、可审查、可 diff 的 JSON 指令集。真正容易被忽略的是:它只在安装/更新时运行一次,之后完全不参与运行时逻辑 —— 配置是否生效,终究取决于你最终写进 config/ 目录里的那些 YAML 文件。











