直接打包 vendor 不可行,因其含绝对路径、本地生成代码及扩展依赖;需锁定依赖、生成位置无关 autoload 并清理构建痕迹才能跨环境运行。

composer install –no-dev –prefer-dist 为什么不能直接打包?
它只是把依赖下载到 vendor/,没处理自动加载路径硬编码、扩展依赖(如 .so)、PHP 版本绑定或二进制工具路径。直接拷走整个 vendor/ 在另一台机器上大概率报 class not found 或 failed to open stream —— 因为 autoload.php 里写的路径是绝对的,或者某些包在安装时生成了本地适配的代码。
vendor 目录本身不是可移植单元,真正要打包的是「运行时快照」
composer 没提供一键 tar 打包命令,但核心思路是:锁定依赖 + 生成位置无关的 autoloader + 清理构建痕迹。关键操作如下:
- 先确保
composer.lock已提交,且不含 dev 分支或require-dev的干扰项(用--no-dev安装) - 运行
composer install --no-dev --optimize-autoloader --classmap-authoritative:前者减少条件判断,后者让 autoloader 完全依赖预生成的 classmap,不查文件系统 - 删掉
vendor/composer/autoload_*.php以外的动态生成文件(如installed.json、ClassLoader.php的调试逻辑),它们可能含绝对路径 - 检查是否有包在
post-install-cmd里写死了/tmp或用户家目录(比如ramsey/uuid的 pecl 扩展检测、ext-protobuf的编译缓存)—— 这类必须手动清理或换为纯 PHP 实现
如何验证打包后的 vendor 是否真能跨环境运行?
别只测 php -r "require 'vendor/autoload.php';"。真实陷阱在运行时路径解析:
- 用
php -d display_errors=1 -d error_reporting=-1 script.php启动,避免静默失败 - 检查是否用了
__DIR__或dirname(__FILE__) + '/vendor'这类硬编码路径(常见于老框架的 bootstrap) - 运行
composer dump-autoload --classmap-authoritative --no-dev后再打包,比 install 更干净——它跳过所有 install hook,只专注生成 autoload - 若项目含 bin 工具(如
vendor/bin/phpunit),确认其 shebang 是#!/usr/bin/env php而非#!/usr/local/bin/php
真正便携的方案其实是「冻结运行时」而非打包 vendor
vendor 目录本质是构建产物,不是部署单元。更可靠的做法:
- 用
composer archive命令(需配置archive-format)导出源码+lock,但不包含 vendor —— 让目标环境重新install(前提是网络和镜像可用) - 用
phpstan或psalm静态扫描,确认没调用getcwd()、realpath()等路径敏感函数 - 如果必须离线部署,把
vendor/+composer.lock+ 一个最小index.php(只 require autoload)打成 tar.gz,解压后第一件事是运行php -d phar.readonly=0 vendor/bin/composer install --no-dev—— 别省这步,它会重写 autoload 中的路径
最常被忽略的点:有些包(如 symfony/flex)会在 install 时往 config/ 写文件,或修改 composer.json。这类行为必须禁用,否则打包后首次运行就破坏结构。