Composer install 不会自动发布静态资源,因其仅管理 PHP 依赖下载与自动加载,不处理前端构建、文件复制或资源发布;发布需显式通过 scripts(如 post-install-cmd)调用 PHP/shell 命令完成。

为什么 composer install 不会自动发布静态资源
Composer 本身只管 PHP 依赖的下载与自动加载,它不处理前端构建、文件复制或资源发布。所谓“发布静态资源”,比如把 vendor/package/assets/js/app.js 复制到 public/vendor/package/js/,这属于项目部署流程的一部分,必须显式定义。
常见错误是误以为加了 "type": "library" 或写了 extra.asset-dir 就能自动搬运——这些字段只是给第三方插件(如 hirak/prestissimo 或自定义脚本)提供线索,Composer 自身完全忽略它们。
- Composer 的
scripts是唯一被原生支持的扩展点 - 所有资源复制、构建、符号链接操作,都得靠
scripts+ shell 命令或 PHP 脚本完成 - 执行时机必须明确:用
post-install-cmd还是post-update-cmd?二者触发条件不同,别混用
怎么在 composer.json 里写发布脚本
直接在 "scripts" 下定义一个可执行命令,推荐用 PHP 匿名函数封装逻辑,比 shell 更跨平台、更可控。
例如,要将某个包的 dist/ 目录复制到 public/vendor/foo/:
"scripts": {
"publish-assets": [
"@php -r \"copy(__DIR__.'/vendor/foo/dist/app.css', __DIR__.'/public/vendor/foo/app.css');\"",
"@php -r \"copy(__DIR__.'/vendor/foo/dist/app.js', __DIR__.'/public/vendor/foo/app.js');\""
],
"post-install-cmd": ["@publish-assets"],
"post-update-cmd": ["@publish-assets"]
}
- 用
@php -r可避免写独立 PHP 文件,适合简单复制;复杂逻辑建议拆成scripts/publish-assets.php并调用"publish-assets": "php scripts/publish-assets.php" -
post-install-cmd和post-update-cmd都调用同一命令,确保本地开发和 CI 环境行为一致 - 注意路径拼接:用
__DIR__而非硬编码路径,否则在不同系统或工作目录下会失败
用 symfony/flex 替代手动脚本是否可行
Flex 是 Symfony 生态的插件机制,它通过 recipes 提供预定义的资源发布规则(比如自动软链 assets/ 到 public/),但它只对已注册 recipe 的包生效,且要求项目启用 Flex。
如果你不是 Symfony 项目,或要发布的包没提交 recipe,Flex 不起作用。强行启用 Flex 还可能干扰现有 autoload 或脚本流程。
- Flex 的
copy-from-package功能本质仍是调用内部 PHP 脚本,和手写scripts没有本质区别 - 检查 recipe 是否存在:访问
https://github.com/symfony/recipes/tree/master/{vendor}/{package} - 非 Symfony 项目中引入 Flex 属于“杀鸡用牛刀”,反而增加维护成本
复制资源时容易漏掉的三个细节
静态资源发布看着简单,但线上出问题往往卡在这几个地方:
- 目标目录不存在就报错:执行
mkdir -p public/vendor/foo或在 PHP 脚本里用is_dir() && mkdir(..., 0755, true) - 覆盖已有文件却不校验哈希:用
filemtime()或sha1_file()判断源文件是否更新,避免无谓覆盖导致 CDN 缓存失效 - 权限丢失:Linux/macOS 下复制后文件可能无执行位或组读写权限,需显式
chmod;Windows 下则要注意\路径分隔符引发的copy()失败
真正麻烦的从来不是“怎么复制”,而是“什么时候该跳过、什么时候必须强制、谁来清理旧版本”。这些边界逻辑一旦写死在 Composer 脚本里,后期就很难动态调整。











