在Composer出现之前,PHP项目管理依赖是件痛苦的事情。需要用什么库,去官网下载zip包,解压到项目目录,手动require。如果这个库还依赖其他库,就得一个个去下载,版本不兼容更是家常便饭。升级的时候更麻烦,得手动替换文件,还容易出错。

Composer的出现彻底改变了这一切。它是PHP的依赖管理工具,类似于Node.js的npm、Ruby的gem、Python的pip。只需要在composer.json里声明依赖了哪些库、什么版本,Composer会自动帮你下载、安装、管理,还会处理依赖之间的版本关系。

如今Composer已经成为PHP生态的标准,主流框架(Laravel、Symfony、Yii、CodeIgniter)都用它来管理依赖。做PHP开发,不会Composer都不好意思说自己是PHPer。今天就来分享一些Composer的最佳实践。

自动加载的原理

Composer不只是下载依赖,它还提供了强大的自动加载功能。安装完依赖后,vendor目录下会生成一个autoload.php文件,在项目入口require这个文件,就能自动加载所有依赖的类,不需要手动require。

Composer支持四种自动加载方式:PSR-0、PSR-4、classmap、files。其中PSR-4是目前推荐的方式,它把命名空间映射到目录,类名和文件名一一对应。比如配置:

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

这样App\User类就对应src/User.php文件,Lib\Db类对应lib/Db.php文件。新增类文件不需要任何额外操作,Composer会自动加载。

有个注意点:修改了autoload配置后,需要运行composer dump-autoload重新生成自动加载文件,否则新配置不会生效。

版本约束怎么写

composer.json里声明依赖时,版本号的写法很有讲究。常见的写法有:

  • 精确版本:1.2.3,只安装这个版本
  • 范围版本:>=1.0 <2.0,安装1.0到2.0之间的版本
  • 通配符:1.0.*,安装1.0开头的最新版本
  • 波浪号:~1.2,相当于>=1.2 <2.0
  • 脱字符:^1.2,相当于>=1.2 <2.0(遵循语义化版本)

我推荐用脱字符^,比如"monolog/monolog": "^1.12"。它会安装1.12以上、2.0以下的最新版本,既能获得bug修复和新功能,又不会升级到有破坏性变更的大版本。

但有个前提:依赖库必须遵循语义化版本(SemVer)——主版本号.次版本号.修订号,主版本号升级意味着有不兼容的API变更。大部分知名库都遵循这个规范,但也有例外,使用前最好看一下库的更新日志。

composer.lock的作用

运行composer install后,会生成一个composer.lock文件,记录了当前安装的所有依赖的精确版本号。这个文件非常重要,必须提交到版本控制系统。

为什么?因为composer.json里的版本约束是一个范围,不同时间运行composer install可能安装不同的版本。比如你声明了"^1.0",今天安装的是1.2.3,三个月后可能安装的是1.5.0。如果没有composer.lock,团队成员和生产环境安装的依赖版本可能不一致,导致"在我机器上能跑"的问题。

有了composer.lock,运行composer install会按照lock文件里的精确版本安装,保证所有环境的依赖版本一致。只有当你想升级依赖时,才运行composer update,它会更新依赖并重新生成lock文件。

加速Composer

Composer默认从Packagist下载包,服务器在国外,国内访问速度很慢,经常超时。解决方法是使用国内镜像。

我用的是中国全量镜像,在项目根目录运行:

composer config -g repo.packagist composer https://packagist.phpcomposer.com

这样全局配置了国内镜像,以后所有项目都会从国内镜像下载,速度快很多。

还有一个优化:安装时加--prefer-dist参数,优先下载zip包而不是克隆git仓库,速度更快:

composer install --prefer-dist

总结

Composer是PHP开发者必备的工具,掌握它的最佳实践能让你的项目更规范、更稳定、更高效。核心要点:用PSR-4自动加载、用脱字符约束版本、提交composer.lock、使用国内镜像加速。如果还没用上Composer,赶紧在你的下一个项目里试试吧。