github actions 中直接运行 composer install 必然失败,因 ubuntu-latest 镜像默认无 php 和 composer;必须组合使用 shivammathur/setup-php@v2(安装指定 php 版本及扩展如 mbstring、xml、zip)和 php-actions/composer@v6(按需下载校验指定版本 composer 二进制并注入 path),同时严格缓存 vendor/ 与 ~/.composer/cache(key 含 composer.lock 哈希、php 版本等),并添加 --no-interaction --prefer-dist --optimize-autoloader 参数确保稳定高效执行。

GitHub Actions 里直接写 composer install 必然失败——ubuntu-latest 镜像默认不带 PHP,更没有 Composer;不是配置错,是根本不存在。
为什么 setup-php + php-actions/composer 是当前最稳组合
官方 shivammathur/setup-php@v2 只装 PHP 和扩展,不附带 Composer;而 php-actions/composer@v6 是专为 CI 设计的轻量动作:它按当前 PHP 版本下载对应二进制、跳过全局安装、自动校验 composer.json 格式,并支持指定版本(如 composer-version: '2.5.8')。
- 别在
run步骤里写sudo apt install composer:Ubuntu 的包常是 2.0.x,且不保证启用ext-zip等必需扩展 - 手动用
curl装容易绕过缓存、校验缺失,还可能因 TLS 或镜像源不稳定中断 -
php-actions/composer把二进制放./bin/composer并自动注入PATH,避免和系统 PATH 冲突
PHP 版本与扩展必须手写全,不能靠“默认”
setup-php 默认不启用任何扩展,而 Laravel、Symfony 等主流框架硬依赖 mbstring、xml、zip、pdo、curl —— 缺一个,composer install 就卡在 “Your requirements could not be resolved”。
-
php-version必须和composer.json中"php": "^8.1"完全兼容,否则解析器直接拒绝执行 -
extensions列表必须显式写全,例如:mbstring, xml, zip, pdo, pdo_mysql, curl - 若项目用了
ext-redis或ext-sodium,也得加进去,否则测试阶段才爆错,白跑一轮 CI
缓存 vendor 和 ~/.composer/cache 必须同时做
只缓存 vendor/,下次 composer install 仍要重下 ZIP 包;只缓存 ~/.composer/cache,则每次还要重新解压软链进 vendor/。两者缺一不可。
- 缓存
vendor/的 key 应含composer.lockhash、PHP 版本、Composer 版本,例如:composer-${{ hashFiles('**/composer.lock') }}-${{ matrix.php-version }}-${{ steps.composer-version.outputs.version }} - 缓存
~/.composer/cache的 key 至少含composer.lockhash,路径写绝对路径:${{ github.workspace }}/vendor和${{ github.home }}/.composer/cache - 务必在
composer install前加--no-interaction --prefer-dist --optimize-autoloader,否则缓存命中后仍可能因交互提示或 dev-only 包失败
私有包认证和 --no-dev 的使用时机很关键
CI 中访问 GitHub Packages 或私有 Git 仓库,必须注入 token;但 --no-dev 不是万能开关,用错阶段会导致脚本找不到命令或测试无法运行。
- 构建/打包 job:用
composer install --no-dev --optimize-autoloader,减体积、提速 autoload - 测试 job:必须去掉
--no-dev,否则vendor/bin/phpunit根本不存在 - 私有包需在
composer install前配置认证:composer config github-oauth.github.com ${{ secrets.GITHUB_TOKEN }}
最容易被忽略的是 composer.lock 必须提交到 Git,且缓存 key 里必须用它的 hash——不是 composer.json,也不是文件名。一旦 lock 文件更新但缓存没失效,CI 就会静默装错依赖,问题往往在运行时才暴露,排查成本极高。











