依赖声明与 require
Composer 的核心功能是依赖管理。composer.json 中的 require 和 require-dev 字段声明了项目的生产依赖和开发依赖,而 composer require 命令则是日常开发中最常用的交互方式。深入理解依赖声明的机制、传递性依赖解析、平台依赖和冲突处理,是构建可靠 PHP 项目的基础。
前置知识
阅读本节前,建议先了解:
- Composer 的安装与基本命令(参见 Composer 基本命令)
composer.json的基本结构(参见 composer.json 配置)- PHP 命名空间与自动加载(参见 类的自动加载)
基础概念
require vs require-dev
composer.json 中有两个依赖声明区域,它们有明确的职责划分:
json
{
"require": {
"php": "^8.1",
"monolog/monolog": "^2.0",
"guzzlehttp/guzzle": "^7.5"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"phpstan/phpstan": "^1.10",
"squizlabs/php_codesniffer": "^3.7"
}
}| 特性 | require | require-dev |
|---|---|---|
| 用途 | 生产环境必需 | 仅开发/测试环境 |
composer install | 默认安装 | 默认安装 |
composer install --no-dev | 安装 | 不安装 |
| 部署到生产 | 必须包含 | 不需要 |
| 典型包 | 框架、库、SDK | 测试工具、分析工具 |
生产环境部署
生产环境务必使用 composer install --no-dev,避免将测试工具、调试工具等非必要包部署到生产环境,既减小体积又提升安全性。
依赖类型
Composer 支持多种依赖类型,通过 type 字段区分:
| 类型 | 说明 | 示例 |
|---|---|---|
library | 普通库(默认) | monolog/monolog |
project | 完整项目 | laravel/laravel |
metapackage | 空包,仅依赖 | symfony/monolog-bundle |
composer-plugin | Composer 插件 | composer/installers |
composer-installer | 安装器插件 | composer/installers |
语法与命令
composer require 命令
bash
# 基本用法
composer require vendor/package
# 指定版本约束
composer require vendor/package:"^1.0"
# 添加到 require-dev
composer require --dev phpunit/phpunit
# 添加多个包
composer require psr/log psr/http-message psr/container
# 仅写入 composer.json,不安装(CI/CD 中有用)
composer require vendor/package --no-update
# 更新后统一安装
composer installcomposer remove 命令
bash
# 移除生产依赖
composer remove vendor/package
# 移除开发依赖
composer remove --dev phpunit/phpunit
# 移除多个包
composer remove package-a package-b
# 移除并删除不再需要的传递依赖(Composer 2.2+)
composer remove --unused vendor/package
# 移除时显示详细原因
composer remove -vvv vendor/packagerequire 字段的完整结构
json
{
"require": {
"php": "^8.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"lib-curl": ">=7.29.0",
"monolog/monolog": "^2.0 || ^3.0",
"guzzlehttp/guzzle": "^7.5",
"symfony/console": "~6.0",
"laravel/framework": ">=10.0 <11.0"
}
}其中依赖名称有三种类型:
| 类型 | 格式 | 示例 | 说明 |
|---|---|---|---|
| PHP 版本 | php | "php": "^8.1" | PHP 语言版本约束 |
| PHP 扩展 | ext-* | "ext-mbstring": "*" | PHP 扩展依赖 |
| 库依赖 | vendor/package | "monolog/monolog" | Composer 包依赖 |
| 系统库 | lib-* | "lib-curl": "*" | 系统底层库依赖 |
详细说明
传递性依赖
Composer 会自动解析传递性依赖。当你安装 laravel/framework 时,Composer 会递归安装所有它依赖的包:
你的项目
├── laravel/framework
│ ├── laravel/serializable-closure
│ ├── symfony/console
│ │ └── symfony/polyfill-php80
│ ├── symfony/http-foundation
│ │ ├── symfony/deprecation-contracts
│ │ └── symfony/polyfill-mbstring
│ ├── symfony/routing
│ └── ...
├── guzzlehttp/guzzle
│ ├── guzzlehttp/promises
│ ├── guzzlehttp/psr7
│ │ └── psr/http-message
│ └── psr/http-factory
└── monolog/monolog
└── psr/logbash
# 查看完整的依赖树
composer show --tree
# 查看特定包的依赖
composer show --tree laravel/framework
# 查看谁依赖了某个包
composer why psr/log
# 输出:monolog/monolog requires psr/log (^1.0.1 || ^2.0 || ^3.0)依赖冲突与解决
当两个包要求同一依赖的不同版本时,会产生冲突:
bash
# 常见冲突场景
# 包 A 需要 guzzlehttp/guzzle ^6.0
# 包 B 需要 guzzlehttp/guzzle ^7.0
# Composer 无法同时满足
# 查看为什么某个版本不可用
composer why-not guzzlehttp/guzzle:^7.0
# Composer 2 的冲突提示更清晰:
# Problem 1
# - Package-a 1.0.0 requires guzzlehttp/guzzle ^6.0 -> satisfiable by guzzlehttp/guzzle[v6.5.8].
# - Package-b 2.0.0 requires guzzlehttp/guzzle ^7.0 -> satisfiable by guzzlehttp/guzzle[v7.8.0].
# - Can only install one of: guzzlehttp/guzzle[v6.5.8, v7.8.0].解决冲突的策略
- 升级包 A:检查是否有支持新版本的版本
- 降级包 B:使用兼容旧依赖的版本
- 寻找替代包:寻找功能类似但不冲突的替代
- 使用别名:通过
provide字段声明版本别名
平台依赖声明
json
{
"require": {
"php": "^8.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"ext-filter": "*",
"ext-hash": "*",
"ext-session": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"lib-pcre": ">=8.0",
"lib-curl": ">=7.29.0"
}
}扩展依赖版本约束
*表示需要安装该扩展,但版本不限- PHP 扩展的版本号遵循 PHP 版本号,例如
"ext-pdo": ">=8.1"表示 PHP 8.1+ 的 PDO - 使用
composer check-platform-reqs验证当前环境是否满足
bash
# 检查当前 PHP 环境是否满足平台要求
composer check-platform-reqs
# php 8.3.12 success
# ext-json 8.3.12 success
# ext-mbstring * success
# ext-curl 8.3.12 success
# 以严格模式检查
composer check-platform-reqs --no-devprovide 与 replace
json
{
"name": "my/framework",
"provide": {
"psr/http-message-implementation": "1.0",
"psr/container-implementation": "1.0"
},
"replace": {
"old/package": "self.version"
}
}| 字段 | 作用 | 场景 |
|---|---|---|
provide | 声明本包实现了某个接口/抽象包 | 实现 PSR 接口时声明 |
replace | 声明本包替代了某个包 | 包重命名、分支合并 |
conflict | 声明与某个包不兼容 | 互斥的包 |
json
{
"name": "guzzlehttp/guzzle",
"provide": {
"psr/http-client-implementation": "1.0"
},
"conflict": {
"psr/http-client": "<1.0"
}
}suggest 字段
suggest 不强制安装,仅作为推荐提示:
json
{
"suggest": {
"ext-curl": "Needed for HTTP client support",
"ext-redis": "Needed for Redis session handler",
"ext-mongodb": "Needed for MongoDB support",
"symfony/var-dumper": "For better debugging output",
"monolog/monolog": "For logging support"
}
}bash
# 查看所有建议的依赖
composer suggests实战示例
场景一:从零开始的项目依赖声明
json
{
"name": "myorg/myapp",
"description": "My Awesome Application",
"type": "project",
"license": "MIT",
"require": {
"php": "^8.1",
"ext-ctype": "*",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-pdo": "*",
"laravel/framework": "^10.0",
"laravel/tinker": "^2.8",
"guzzlehttp/guzzle": "^7.5",
"intervention/image": "^3.0",
"spatie/laravel-permission": "^5.0"
},
"require-dev": {
"phpunit/phpunit": "^10.0",
"phpstan/phpstan": "^1.10",
"squizlabs/php_codesniffer": "^3.7",
"friendsofphp/php-cs-fixer": "^3.40",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^7.0"
},
"suggest": {
"ext-redis": "Needed for Redis cache and session support",
"ext-pgsql": "Needed for PostgreSQL support"
}
}场景二:库开发的依赖声明
php
<?php
declare(strict_types=1);
// composer.json 示例:一个可复用的库
// {
// "name": "myorg/http-client",
// "description": "A lightweight HTTP client wrapper for PHP 8.1+",
// "type": "library",
// "license": "MIT",
// "require": {
// "php": "^8.1",
// "psr/http-client": "^1.0",
// "psr/http-factory": "^1.0",
// "psr/http-message": "^1.0 || ^2.0"
// },
// "require-dev": {
// "phpunit/phpunit": "^10.0",
// "guzzlehttp/guzzle": "^7.5",
// "nyholm/psr7": "^1.5"
// },
// "provide": {
// "psr/http-client-implementation": "1.0"
// },
// "suggest": {
// "ext-curl": "For better performance (recommended)",
// "guzzlehttp/guzzle": "As a default HTTP client adapter"
// },
// "autoload": {
// "psr-4": {
// "MyOrg\\HttpClient\\": "src/"
// }
// },
// "autoload-dev": {
// "psr-4": {
// "MyOrg\\HttpClient\\Tests\\": "tests/"
// }
// }
// }场景三:开发工具链的依赖管理
bash
# 安装全套开发工具
composer require --dev \
phpunit/phpunit:^10.0 \
phpstan/phpstan:^1.10 \
squizlabs/php_codesniffer:^3.7 \
friendsofphp/php-cs-fixer:^3.40 \
pestphp/pest:^2.0 \
spatie/laravel-ignition:^2.0 \
beyondcode/laravel-dump-server:^1.9
# 安装特定分析规则集
composer require --dev \
phpstan/phpstan-deprecation-rules \
phpstan/phpstan-strict-rules \
thecodingmachine/phpstan-strict-rules场景四:多环境依赖管理
php
<?php
declare(strict_types=1);
/**
* 多环境 composer.json 管理策略
*
* 项目结构:
* ├── composer.json # 基础配置(版本控制)
* ├── composer.local.json # 本地开发覆盖配置(不纳入版本控制)
* └── composer.production.json # 生产环境覆盖配置
*/
// .gitignore 中添加:
// composer.local.json
// composer.lock
// 本地开发环境可创建 composer.local.json:
// {
// "require-dev": {
// "barryvdh/laravel-ide-helper": "^2.12",
// "beyondcode/laravel-dump-server": "^1.9"
// }
// }
// Composer 会自动合并 composer.json 和 composer.local.json:
// composer install # 自动合并
// composer show # 显示合并后的完整依赖bash
# 使用 merge-plugin 管理多配置
composer require --dev wikimedia/composer-merge-plugin
# composer.json
# {
# "extra": {
// "merge-plugin": {
// "include": [
// "composer.local.json",
// "composer.*.json"
// ],
// "recurse": true,
// "replace": false
// }
// }
// }注意事项
1. 依赖版本锁定
bash
# composer.lock 必须纳入版本控制
# 这确保所有团队成员和部署环境使用完全相同的依赖版本
# 查看锁文件中的版本
cat composer.lock | grep -A 5 '"name": "monolog/monolog"'
# 更新锁文件但不更新包
composer update --lock
# 从 lock 文件安装(而非 json 文件)
composer install团队协作规则
composer.json和composer.lock都必须纳入版本控制- 团队成员使用
composer install(读取 lock 文件) - 只有在需要升级依赖时才使用
composer update - 升级后提交更新后的
composer.lock
2. 最小稳定性
json
{
"minimum-stability": "stable",
"prefer-stable": true,
"require": {
"vendor/package": "^1.0"
}
}稳定性级别(从低到高):
| 级别 | 说明 |
|---|---|
dev | 开发版本(分支代码) |
alpha | Alpha 版本 |
beta | Beta 版本 |
RC | Release Candidate |
stable | 稳定版本(默认) |
3. 避免依赖过多
bash
# 查看项目总依赖数
composer show | wc -l
# 查看过期的依赖
composer outdated
# 查看已弃用的包
composer outdated --deprecated
# 安全审计(Composer 2.4+)
composer audit
# 查看依赖大小
composer show --tree | head -504. require 与 require-dev 的选择
bash
# 判断某个包应该是 require 还是 require-dev:
# require(生产环境需要):
composer require laravel/framework
composer require guzzlehttp/guzzle
composer require monolog/monolog
# require-dev(仅开发/测试需要):
composer require --dev phpunit/phpunit
composer require --dev phpstan/phpstan
composer require --dev friendsofphp/php-cs-fixer
composer require --dev mockery/mockery
composer require --dev barryvdh/laravel-ide-helper
# 模糊地带的包:
# IDE 辅助 → require-dev(不影响运行时)
# 调试工具 → require-dev(生产环境不应有)
# 性能分析 → require-dev(仅调试时使用)
# 迁移工具 → require(可能需要在生产运行)最佳实践
1. 生产环境安装命令
bash
# 生产环境标准安装
composer install \
--no-dev \
--optimize-autoloader \
--no-interaction \
--no-progress \
--prefer-dist \
--no-scripts
# 优化自动加载
composer dump-autoload --no-dev --optimize --classmap-authoritative2. 依赖更新策略
bash
# 安全更新:仅更新补丁版本
composer update --prefer-lowest --prefer-stable
# 逐个更新依赖(降低风险)
composer update vendor/package-1
composer update vendor/package-2
# 更新后运行测试
composer update vendor/package && composer test
# 大版本升级前检查兼容性
composer why-not vendor/package:^2.03. 依赖瘦身
bash
# 移除不再使用的开发依赖
composer remove --dev unused/package
# 使用 Composer 2.2+ 的 --unused 选项自动移除
composer remove --unused
# 分析项目依赖体积
composer show --tree | grep -c "^"4. 安全性管理
bash
# Composer 内置安全审计(2.4+)
composer audit
# 查看已知漏洞
composer audit --format=json
# 在 CI/CD 中集成安全检查
# composer audit --no-dev # 仅检查生产依赖下一节
继续学习:版本约束 — 了解 Composer 支持的各种版本约束语法,精确控制依赖版本范围。