Composer如何在离线打包后分发到多台服务器?(vendor归档方案)

2次阅读

vendor目录不能直接tar因含软链接、.git、dev包及平台扩展;需目标环境执行composer install –no-dev –optimize-autoloader –ignore-platform-reqs后再tar;用composer dump-autoload –classmap-authoritative破除autoload路径依赖。

Composer如何在离线打包后分发到多台服务器?(vendor归档方案)

离线打包时 vendor 目录为什么不能直接 tar -cf?

因为 composer install 生成的 vendor/ 里混着大量软链接(比如 bin/ 下的可执行文件指向 ../vendor/bin/xxx)、.git 目录残留、dev-only 的包(如 phpunit),还有平台相关扩展(如 ext-redis 编译产物)。直接 tar 会把开发机环境“气味”一起打包过去,到目标服务器可能报 Class not foundundefined symbol

正确做法是:在目标服务器相同 OS + PHP 版本的机器上执行打包,且用 --no-dev --optimize-autoloader --ignore-platform-reqs 参数确保干净:

composer install --no-dev --optimize-autoloader --ignore-platform-reqs

再 tar,避免把本地路径、符号链接、测试依赖带进去。

如何让离线包在不同服务器上自动适配 autoload?

Composer 生成的 vendor/autoload.php 默认硬编码了绝对路径(比如 /home/user/project/vendor),一挪位置就失效。这不是 bug,是设计如此——但离线分发时必须破除它。

关键操作只有一步:打包前运行

composer dump-autoload --classmap-authoritative

这会让 autoloader 放弃动态查找,改用预生成的 classmap,彻底脱离路径依赖。同时确保 composer.json 中没写死 autoload.files 绝对路径,所有 psr-4 映射都用相对路径(如 "App": "src/")。

常见错误现象:Class 'ApphttpControllersController' not found —— 八成是没跑 dump-autoload,或者跑了但没加 --classmap-authoritative

分发后 composer install 还要重跑吗?

不需要,也不应该。离线包的核心价值就是跳过 composer install

但要注意三个条件:

  • 打包机和目标机的 PHP_VERSION 和关键扩展(mbstringjsonopenssl)必须一致,否则 vendor/composer/autoload_static.php 里的类定义可能不兼容
  • composer.lock 必须随包一起分发,并放在项目根目录;运行时若检测到 lock 文件变更,某些插件(如 hirak/prestissimo)会悄悄触发网络请求
  • 如果用了 composer-plugin(比如 symfony/flex),它可能在 install 阶段写文件或调 API,这类插件必须提前禁用:"config": {"disable-tls": true, "secure-http": false} 不起作用,得删掉插件或用 --no-plugins

压缩包解压后权限和用户问题怎么处理?

linux 下常见问题是:打包机用 root 打的包,解压后 www-data 用户没权限读 vendor/ 里的 .so 文件或写 cache/ 目录;或者 vendor/bin/ 下脚本丢失 x 权限。

实操建议:

  • 打包前统一 chown 到普通用户(如 www-data:www-data),再 chmod 755 vendor/bin/*
  • tar --owner=0 --group=0 --numeric-owner 打包,避免 uid/gid 错位
  • 解压后立刻运行:
    find vendor/ -type f -name "*.php" -exec chmod 644 {} ;

    find vendor/bin/ -type f -exec chmod 755 {} ;

别指望 umasktar -p 自动搞定——不同发行版默认行为差异太大,手动加固最稳。

最麻烦的其实是 PHP 扩展 ABI 兼容性:同一版本 PHP,但打包机是 ubuntu 22.04(glibc 2.35),目标机是 centos 7(glibc 2.17),ext-igbinary 这种带 .so 的包一加载就 Segfault。这种没法靠打包技巧绕过,只能确保基础环境一致。

text=ZqhQzanResources