provide字段仅在依赖解析阶段声明本包实现某抽象能力,不触发安装或运行时判断,不能用于条件加载;它必须写在被require包的composer.json根级,值为语义化版本约束,修改后需执行composer update --lock生效。

Composer 本身不支持条件加载依赖 —— provide 字段不是用来做“根据 PHP 版本/扩展是否存在来决定装不装某个包”的,它只在 composer install 或 composer update 阶段参与依赖解析,且仅用于声明“我已实现某抽象能力”,不触发运行时判断。
provide 字段根本不能替代条件依赖
很多人误以为加个 "provide": {"ext-redis": "7.4.0"} 就能让 Composer 在没装 redis 扩展时自动跳过相关包,这是错的。实际效果是:ext-redis 只会被当作一个“平台包(platform package)”纳入依赖图,用于满足其他包的 require,但它不会让 Composer 主动检测扩展是否存在,也不会因此跳过安装或禁用功能。
常见错误现象:
- 本地没装
ext-redis,但包里写了"provide": {"ext-redis": "*"},结果运行时报Class 'Redis' not found - 期望“有 ext-redis 就装 A 包,没有就装 B 包”,但
provide完全不控制这个逻辑
真正能做的只有两件事:
- 用
provide声明“我已具备某能力”,让依赖该能力的包能顺利安装 - 用
conflict或replace防止冲突,比如避免同时装两个缓存驱动
哪些场景下 provide 真正有用
它只在「依赖解析阶段」起作用,典型有效场景包括:
- 你写了个自定义日志类,实现了
Psr\Log\LoggerInterface,想被 Laravel 的log门面识别 → 在composer.json里加"provide": {"psr/log-implementation": "*"} - 你封装了一个轻量数据库驱动,兼容
illuminate/database接口但不想引入整个 Laravel 框架 → 加"provide": {"illuminate/database": "^10.0"} - 你想声明当前环境已启用某些 PHP 扩展(如
ext-gd),供其他包做条件依赖判断 →"provide": {"ext-gd": "8.2.0"},注意版本应与实际扩展版本一致
关键点:
- 必须写在被 require 的包的
composer.json根级,根项目里的provide字段会被完全忽略 - 键名必须是 Packagist 上公认的虚拟包名,比如
psr/log-implementation,不是psr/log(那是接口规范包) - 值只能是语义化版本约束,不能是
dev-main或@dev这类分支别名
provide 生效前必须执行 update --lock
修改了 composer.json 中的 provide 字段后,光 composer install 是不够的。Composer 只在生成或更新 composer.lock 时读取并固化 provide 声明。如果你只是改了字段但没跑命令,下游包依然会报错 “xxx is required but not provided”。
工作流任务拆解与模型编排技能。使用场景:(1) 收到复杂工作任务需要拆解为可执行步骤,(2) 需要为不同步骤选择最合适的模型,(3) 需要跟踪工作流进度和模型使用情况,(4) 长时间任务卡住需要问题诊断和解决方案。
正确操作顺序:
- 编辑你的包的
composer.json,确保provide写在顶层、格式正确 - 发布新版本(如
v1.2.0)到 Packagist 或私仓 - 在依赖该项目的父项目中执行
composer update your-vendor/your-package --lock
验证是否生效:
- 运行
composer show --platform查看当前环境中哪些虚拟包已被提供(含 PHP 扩展和provide声明) - 运行
composer why psr/log-implementation看哪个包触发了该虚拟依赖
容易被忽略的运行时陷阱
provide 不等于功能就绪。它只解决 Composer 层的依赖检查,不保证运行时可用。
最常踩的坑:
- 声明了
"provide": {"psr/cache-implementation": "*"},但没 autoload 对应的类,运行时直接Class not found - 提供了
ext-redis,但实际未安装扩展,代码里调用new Redis()仍会崩 - 两个包都提供
psr/http-client-implementation,Composer 随机选了一个,而你代码强依赖 Guzzle 的特定行为
所以必须手动确保:
- 所有被声明实现的接口类,都能通过 PSR-4/PSR-0 或 files autoloading 正确加载
- 运行时做扩展检测(如
extension_loaded('redis'))并抛出有意义的异常,而不是等 fatal error - 如果提供的是框架私有契约(如
laravel/framework),通常还需配合replace或服务提供者注册,否则框架不会识别










