热点





composer.json 应该怎么写?PHP 项目的依赖和版本管理基础

发表时间: 2026-09-24 18:30:01   人气:85 次   

对于 PHP 项目来说,composer.json 并不是一个简单的“安装软件包列表”,而是整个项目依赖关系的声明文件。项目需要什么 PHP 版本,需要哪些第三方库,生产环境和开发环境分别依赖什么,以及项目应该如何自动加载自己的代码,都可以在这个文件中定义。

理解 composer.json,实际上是在理解现代 PHP 项目是怎样管理依赖的。

一个最简单的 PHP 项目,可以从这样的 composer.json 开始:

{
"name": "example/my-project",
"description": "A PHP application",
"type": "project",
"require": {
"php": "^8.2",
"monolog/monolog": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}

这里最重要的是 require 和 require-dev。

require 表示项目运行所需要的依赖。例如一个网站运行时需要日志库、数据库组件或者 HTTP 客户端,这些应该放在 require 中。

require-dev 则用于开发和测试。例如 PHPUnit、PHPStan、PHP_CodeSniffer 等工具通常只在开发环境使用。生产服务器执行 composer install --no-dev 时,可以不安装这些开发依赖。

这两个字段的区别并不是“重要依赖”和“不重要依赖”,而是“生产运行时需要”与“开发阶段需要”。

Composer 的核心机制是依赖解析。

例如项目声明:

{
"require": {
"monolog/monolog": "^3.0"
}
}

这并不是告诉 Composer “永远安装 Monolog 3.0.0”,而是在告诉 Composer,项目接受符合这个版本约束的 Monolog 版本。

Composer 随后会分析这个包自己的依赖,以及其他直接依赖和间接依赖之间的关系,寻找一个能够同时满足所有版本约束的依赖集合。

这里也需要纠正一个常见误解。

如果项目 A 需要某个库的 ^1.0,项目中的另一个依赖需要同一个库的 ^2.0,Composer 并不会简单地“优先选择最新版本”然后强行安装。

如果两个约束无法同时满足,Composer 通常会报告依赖冲突。解决方案是升级或降级其中一个依赖、调整版本约束,或者寻找兼容的版本组合。

这也是 Composer 和简单复制 PHP 类库最大的区别。

版本号写法尤其重要。

1.2.3 表示一个非常具体的版本约束,而 ^1.2.3 和 ~1.2.3 则允许一定范围内的版本。

对于 Composer 常见的语义版本规则,可以简单理解为:

^1.2.3
允许 1.x 的兼容更新,但不进入 2.0.0

~1.2.3
允许 1.2.x 的更新,但通常不会进入 1.3.0

1.2.3
只允许这个版本

不过,实际版本约束还受到 Composer 对不同主版本以及零版本号规则的处理影响,所以不能把 ^ 永远理解成“只要小于下一个整数版本”。对于正常遵循语义化版本的库,^1.2.3 最常见的含义是允许 >=1.2.3 <2.0.0。

很多项目最初会把版本写得非常死,例如:

"monolog/monolog": "3.0.0"

这样做确实可以限制版本变化,但对于普通应用项目,通常没有必要把所有依赖都固定到一个具体版本。Composer 本身就通过 composer.lock 来记录经过解析后的具体版本。

这两个文件的职责不同。

composer.json 描述的是“项目允许使用什么依赖”。

composer.lock 描述的是“这一次解析之后,实际使用了哪些具体版本”。

因此,对于应用程序项目,通常应该把 composer.lock 一起提交到 Git。

例如开发人员运行:

composer update

Composer 会重新解析依赖,并按照 composer.json 中的约束寻找新的可用版本,然后更新 composer.lock。

而在服务器部署时,通常使用:

composer install --no-dev

如果存在 composer.lock,Composer 会根据锁定文件安装已经确定的版本,而不是每次部署都重新选择一套依赖。

这也是为什么不要把“composer.json 改了但 composer.lock 没改”当成正常的版本管理方式。如果修改了依赖声明,通常应该让 Composer 重新解析相关依赖,并把更新后的 composer.lock 一起提交。

如果只想更新某一个依赖,也可以针对具体包执行更新,而不是每次让整个依赖树全部升级。

Composer 的另一个重要功能是自动加载。

现代 PHP 项目通常不需要手动写几十个 require 或 require_once。例如:

"autoload": {
"psr-4": {
"App\\": "src/"
}
}

表示 App 命名空间下的类可以按照 PSR-4 规则从 src/ 目录自动加载。

项目入口文件通常只需要:

require __DIR__ . '/vendor/autoload.php';

然后就可以使用 Composer 管理的类。

如果修改了 autoload 配置,需要重新生成自动加载文件:

composer dump-autoload

生产环境还可以使用:

composer dump-autoload -o

优化自动加载。

这里也需要纠正原文中的一个概念:optimize-autoloader 并不是“加快依赖解析速度”。它主要影响的是 Composer 生成和运行时的自动加载机制,而不是 Composer 如何解决版本冲突。

如果希望把自动加载优化作为项目配置的一部分,可以使用:

{
"config": {
"optimize-autoloader": true
}
}

大型生产项目通常也会在部署时使用优化自动加载。

PHP 版本约束同样应该写进 composer.json。

例如:

"require": {
"php": "^8.2"
}

这意味着项目的运行环境必须满足相应的 PHP 版本要求。

如果开发机器运行 PHP 8.3,而生产服务器只有 PHP 8.1,Composer 就应该尽早暴露这个问题,而不是等程序上线以后才发现语法或者依赖无法运行。

还有一个容易混淆的配置是 config.platform。

正确的结构应该是:

{
"config": {
"platform": {
"php": "8.2.0"
}
}
}

而不是把 platform 直接放在 composer.json 顶层。

config.platform 的作用,是告诉 Composer 在依赖解析时按照指定的平台版本进行判断。例如开发人员电脑安装的是 PHP 8.3,但生产环境实际上是 PHP 8.2,那么可以通过 platform 设置让 Composer按照 PHP 8.2 的兼容条件解析依赖。

不过它并不会把电脑上的 PHP 8.3 “变成” PHP 8.2。

这一点非常重要。

如果服务器实际上只有 PHP 8.1,那么 composer.json 写:

"platform": {
"php": "8.2.0"
}

并不会 magically 把 PHP 8.1 升级成 PHP 8.2。它只是改变 Composer 的依赖解析视角。因此部署前仍然必须检查服务器实际 PHP 版本。

Composer 还会把 PHP 扩展当作平台依赖。

例如:

"require": {
"php": "^8.2",
"ext-curl": "*",
"ext-mbstring": "*",
"ext-gd": "*"
}

这里的 ext-curl、ext-mbstring 和 ext-gd 不是普通 Composer 软件包,而是 PHP 环境提供的扩展。

Composer 可以检查它们是否存在,但 Composer 并不会像安装普通 PHP 包一样替你安装这些扩展。

遇到“为什么 Composer 安装失败”的问题时,可以使用:

composer show --platform

查看当前 PHP 环境和已经提供的平台依赖。

处理复杂依赖冲突时,composer why 和 composer why-not 非常有用。

例如:

composer why monolog/monolog

可以帮助查看为什么项目需要这个包。

而:

composer why-not monolog/monolog 3.0.0

可以用于分析为什么某个指定版本无法安装。

这比遇到冲突以后直接删除 composer.lock 或者随便更换一个依赖包更加可靠。

对于私有依赖,也可以在 composer.json 中声明自定义仓库。例如公司内部维护的 Git 仓库:

{
"repositories": [
{
"type": "vcs",
"url": "https://gitlab.example.com/team/private-package.git"
}
],
"require": {
"company/private-package": "^1.0"
}
}

不过 repositories 的作用主要是告诉 Composer 去哪里寻找包,并不等于把 GitLab、GitHub 等服务的账号密码直接写进 composer.json。

认证信息应该通过 Composer 的认证机制、环境变量、CI/CD 的安全凭据等方式处理。不要把访问令牌、密码或者私钥直接提交到 Git 仓库。

Composer 还支持 scripts,可以把项目中的常用操作统一起来。

例如:

{
"scripts": {
"test": "phpunit",
"analyse": "phpstan analyse",
"cs": "phpcs"
}
}

之后可以直接运行:

composer test
composer analyse
composer cs

这样团队成员不需要记住一大串命令。

不过,自动执行脚本也需要谨慎设计。尤其是 post-install-cmd、post-update-cmd 这类生命周期脚本,如果里面放置大量修改文件或者执行外部程序的操作,部署环境应该提前审查,避免第三方依赖安装时产生不必要的副作用。

安全方面,现在 Composer 项目应该把依赖审计作为正常开发流程的一部分。

可以运行:

composer audit

检查当前依赖是否存在已知安全漏洞。

如果发现漏洞,不能简单理解成“Composer 有问题”。Composer 只是把依赖关系和已知安全公告呈现出来,真正需要处理的是存在问题的第三方包,以及是否存在可以升级到的安全版本。

一个实际项目的 composer.json 通常不需要写得非常复杂。

例如一个普通 PHP 网站可能已经足够:

{
"name": "mnewstv/website",
"description": "MNewsTV website",
"type": "project",
"require": {
"php": "^8.2",
"ext-curl": "*",
"ext-mbstring": "*"
},
"require-dev": {
"phpunit/phpunit": "^11.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"config": {
"optimize-autoloader": true,
"sort-packages": true
}
}

然后执行:

composer install

Composer 会建立 vendor/ 目录,并生成相应的自动加载文件。

通常不需要把 vendor/ 目录提交到 Git,因为它可以根据 composer.json 和 composer.lock 在部署时重新安装。

一个比较规范的 PHP 项目流程通常是这样的:

开发阶段修改 composer.json,然后执行 Composer 命令解析依赖;Composer 更新 composer.lock;开发人员测试程序;composer.json 和 composer.lock 一起提交到版本控制系统;生产环境执行 composer install --no-dev;最后根据部署环境生成优化后的 autoload。

这样,项目依赖就从“某个人电脑上安装了哪些 PHP 包”,变成了可以被代码库明确描述、重复安装和部署的工程配置。

理解 composer.json 最重要的不是记住几十个字段,而是理解几个文件之间的关系:composer.json 负责声明项目需要什么,composer.lock 负责锁定这次解析得到的具体版本,vendor 负责保存实际安装的依赖,vendor/autoload.php 负责把这些类加载进 PHP 程序。

当这几个概念分清楚以后,Composer 就不再只是一个“下载 PHP 插件的工具”,而成为 PHP 项目控制依赖、版本、自动加载、开发工具和部署环境的重要基础设施。对于一个需要长期维护的 PHP 项目来说,真正麻烦的从来不是安装第一个包,而是几年之后依然能够知道这个项目为什么依赖它、依赖哪个版本,以及升级以后会发生什么。

分享 Facebook | X | Whatsapp | Linkedin

捐助(Paypal): https://www.paypal.me/observeccp
订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP

责任编辑:雨轩  来源:中国观察  转载请注明作者、出处並保持完整。



今日热点:
更多新闻...
近期焦点:
最新视频

推荐阅读:


更多推荐文章:


更多新闻


推荐文章

金正恩死亡传闻引爆网络 朝鲜断网与金与正接班说法缺乏证据

近日,社交平台流传一则有关朝鲜最高领导人金正恩死亡的紧急消息,声称金与正已经接掌政权、在全国实施紧急戒严,并命令切断互联网,以阻止金正恩死讯传播。相关帖子还...

2026年10月23日   129

印度首都为何突然封城式维稳 选民名册争议正在考验莫迪政府的民主底线

新德里街头,警察和准军事部队提前封锁了集会地点,地铁站关闭,部分商业区的移动互联网服务中断,抗议者还没有走到指定广场,拘留行动就已经开始。2026年10...

2026年10月11日   154635

阿尔伯塔公投改变省政坛走向 史密斯开始整顿党内独立派

发表:2026-10-22 17:17:42 | 更新:2026-10-22 17:17:42  人气: 12940

阿尔伯塔公投改变省政坛走向 史密斯开始整顿党内独立派

本文由中国观察編輯部原创。为了促进信息自由传播,本平台原创内容全面开放转载,无须申请授权,只需注明来源、保留原文链接,即可全文转载、引用、翻译。我们不追究版权,只在乎公正的传播。

捐助(Paypal): https://www.paypal.me/observeccp
订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP

最近文章:
更多文章...


近期要闻:
更多新闻...

编辑推荐文章:

最新发表   ⏬
更多文章...
疫情