使用 Composer 管理项目依赖

我个人入行以来,最早接触软件包的概念,是从 Java 开始的,Java 所有的代码,都必须属于一个软件包。如果要调用某个现成的类库,就要 import 一个软件包,就可以使用那个软件包里的所有代码了。

然后,又在操作系统层面接触了包管理器,那是最初使用 Linux 的时候,我接触的第一个发行版是 RedHat,非付费版本的 RedHat 没有包管理器,只能自己解决类库依赖,简直就是地狱。直到遇到了 Ubuntu 的 apt-get,又瞬间来到了天堂。

后来,我成为了一名程序员,PHP 作为工作的开发语言,但是,这个语言里没有包管理的概念的,PHP 代码,都是一个项目一个项目的,能用的类库是比较少的,最多有些网上能找到的东一榔头、西一棒槌的代码片段。

所幸,那个时代,也没有接触到所谓的大规模软件开发。即使进入到大公司,发现大家也都在刀耕火种。所谓的代码复用,就是把一些代码组织到一个文件夹里,然后单独设立一个 SVN 库,最好的复用,就是做到了用 SVN 的 external 特性来引入一个外部库代码。

介绍

但是,随着 GitHub 这样的网站火爆发展,开源代码前所未有地爆发。大量的软件包涌现出来,而整个软件研发世界,也更倾向于使用更规范和自动化的形式去复用代码,node 因为旗下的 npm 而大行其道,简直就是杀手级的应用,所以,PHP 从 Symfony 社区开始,出现了 Composer 这个杀手级的包依赖管理应用。

而 PHP 语言,也整体都升到了 5.x 以上,namespace 特性已经全面普及,而 PHP-FIG 民间标准化组织的成立和 PSR 标准的普及,也已经到了成熟的阶段。我想,现在是时候全面使用 Composer 这类的工具来管理项目依赖和依托于这种工具来完成代码的复用了。

这是一种全新的思维模式。通过依赖配置文件,使用自动化工具来下载代码,使用自动加载来使用代码。不再是老的,在代码目录里放个取名为 lib 的文件夹,把代码放进去,用子文件夹管理。

使用单独的 Git 版本库来管理被复用的 lib 代码,设立单独项目库,这样的成本是极低的,通过规范的项目式管理,来管理每一个内聚的软件包,每个 release 都带有各自的版本号。

每一个项目单独配置依赖文件,对版本的依赖的是逐个项目指定的,而不是放在系统级的 include 目录。避免因为一个 lib 的版本升级,搞挂整个服务器上部署的所有项目。

所有,这些,程序员需要学习的,就是 Composer 的正确使用方法和语义化版本的原理和实施方案。我感觉,这是时代的潮流,虽然现在来说这个话,显得很滑稽,因为 Composer 早已流行了数年。但是,我仍然庆幸自己已经认识到了这一点。即便是 PHP 软件的研发,也已经全面进入了一个工业化时代,整个行业都变得更加的自动化和规范化,现在,一个 PHP 程序员,可能只需要写很少很少的代码,就可以实现一个功能丰富得匪夷所思的软件。而能够实现这一切,Composer 居功至伟。

纵使 Composer 仍然有很多的缺点,我实际使用中确实发现,其缺点相当多,比如,软件包的质量良莠不齐,比如,依赖配置文件,一般对软件的版本依赖都是活动的,就是依赖的实际是一个软件的版本范围,当执行升级指令的时候,可能因为依赖的变动而导致整个应用的崩溃。

而 Composer 这样的组件,明显的一种设计思想,就是打破比较保守的软件开发思路,可能将软件开发者推上一个版本更新的极致道路,只有一款软件有人维护,那么不单软件代码,甚至其依赖的所有类库代码都会不断被更新到最新的版本,这在过去保守的软件开发观念看来,简直不可思议。而时代要求我们现在这么做。

此外,比较困扰中国用户的就是,网络的封闭性问题,比如 GitHub 和 packigist 等网站的联通性和流量带宽都十份让人忧愁。所以,我想,肯定会出现更好的技术解决方案,来代替这个东西,但是这种思想应该是潮流和趋势无误了,可以全面推进去部署和执行了。

基本用法

composer.json

配置文件,定义了本项目的依赖情况,大体上分为“直接依赖”和“开发依赖”两个部分,需要程序员指定项目直接在运行时依赖的软件包,以及在开发阶段依赖的软件包。

除了指定包名外,还需要指定版本号和版本约束。

版本约束符合

这可能是我使用 composer 觉得最难的东西,因为我从来没记住过,那些奇怪的符合是什么意思。

精确版本约束

直接不带任何符合,写一个完整的版本号,例如:1.0.2。表示,只能使用这个版本。

版本范围

可以使用大于,小于,等于号,指定一个范围,这也比较好理解:

1
2
3
4
>=1.0
>=1.0 <2.0
>=1.0 <1.1 || >=1.2
1.0 - 2.0

通配符范围

这个也很好理解,例如:1.0.*

下一个显著发布约束

这可能是最难的,比如 ~ 这个符号,这个符号内涵很深,非常难理解。它的含义是变动的,要结合语义版本号来理解。它表示的最多能容忍的变更范围。如果你写 ~1.2 它的含义是 >=1.2 <2.0.0 可以看到,只要第一位版本号没变,就都可以。

但是如果你写 ~1.2.3,那么意思变成了 >=1.2.3 <1.3.0 因为多了一位版本号,能容忍的范围反倒下降了。

综合起来,你可以理解成,允许最后一位版本号增加。所以,你指定版本号的位数,会影响这个符号约束的范围。

另一个很难理解的符合是 ^ ,这个符号英文叫 Caret,它表达的意思和 ~ 很像,一般情况下,用了这个符号的话,接受的版本变化范围要比上面那个波浪号要广,代表只要大版本没变即可,即接纳所有向下兼容的变更。

但是,例外是当大版本是数字 0 的时候,却又不一样了。例如:^0.3 表示的是 >=0.3.0 < 0.4.0^0.0.3 则表示 >=0.0.3 <0.0.4,就和首位不是 0 的时候不一样。可能这个语义版本号体系里,0 这个数字作为主版本号,非常特殊吧。

总体来说,是不允许破坏性变更。

install

安装依赖文件,如果你的项目已经存在了一个 compsoer.lock 文件,composer 会按照文件中记载的包的版本信息下载并安装这些依赖的软件包。

update

composer 默认不会自动帮你把所有的软件包升级到最新,这样可以最大程度保持你代码库的稳定。它只会按照 lock 文件记载的版本号进行安装。

但是如果你想升级的时候,你可以主动使用 update 命令来完成。这个命令也可以升级一个软件包。非常灵活。

常见任务

list:列出所有命令

使用 composer list 列出所有可以执行的命令。

show:列出所有已经安装的包

使用 composer show -i 列出所有已经安装的包。

global: 将所有命令变成全局

例如 composer global show -i 在全局环境下安装了哪些包?

问题

curl error 60

这个问题报错是:

1
curl error 60 while downloading https://packagist.phpcomposer.com/packages.json: SSL certificate problem: certificate has expired

这个原因可能是 curl 的 openssl 引用的跟证书文件过期了。可以通过 PHP 指令来查看证书的存放位置:

1
php -r "print_r(openssl_get_cert_locations());"

来查看 openssl 引用的证书的位置,去 curl 官网下载最新版本的证书替换上去。我在自己电脑上试了一下,没有成功,不过我却有点相信这可能会成为一个问题。

另一种方法,就是更换一个 composer 的 repository。

在 composer.json 配置文件中,更换为更快更新的 repository,可能会解决这个问题:

1
2
3
https://mirrors.aliyun.com/composer/
https://packagist.laravel-china.org
https://packagist.phpcomposer.com

第一个是阿里云镜像,第二个是 Laravel 中国镜像,第三个是中国全量镜像。我发现我换了第一个,我的问题解决了。

我不明白,为什么 ssl 错误,换一个源,竟然就解决了问题,那么不是证书过期么?那为什么错误信息又要这么写呢?