2016年,Composer已经成为PHP生态中最主流的依赖管理工具,几乎所有现代PHP项目都在使用。
说起来,最早接触Composer,是在2014年左右,那时候,Composer还不是特别流行,很多PHP项目还是手动管理依赖,或者用PEAR。但是,随着PHP生态的发展,特别是Laravel、Symfony等现代PHP框架的流行,Composer逐渐成为了PHP依赖管理的标准。
刚开始用Composer的时候,觉得它很神奇,一行命令,就能把需要的依赖包下载下来,自动加载,很方便。但是,用着用着,就踩了很多坑,也积累了一些实战经验。
今天,就来聊聊Composer依赖管理的踩坑总结与实战经验。
一、Composer是什么
先简单介绍一下Composer是什么。
Composer是PHP的依赖管理工具,类似于Node.js的npm,Python的pip,Ruby的bundler。它可以帮我们管理项目的依赖包,声明项目需要哪些依赖包,然后自动下载、安装、更新这些依赖包,以及它们的依赖。
Composer的核心是两个文件:composer.json和composer.lock。
composer.json是项目的依赖声明文件,里面声明了项目需要哪些依赖包,以及版本约束,还有项目的自动加载配置、脚本、仓库地址等信息。
composer.lock是依赖锁定文件,里面记录了项目实际安装的依赖包的精确版本,以及它们的依赖关系。有了composer.lock,团队成员就能安装完全相同版本的依赖包,保证开发环境的一致性。
Composer的包仓库主要是Packagist(https://packagist.org),上面有大量的PHP开源包,我们可以从上面搜索、下载需要的包。当然,也可以配置私有仓库,或者使用本地路径、Git仓库等作为依赖来源。
二、基本使用
Composer的基本使用很简单。
1. 初始化项目
在项目根目录下,运行composer init,按照提示填写项目信息,就能生成composer.json文件。
当然,也可以手动创建composer.json文件,内容大概是这样的:
{
"name": "vendor/project-name",
"description": "项目描述",
"type": "project",
"require": {
"php": ">=7.0",
"monolog/monolog": "^1.0"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}2. 安装依赖
运行composer install,Composer会读取composer.json,解析依赖,下载安装需要的包,生成composer.lock文件,以及vendor目录(依赖包都安装在这里),还有autoload.php(自动加载文件)。
如果项目已经有composer.lock文件,那么composer install会按照composer.lock里记录的精确版本安装,保证版本一致。
3. 添加依赖
运行composer require vendor/package,就能添加一个新的依赖包,Composer会自动更新composer.json和composer.lock,下载安装这个包。
也可以指定版本,比如composer require vendor/package:^1.0。
4. 更新依赖
运行composer update,会更新所有依赖包到符合版本约束的最新版本,同时更新composer.lock。
也可以只更新某个包,比如composer update vendor/package。
注意:composer update和composer install的区别是,install是按照composer.lock安装,不会更新版本;update是更新到最新版本,会修改composer.lock。
5. 自动加载
安装完依赖后,在项目的入口文件里,引入vendor/autoload.php,就能自动加载所有依赖包的类,以及自己项目的类(按照composer.json里的autoload配置)。
require __DIR__ . '/vendor/autoload.php';这样,就可以直接use依赖包的类,或者自己项目的类,不用手动require了。
三、版本约束
Composer的版本约束是一个很重要的概念,也是容易踩坑的地方。
Composer支持多种版本约束方式:
1. 精确版本
直接写版本号,比如1.0.0,表示只安装1.0.0这个版本。
2. 范围版本
用比较运算符指定范围,比如>=1.0.0、>=1.0.0 <2.0.0、>=1.0.0 <1.1.0 || >=1.2.0。
3. 通配符版本
用作为通配符,比如1.0.,表示1.0.x的所有版本。
4. 波浪号版本(~)
~1.2等价于>=1.2.0 <2.0.0,~1.2.3等价于>=1.2.3 <1.3.0。
波浪号的意思是,允许最后一位指定的部分升级。
5. 脱字符版本(^)
^1.2.3等价于>=1.2.3 <2.0.0,^0.3.2等价于>=0.3.2 <0.4.0。
脱字符的意思是,允许不修改第一位非零部分的升级,也就是遵循语义化版本(SemVer)的兼容更新。
语义化版本的格式是主版本号.次版本号.修订号,主版本号升级表示不兼容的API修改,次版本号升级表示向后兼容的功能新增,修订号升级表示向后兼容的问题修复。
所以,^1.2.3表示可以升级到1.x.x的最新版本,因为1.x.x都是向后兼容的;^0.3.2表示可以升级到0.3.x的最新版本,因为0.x.x还在开发阶段,次版本号升级可能不兼容。
脱字符版本约束是Composer默认使用的版本约束方式,也是推荐使用的方式,因为它遵循语义化版本,既能获得兼容更新,又不会引入不兼容的版本。
四、自动加载
Composer的自动加载也是一个重要的功能,也是容易踩坑的地方。
Composer支持多种自动加载方式:
1. PSR-4
PSR-4是PHP Standards Recommendations 4,是PHP-FIG制定的自动加载规范,也是现在最主流的自动加载方式。
PSR-4的配置方式是,命名空间前缀对应目录路径,比如:
"autoload": {
"psr-4": {
"App\\": "src/",
"App\\Models\\": "src/models/"
}
}这样,App\User类就会从src/User.php加载,App\Models\User类就会从src/models/User.php加载。
PSR-4的好处是,命名空间和目录结构对应,清晰规范,而且支持多个目录映射同一个命名空间前缀,灵活方便。
2. PSR-0
PSR-0是旧的自动加载规范,现在已经被废弃,不推荐使用。PSR-0和PSR-4类似,但是命名空间的下划线会被转换为目录分隔符,而且命名空间前缀需要对应目录。
3. Classmap
Classmap是通过扫描指定目录或文件,生成类映射表,实现自动加载。比如:
"autoload": {
"classmap": ["src/", "lib/"]
}Classmap的好处是,不管类的命名空间和目录结构是否规范,都能自动加载,适合一些老项目或者不规范的项目。但是,每次新增类都需要运行composer dump-autoload重新生成映射表。
4. Files
Files是直接加载指定的文件,适合加载一些全局函数或者配置文件。比如:
"autoload": {
"files": ["src/helpers.php", "src/constants.php"]
}这些文件会在自动加载时被直接require,适合定义全局函数。
5. 开发环境自动加载
除了autoload,还有autoload-dev,用于开发环境的自动加载,比如测试类、开发工具类等,这些只在开发环境加载,生产环境不加载。
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
}运行composer install --no-dev就不会安装开发依赖,也不会加载autoload-dev。
五、常见踩坑
在使用Composer的过程中,我踩了很多坑,总结一下常见的坑。
1. 版本约束太松或太紧
版本约束太松,比如用*或者dev-master,可能会安装到不兼容的版本,导致项目出错。版本约束太紧,比如精确版本,可能会导致依赖冲突,或者无法获得安全更新。
建议使用脱字符版本约束(^),遵循语义化版本,既能获得兼容更新,又不会引入不兼容的版本。
2. 忽略composer.lock
很多人不重视composer.lock,甚至把它加入.gitignore,这是不对的。composer.lock记录了项目实际安装的依赖版本,有了它,团队成员和部署环境就能安装完全相同版本的依赖,保证环境一致。
如果没有composer.lock,每个人运行composer install安装的版本可能不同,导致"在我机器上能跑"的问题。
所以,composer.lock一定要提交到版本库,不要忽略。
3. 直接修改vendor目录
有些人会直接修改vendor目录里的依赖包代码,这是非常不好的习惯。vendor目录是Composer管理的,运行composer install或update会覆盖vendor目录,修改会丢失。
如果需要修改依赖包的代码,正确的做法是:
- 给依赖包提PR,贡献代码
- Fork依赖包,维护自己的版本,然后通过Composer的仓库配置引用自己的版本
- 使用Composer的patches功能,打补丁
4. 自动加载不生效
有时候,新增了类,但是自动加载不生效,找不到类。这通常是因为:
- 命名空间和目录不对应(PSR-4)
- 类名和文件名不对应
- 用了classmap,但是没有运行
composer dump-autoload重新生成映射表 - 没有引入vendor/autoload.php
解决方法:检查命名空间和目录是否对应,类名和文件名是否对应,如果用了classmap,运行composer dump-autoload。
5. 内存不足
运行composer install或update的时候,有时候会报内存不足的错误,特别是依赖比较多的项目。
解决方法:
- 增加PHP的内存限制,比如
php -d memory_limit=-1 composer.phar install - 使用Composer的缓存,避免重复下载
- 优化依赖,减少不必要的依赖
6. 国内访问Packagist慢
在国内,访问Packagist有时候很慢,甚至无法访问,导致composer install或update很慢,或者失败。
解决方法:
- 使用国内镜像,比如阿里云Composer镜像、腾讯云Composer镜像等
- 配置Composer的仓库地址,指向国内镜像
配置方法:
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/这样,全局配置Packagist的镜像地址,以后安装依赖就会从国内镜像下载,速度快很多。
7. 依赖冲突
有时候,添加新依赖的时候,会报依赖冲突的错误,因为新依赖需要的某个包的版本,和现有依赖需要的版本不兼容。
解决方法:
- 升级或降级现有依赖,找到兼容的版本
- 使用Composer的
composer why-not命令,查看为什么不能安装 - 如果实在无法解决,可以考虑替换依赖包
8. 生产环境安装开发依赖
部署到生产环境的时候,如果运行composer install,会安装开发依赖(require-dev里的包),比如PHPUnit、调试工具等,这些在生产环境不需要,会增加代码量,甚至可能有安全风险。
解决方法:生产环境运行composer install --no-dev --optimize-autoloader,不安装开发依赖,并且优化自动加载。
六、最佳实践
总结一些Composer的最佳实践。
1. 遵循语义化版本
自己的项目和包,遵循语义化版本(SemVer),版本号格式为主版本号.次版本号.修订号,主版本号升级表示不兼容的API修改,次版本号升级表示向后兼容的功能新增,修订号升级表示向后兼容的问题修复。
这样,别人依赖你的包的时候,就能用脱字符版本约束,安全地获得兼容更新。
2. 使用脱字符版本约束
依赖包的版本约束,推荐使用脱字符(^),遵循语义化版本,既能获得兼容更新,又不会引入不兼容的版本。
避免使用*、dev-master等太松的版本约束,也避免使用精确版本等太紧的版本约束。
3. 提交composer.lock
composer.lock一定要提交到版本库,不要忽略,保证团队成员和部署环境安装相同版本的依赖。
4. 不要修改vendor目录
不要直接修改vendor目录里的依赖包代码,如果需要修改,用正确的方式(提PR、Fork、打补丁)。
5. 使用国内镜像
在国内,配置Composer使用国内镜像,提高安装速度。
6. 生产环境优化
生产环境运行composer install --no-dev --optimize-autoloader,不安装开发依赖,优化自动加载,提高性能。
7. 定期更新依赖
定期运行composer update,更新依赖到最新兼容版本,获得安全更新和bug修复。但是,更新后要充分测试,确保没有兼容性问题。
8. 使用composer scripts
Composer支持scripts配置,可以定义一些脚本命令,比如测试、构建、部署等,方便统一管理项目的常用命令。
"scripts": {
"test": "phpunit",
"build": "php build.php",
"deploy": "php deploy.php"
}然后运行composer test就能执行测试,composer build就能执行构建,很方便。
七、高级技巧
再分享一些Composer的高级技巧。
1. 私有仓库
如果公司有私有包,可以配置私有仓库,比如使用Satis、Toran Proxy、Private Packagist等,搭建自己的Composer私有仓库。
配置方法:
"repositories": [
{
"type": "composer",
"url": "https://packagist.example.com"
}
]2. 路径依赖
开发的时候,可以使用本地路径作为依赖,方便调试。
"repositories": [
{
"type": "path",
"url": "../my-package"
}
]这样,Composer会从本地路径安装依赖,修改本地路径的代码,项目里就能立即生效,方便开发调试。
3. Git依赖
也可以直接使用Git仓库作为依赖,指定分支或标签。
"repositories": [
{
"type": "git",
"url": "https://github.com/vendor/package.git"
}
],
"require": {
"vendor/package": "dev-master"
}4. 替换依赖
如果某个依赖包有问题,或者想用自己的版本替换,可以使用replace配置。
"replace": {
"vendor/package": "self.version"
}这样,Composer会认为你的项目提供了这个包,不会再安装它。
5. 提供依赖
如果你的项目提供了某个虚拟包(比如接口、规范),可以使用provide配置。
"provide": {
"psr/log-implementation": "1.0.0"
}6. 建议依赖
如果某个依赖是建议安装的,不是必须的,可以使用suggest配置。
"suggest": {
"ext-memcached": "需要使用Memcached缓存时安装",
"ext-redis": "需要使用Redis缓存时安装"
}安装的时候,Composer会提示这些建议的依赖。
7. 平台配置
可以配置平台版本,模拟特定的PHP版本或扩展版本,用于兼容性检查。
"config": {
"platform": {
"php": "7.0.0",
"ext-mbstring": "1.0.0"
}
}这样,Composer会按照配置的平台版本解析依赖,即使实际环境版本不同。
八、写在最后
Composer依赖管理:踩坑总结与实战经验。
Composer是PHP生态中非常重要的工具,它彻底改变了PHP项目的依赖管理方式,让PHP项目的依赖管理变得简单、规范、高效。
但是,Composer虽然好用,也有很多需要注意的地方,只有理解了它的原理和机制,才能用好它,避免踩坑,提高开发效率。
希望我的这些踩坑总结和实战经验,能对大家有所帮助,让大家在使用Composer的时候,少踩坑,更高效。
最后,用一句话结尾:
"工具是手段,不是目的。理解工具的原理,才能真正用好工具。"
愿大家都能用好Composer,写出更优雅、更高效的PHP代码。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录