Composer install 和 update 命令的使用和区别

最近面试被问到,为什么给 Designup 选择的框架是 Laravel,当时总结的几个原因,一个是开发效率,真的高,另外一个,就是这个我接触到的第一个使用 Composer 进行包管理的框架,非常的方便。紧接着就被问了一些关于Composer原理的问题,没答上来,想来也是,用了一年多的这么方便的工具,竟然都没有仔细探究过它这么方便的原因和原理,罪过罪过。。。。

官方定义:

Composer 是 PHP 的一个依赖管理工具。它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们。

传送门:Composer 中文文档 。 Composer 的全局安装和配置等文档中讲解详细,本文不再赘述。

Composer 不是一个包管理器。是的,它涉及 “packages” 和 “libraries”,但它在每个项目的基础上进行管理,在你项目的某个目录中(例如 vendor)进行安装。默认情况下它不会在全局安装任何东西。因此,这仅仅是一个依赖管理。

Composer 将这样为你解决问题:

  • 你有一个项目依赖于若干个库。
  • 其中一些库依赖于其他库。
  • 你声明你所依赖的东西。
  • Composer 会找出哪个版本的包需要安装,并安装它们(将它们下载到你的项目中)。

注: Composer 需要在 PHP 5.3.2 + 以上的版本运行。另外它从包的来源直接安装,而不是简单的下载 zip 文件,故而需要 git/svn/hg 等,这取决于载入的包所使用的版本管理系统。

一、声明依赖关系

首先在项目根目录下创建一个 composer.json 文件,用于描述项目的依赖关系,格式如下:

1
2
3
4
5
{
"require": {
"monolog/monolog": "1.2.*"
}
}

则表明,需要依赖项目 monolog/monolog 的包,版本为 1.2.0(含) ~ 1.3.0(不含) 之间的任何版本。
其中,monolog/monolog 为包名称,由供应商名称和其项目名称构成。通常容易产生相同的项目名称,而供应商名称的存在则很好的解决了命名冲突的问题。
1.2.* 为版本号,包的版本号描述规则如下

类型 实例 描述
确切的版本号`1.0.2`你可以指定包的确切版本。
范围`>=1.0` `>=1.0,<2.0` `>=1.0,<1.1|>=1.2`通过使用比较操作符可以指定有效的版本范围。
有效的运算符:`>`、`>=`、`<`、`<=`、`!=`。
你可以定义多个范围,用逗号隔开,这将被视为一个 **逻辑AND** 处理。一个管道符号 `|` 将作为 **逻辑OR** 处理。
AND 的优先级高于 OR。
通配符`1.0.*`你可以使用通配符 `*` 来指定一种模式。`1.0.*` 与 `>=1.0,<1.1` 是等效的。
赋值运算符`~1.2`这对于遵循语义化版本号的项目非常有用。 `~1.2` 相当于 `>=1.2,<2.0`

注意: 虽然 2.0-beta.1 严格地说是早于 2.0,但是,根据版本约束条件, 例如 ~1.2 却不会安装这个版本。就像前面所讲的 ~1.2 只意味着 .2 部分可以改变,但是 1. 部分是固定的。
默认情况下只有稳定的发行版才会被考虑在内。如果你也想获得 RC、beta、alpha 或 dev 版本,你可以使用 稳定标志。你可以对所有的包做 最小稳定性 设置,而不是每个依赖逐一设置。

稳定标识实例:

1
2
3
4
5
6
{
"require": {
"monolog/monolog": "1.0.*@beta",
"acme/foo": "@dev"
}
}

指定开发版本号:

1
2
3
4
5
6
{
"require": {
"monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7",
"acme/foo": "1.0.x-dev#abc123"
}
}

可用的稳定性标识(按字母排序):devalphabetaRCstable。 默认 stable

二、安装依赖包

在创建好 composer.json 文件后,只需执行如下命令,即可安装所需的依赖到本地项目中:

1
$ php composer.phar install

然后 composer 会将 composer.json 中配置的依赖包全都按照其版本设置中的最新版本下载到 vendor 目录中。比如前文中的 monolog/monolog 就会创建 vendor/monolog/monolog 目录。

小技巧: 如果你正在使用 Git 来管理你的项目, 你可能要添加 vendor 到你的 .gitignore 文件中。 你不会希望将所有的代码都添加到你的版本库中。

同时,composer install 会创建一个 composer.lock 文件到你的根目录中。

三、Composer.lock 锁文件

在安装依赖后,Composer 将把安装时确切的版本号列表写入 composer.lock 文件。这将锁定改项目的特定版本。

请提交你应用程序的 composer.lock (包括 composer.json)到你的版本库中

这是非常重要的,因为 install 命令将会检查锁文件是否存在,如果存在,它将下载指定的版本(忽略 composer.json 文件中的定义)。

这意味着,任何人建立项目都将下载与指定版本完全相同的依赖。你的持续集成服务器、生产环境、你团队中的其他开发人员、每件事、每个人都使用相同的依赖,从而减轻潜在的错误对部署的影响。即使你独自开发项目,在六个月内重新安装项目时,你也可以放心的继续工作,即使从那时起你的依赖已经发布了许多新的版本。

如果不存在 composer.lock 文件,Composer 将读取 composer.json 并创建锁文件。

这意味着如果你的依赖更新了新的版本,你将不会获得任何更新。此时要更新你的依赖版本请使用 update 命令。这将获取最新匹配的版本(根据你的 composer.json 文件)并将新版本更新进锁文件。

如果只想安装或更新一个依赖,你可以白名单它们:

1
$ php composer.phar update monolog/monolog [...]

换句话说,关于 composer.jsoncomposer.lock 文件的区别如下:

(1)composer.json 中,依赖包的版本配置的是规则/范围,而 composer.lock 中配置的是具体的版本号。
(2)执行 composer install 命令,会判断 composer.lock 文件是否存在并首先依据 composer.lock 文件来安装指定版本的依赖包,若 composer.lock 文件不存在,则会根据 composer.json 文件中的版本限制,安装符合要求的最新版本依赖包,并以此创建 composer.lock 文件。
(3)执行 composer update 命令时,会忽略 composer.lock 文件,直接依据 composer.json 文件中的配置,更新所有依赖或者指定依赖到符合要求的最新版本,然后更新 composer.lock 文件

四、自动加载

对于库的自动加载信息,Composer 生成了一个 vendor/autoload.php 文件。你可以简单的引入这个文件,你会得到一个免费的自动加载支持。

1
require 'vendor/autoload.php';

这使得你可以很容易的使用第三方代码。例如:如果你的项目依赖 monolog,你就可以像这样开始使用这个类库,并且他们将被自动加载。

1
2
3
$log = new Monolog\Logger('name');
$log->pushHandler(new Monolog\Handler\StreamHandler('app.log', Monolog\Logger::WARNING));
$log->addWarning('Foo');

你可以在 composer.jsonautoload 字段中增加自己的 autoloader
除了 PSR-4 自动加载,classmap 也是支持的。这允许类被自动加载,即使不符合 PSR-0 规范。

1
2
3
4
5
6
7
8
9
10
11
# Laravel 中 的 autoloader
{
"autoload": {
"classmap": [
"database"
],
"psr-4": {
"App\\": "app/" // Composr 将注册一个 autoloader 到 App 命名空间
}
},
}

五、Composer.json 中的其他信息

Composer.json 中可以配置很多信息,在这里进介绍几个相对常见的【Laravel框架中看到了的】,更多更详细的请直接前往 Composer 中文文档 - Composer.json架构

1. 仅在开发环境中生效的配置 : *-dev
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Laravel 中的单元测试
{
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1"
},
"autoload-dev": {
"classmap": [
"tests/TestCase.php"
]
}
}
2. 安装时需要执行的脚本 : scripts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Laravel Demo
{
"scripts": {
"post-root-package-install": [
"php -r \"copy('.env.example', '.env');\""
],
"post-create-project-cmd": [
"php artisan key:generate"
],
"post-install-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postInstall",
"php artisan optimize"
],
"post-update-cmd": [
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
"php artisan optimize"
]
}
}

这些脚本的执行时间如下:

事件名称详细说明
**pre-install-cmd** 在 `install` 命令执行前触发。
**post-install-cmd** 在 `install` 命令执行后触发。
**pre-update-cmd** 在 `update` 命令执行前触发。
**post-update-cmd** 在 `update` 命令执行后触发。
**pre-status-cmd** 在 `status` 命令执行前触发。
**post-status-cmd** 在 `status` 命令执行后触发。
**pre-package-install** 在资源包安装前触发。
**post-package-install** 在资源包安装后触发。
**pre-package-update** 在资源包更新前触发。
**post-package-update** 在资源包更新后触发。
**pre-package-uninstall** 在资源包被卸载前触发。
**post-package-uninstall** 在资源包被卸载后触发。
**pre-autoload-dump** 在自动加载器被转储前触发,无论是 `install/update` 还是 `dump-autoload` 命令都会触发。
**post-autoload-dump** 在自动加载器被转储后触发,无论是 `install/update` 还是 `dump-autoload` 命令都会触发。
**post-root-package-install** 在 `create-project` 命令期间,根包安装完成后触发。
**post-create-project-cmd** 在 `create-project` 命令执行后触发。

注意: Composer 不会去执行任何依赖包中定义的 install 或 update 相关脚本。因此你不应该在依赖包中申明 pre-update-cmd 或 pre-install-cmd。如果你需要在执行 install 或 update 命令前使用脚本,请确保它们已被定义在根包中。

3. 关于本项目的一些基本信息
1
2
3
4
5
6
7
8
# Laravel Demo
{
"name": "laravel/laravel", // 机构名称/项目名称
"description": "The Laravel Framework.", // 项目描述
"keywords": ["framework", "laravel"], // 关键词
"license": "MIT", // 开源证书
"type": "project", // 安装类型,默认library
}

其中, Type 为包的安装类型,默认为 library

包的安装类型,用来定义安装逻辑。如果你有一个包需要一个特殊的逻辑,你可以设定一个自定义的类型。这可以是一个 symfony-bundle,一个 wordpress-plugin 或者一个 typo3-module。这些类型都将是具体到某一个项目,而对应的项目将要提供一种能够安装该类型包的安装程序。

composer 原生支持以下4种类型:

  • library: 这是默认类型,它会简单的将文件复制到 vendor 目录。
  • project: 这表示当前包是一个项目,而不是一个库。例:框架应用程序 Symfony standard edition,内容管理系统 SilverStripe installer 或者完全成熟的分布式应用程序。使用 IDE 创建一个新的工作区时,这可以为其提供项目列表的初始化。
  • metapackage: 当一个空的包,包含依赖并且需要触发依赖的安装,这将不会对系统写入额外的文件。因此这种安装类型并不需要一个 distsource
  • composer-plugin: 一个安装类型为 composer-plugin 的包,它有一个自定义安装类型,可以为其它包提供一个 installler

仅在你需要一个自定义的安装逻辑时才使用它。建议忽略这个属性,采用默认的 library