composer outdated 默认混列所有过期包,需结合 composer show --outdated --direct --format=json 与脚本过滤(排除项目自身、自定义仓库及 path repo),才能精准识别 Packagist 上的第三方过期包。

composer outdated 会列出所有过期包,但默认不区分第三方
直接运行 composer outdated 确实能查到更新,但它把项目自身依赖(require)和开发依赖(require-dev)、甚至你自己的私有包都混在一起。第三方包指的其实是 Packagist 上可公开访问、非当前项目源码的包,比如 monolog/monolog、guzzlehttp/guzzle 这类。
关键判断依据是:是否在 composer.json 的 repositories 里显式定义了非-Packagist 源(如 vcs 或 package 类型),没定义的、且不是当前项目 name 的,基本就是第三方。
- 用
composer show --outdated --direct可过滤掉传递依赖,只看顶层声明的包 - 加
--format=json输出结构化数据,方便后续筛选(比如排除"source": "package"类型) - 注意:如果项目用了
pathrepository 指向本地目录,这类包也会被outdated列出,但不属于第三方 —— 需人工或脚本跳过
写一个 shell 脚本过滤出真正的第三方过期包
靠纯 Composer 命令做不到精准识别“第三方”,得补一层逻辑。下面这个脚本只保留满足两个条件的包:1)出现在 require 或 require-dev 中;2)不在 repositories 里定义,且不是当前项目名。
#!/bin/sh PROJECT_NAME=$(jq -r '.name // ""' composer.json 2>/dev/null) OUTDATED_JSON=$(composer show --outdated --direct --format=json 2>/dev/null) <p>if [ -z "$OUTDATED_JSON" ]; then exit 0 fi</p><h1>提取所有 repositories 中的 package names(只处理 type=package 和 type=vcs)</h1><p>REPO_NAMES=$(jq -r '.repositories[]? | select(.type == "package" or .type == "vcs") | .package?.name // .url' composer.json 2>/dev/null | grep '/' | sort -u)</p><p>echo "$OUTDATED_JSON" | jq -r --arg proj "$PROJECT_NAME" --argjson repos "[$REPO_NAMES]" ' .[] | select(.name != $proj and (.name | IN($repos[]) | not)) | "(.name) (.version) → (.latest)" '</p>
这段脚本依赖 jq,适合 CI 或本地快速检查。它不会误报 path repo,也能跳过你手动添加的私有 vcs 包。
- 如果
composer.json没有name字段,$PROJECT_NAME为空,脚本仍安全(select(.name != $proj)不生效) -
jq版本需 ≥1.6,低版本不支持IN()函数,可改用index()替代 - 别忘了给脚本加执行权限:
chmod +x check-thirdparty-outdated.sh
PHP 脚本调用 Composer API 更可控
想集成进 Laravel 命令或 CI 工具链,用 PHP 直接调用 Composer 内部逻辑更稳。核心是复用 Composer\Package\Version\VersionSelector 和 Composer\Repository\InstalledRepositoryInterface,但不必重造轮子 —— 改写 OutdatedCommand 的逻辑即可。
最小可行做法:在项目中新建 scripts/check-thirdparty.php,用 require_once 'vendor/autoload.php' 加载,并手动构造 InstalledRepository 实例。
- 重点过滤点在
$package->getType() !== 'project'且$package->getRepository()->getRepoName() === 'packagist.org' - 避免用
Composer\Factory::create()初始化完整对象,开销大;优先用Factory::createConfig()+IOInterface模拟 - 如果你的项目启用了
composer.lock的packages-dev分离,记得分别检查两组 installed packages
CI 场景下怎么设为失败阈值
检测出过期包不等于要立刻升级,但可以设硬性规则:比如不允许 symfony/* 主版本落后超过 1 个 minor,或禁止 phpunit/phpunit 处于 EOL 状态。
- 用
composer outdated --minor-only限制只看小版本更新,减少噪音 - 结合
grep -E '^(laravel|symfony)/'快速聚焦关键生态包 - 在 GitHub Actions 中,可用
if: contains(steps.outdated.outputs.list, 'monolog/monolog')做条件分支,但更推荐把规则写进 PHP 脚本里统一判断 - 注意:某些包(如
phpstan/phpstan)的稳定版发布节奏慢,outdated显示有新版但实际未标记 stable,得用--all并检查stability字段
真正难的不是列出来,而是判断“该不该升”——比如 doctrine/dbal 从 3.x 升 4.x 是破坏性变更,outdated 不会告诉你这点。所以脚本输出后,还得有人盯住 changelog 和 BC break 标记。











