Composer 依赖
这一课你将能做到
- 用 composer init 和 require 管理项目依赖,看懂 composer.json 每个字段
- 分清 install / update / dump-autoload 三个命令的职责,正确对待 composer.lock
- 配置 PSR-4 自动加载,把上一课的命名空间知识接到真实工程上
为什么要学
Composer 之于 PHP,就是 Maven + Gradle 之于 Java:下载依赖、解析版本冲突、生成自动加载器,全靠它。现代 PHP 项目没有「手动下载类文件」这回事——composer require monolog/monolog 一条命令,包及其依赖自动就位,vendor/autoload.php 一行 require,全部类按需加载。国内镜像(阿里云、腾讯云)也已成熟,无需担心网络。
人话:Packagist 是「应用商店」,composer.json 是「购物清单」,composer.lock 是「已安装快照」,vendor 目录是「快递箱」。学会这套流程,你就具备了读任何现代 PHP 项目的前提。
先建立图像
Composer 的三层结构:
packagist.org(中央仓库,存元数据)
▲ require monolog/monolog
composer.json ← 你声明「我要什么」
composer.lock ← 解析后的精确版本快照(每个包的 commit hash)
vendor/ ← 实际代码 + vendor/autoload.php
与 Maven 的对应:composer.json ≈ pom.xml 的 dependencies,composer.lock ≈ 锁定版本后的 effective pom,Packagist ≈ Maven Central,vendor/ ≈ 本地仓库(但直接进项目目录)。
跟着做
步骤 1:安装 Composer 并初始化项目
# Windows:官网下载 Composer-Setup.exe;macOS/Linux 用官方安装脚本或包管理器
composer --version # Composer 2.x
mkdir demo-app && cd demo-app
composer init
按提示填写包名、描述后生成 composer.json。也可以直接手写一个最小版本:
{
"name": "myteam/demo-app",
"description": "PHP 学习项目",
"type": "project",
"require": {
"php": ">=8.2",
"monolog/monolog": "^3.5"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
"php": ">=8.2" 是对运行环境的约束——相当于 Maven 里配置的 JDK 版本门槛。
步骤 2:安装依赖并体验自动加载
composer install
生成 vendor/ 目录与 composer.lock。写入口文件 index.php:
<?php
declare(strict_types=1);
require __DIR__ . '/vendor/autoload.php'; // 整个项目唯一需要的 require
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use App\Utils\Calculator; // 你自己的类,同样自动加载
$log = new Logger('app');
$log->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::INFO));
$log->info('应用启动', ['user' => 'leebf']);
echo (new Calculator())->add(2, 3), PHP_EOL; // 5
// src/Utils/Calculator.php —— 命名空间与 PSR-4 目录严格对应
namespace App\Utils;
class Calculator
{
public function add(int $a, int $b): int
{
return $a + $b;
}
}
执行 php index.php:第三方包和你自己的类共用同一套自动加载。
步骤 3:增删依赖与版本约束
composer require symfony/var-dumper # 增加依赖并写入 composer.json
composer remove monolog/monolog # 移除依赖
composer show # 查看已装包列表
版本约束语义(重点):
^3.5 ≥3.5.0 且 <4.0.0 主版本不变,兼容升级(最常用)
~3.5.0 ≥3.5.0 且 <3.6.0 锁定到次版本,只收补丁
3.5.* 只用 3.5 分支
dev-main 开发分支(生产禁用)
^ 表示「遵循语义化版本的向后兼容区间」,等价于 npm 的 caret。
步骤 4:生产部署的正确姿势
composer install --no-dev --optimize-autoloader
--no-dev 跳过 require-dev 里的开发工具(PHPUnit、PHPStan 等);--optimize-autoloader(或 -o)把 PSR-4 映射转成 classmap,减少运行时文件探测。生产环境永远用 install 不用 update:update 会按 composer.json 重新解析版本,可能在生产机器上拉到不一致的新版本。
讲清楚要点
- install 与 update 的本质区别:
install严格按composer.lock里锁定的精确版本安装(lock 不存在时才回退解析一次);update忽略旧 lock、重新求解依赖并重写 lock。日常加包用composer require 包名(内部是 update 该包 + install 其余)。 - composer.lock 必须提交版本库:它保证团队每个人、每台 CI、每台生产机装出完全相同的依赖树。不提交 lock 等于放弃可复现构建——这是 PHP 项目最常见的事故源之一。
- autoload 四种方式:
psr-4(主流,按命名空间映射目录)、classmap(扫描指定目录生成类名→文件映射,适合无命名空间的遗留代码)、files(每次请求强制加载的函数文件)、psr-0(已弃用)。改了映射规则记得composer dump-autoload。 - require-dev 与 scripts:开发工具进
require-dev(phpstan/phpstan、phpunit/phpunit);scripts可挂生命周期钩子(如post-autoload-dump),对标 Maven 的 plugin 绑定。
你可能会懵的地方
- vendor 目录要不要提交到 Git?
不提交。.gitignore 里加
vendor/,靠 composer.lock 保证可复现。vendor 是「构建产物」,提交它会让仓库膨胀且极易产生合并冲突。 - 依赖冲突(conflict)怎么解决?
Composer 求解失败会打印完整的依赖链。常见解法:升级/降级某个包、放宽版本约束(把
^3.0放宽到^2.5|^3.0前先读该包的升级指南)、或用composer why-not 包名 版本定位是谁卡住了版本。 - 国内网络慢怎么办?
配置镜像:
composer config -g repos.packagist composer https://mirrors.aliyun.com/composer/。企业内网一般用 Satis 或 Private Packagist 搭私有仓库托管内部包。 - 为什么 PHP 要把 vendor 放项目里而不是全局仓库? 项目级隔离是刻意的:不同项目可以用同一个包的不同大版本,互不干扰;部署时 vendor 随项目走。这和 Maven 共享本地仓库的策略不同,但避免了「全局升级殃及所有项目」。
- composer require 的时候发生什么? 读取 packagist 元数据 → 连同现有依赖一起做版本求解 → 选出兼容版本集 → 更新 composer.json 与 composer.lock → 下载安装到 vendor → 重建 autoload 映射。一次 require 就是小规模的 update。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
| 把 vendor/ 提交进 Git | 仓库膨胀、冲突频发 | .gitignore 掉 vendor,提交 lock |
| 生产环境跑 composer update | 版本漂移,不可复现 | 提交 lock,生产只 install |
| 手动改 vendor 里的代码修 bug | 下次 install 被覆盖 | fork 上游或写补丁包(patches 插件) |
版本约束写 * 或 dev-main | 任何版本都能装,随时炸 | 语义化约束 ^x.y,主版本升级走专项 |
忘写 "php": ">=8.2" 环境约束 | 低版本机器装出可用假象 | composer.json 声明 php/platform 依赖 |
| 修改 autoload 配置后不 dump-autoload | 新类加载不到 | 执行 composer dump-autoload |
卡点急救
Your requirements could not be resolved:依赖版本无解。用报错里的依赖链定位冲突双方,调整约束或升级兜底包;composer why-not vendor/pkg 2.0查是谁在阻止。- 新增类报
Class not found:先composer dump-autoload;仍不行则核对命名空间声明与 psr-4 映射、文件名大小写是否与类名一致。 Composer detected issues in your platform:运行环境 PHP 版本或扩展不满足 require 里的php/ext-*约束。升级 PHP 或安装对应扩展(如 ext-pdo、ext-gd)。
面试常问
- composer install 和 composer update 的区别? install 按 composer.lock 锁定的精确版本(含 dist 引用 hash)安装,lock 不存在时才解析一次并生成;update 忽略旧 lock 重新求解并重写 lock。团队协作与生产部署一律 install,update 只在有意识地升级依赖时执行。
- composer.lock 的作用?要不要提交?
记录依赖树的精确版本与安装来源,保证所有环境可复现构建。必须提交版本库;部署时配合
install --no-dev --optimize-autoloader。 - PSR-4 与 classmap 自动加载的区别与取舍?
PSR-4 按命名空间规则惰性查文件,开发期加类零成本;classmap 启动时生成完整「类名→文件」映射,无文件系统探测,性能更高。实践是开发用 PSR-4、生产加
--optimize-autoloader(PHP 7.2 后 Composer 默认级别已接近优化模式)。
自测三问
^2.8允许安装的最高版本是多少?~2.8.0呢?- 为什么生产部署命令是
install --no-dev --optimize-autoloader而不是update? - 你的类
App\Service\OrderService应该放在哪个文件?
小结一句话
Composer = 版本求解 + 依赖安装 + PSR-4 自动加载三合一,lock 文件保证可复现,它是 PHP 工程化的地基。
延伸
- Composer 官方文档
- 下一课:Web 请求处理——超全局变量、表单与会话。