Skip to content

依赖声明与 require

Composer 的核心功能是依赖管理。composer.json 中的 requirerequire-dev 字段声明了项目的生产依赖和开发依赖,而 composer require 命令则是日常开发中最常用的交互方式。深入理解依赖声明的机制、传递性依赖解析、平台依赖和冲突处理,是构建可靠 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"
    }
}
特性requirerequire-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-pluginComposer 插件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 install

composer 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/package

require 字段的完整结构

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/log
bash
# 查看完整的依赖树
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].

解决冲突的策略

  1. 升级包 A:检查是否有支持新版本的版本
  2. 降级包 B:使用兼容旧依赖的版本
  3. 寻找替代包:寻找功能类似但不冲突的替代
  4. 使用别名:通过 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-dev

provide 与 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.jsoncomposer.lock 都必须纳入版本控制
  • 团队成员使用 composer install(读取 lock 文件)
  • 只有在需要升级依赖时才使用 composer update
  • 升级后提交更新后的 composer.lock

2. 最小稳定性

json
{
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "vendor/package": "^1.0"
    }
}

稳定性级别(从低到高):

级别说明
dev开发版本(分支代码)
alphaAlpha 版本
betaBeta 版本
RCRelease Candidate
stable稳定版本(默认)

3. 避免依赖过多

bash
# 查看项目总依赖数
composer show | wc -l

# 查看过期的依赖
composer outdated

# 查看已弃用的包
composer outdated --deprecated

# 安全审计(Composer 2.4+)
composer audit

# 查看依赖大小
composer show --tree | head -50

4. 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-authoritative

2. 依赖更新策略

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.0

3. 依赖瘦身

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 支持的各种版本约束语法,精确控制依赖版本范围。

参考链接