composer是现代PHP的基石
现代高级编程语言,依赖管理工具是必不可少的。Java有Maven,Python有pip,Nodejs有npm, 而在composer出现之前,PHP只有被广为诟病的Pear, 由于Pear实在太难用,很少PHP开发者用到这个工具。以致于PHP的开发生态很糟糕。
连一个像样的依赖管理工具都没有,让PHP这门占据了web网站开发�主流市场的语言很尴尬。开发过程中,要用到第三方的类库,需要去下载zip包,然后解压,放到相应的目录,处理好命名空间,自动加载的问题,如果这个第三方包还有其他依赖项,还要再次重复这个流程,看着隔壁家python和node.js一个命令行就搞定,显得php开发人员的操作既原始又滑稽
http://packagist.p2hp.com/
安装流程
安装的流程很简单,归结为以下几步:
php -r “copy(‘https://install.phpcomposer.com/installer’, ‘composer-setup.php’);” # 下载安装脚本 - composer-setup.php - 到当前目录
php composer-setup.php # 执行安装过程
php -r “unlink(‘composer-setup.php’);” # 删除安装脚本
sudo mv composer.phar /usr/local/bin/composer # 全局安装
composer config -g repo.packagist composer https://packagist.phpcomposer.com # 更换国内镜像源
第一次使用
接下来,我们用composer来安装第一个包
以monolog包为例,这个包可以让开发者很方便地将日记写入到文件、数据库或其他储存介质中。
在项目根目录新建composer.json文件,写入以下内容
{
“require”: {
“monolog/monolog”: “1.2.*”
}
}
执行composer install指令安装包依赖
composer install
使用包进行开发
composer包管理规范
什么是包?只要存在composer.json文件的代码都可以称之为一个包。
包名称
包名称由作者+项目名称组成。有些包作者名与项目名是相同的,如mustache/mustache
包名称一定要加上作者,避免冲突。
那么,我们怎么根据一个包的项目名去获取包的信息呢?以mustache包为例:
在packagist查找
安装包
除了在composer.json中写包的安装信息,还可以通过composer require mustache/mustache这种方式直接安装
用composer search指令查找
包版本
在composer.json中声明安装包时,需要指定包的版本,�版本号的指定有多种格式:
确定的版本号
格式:1.0.2
最简单的指定方式,无歧义
在一定范围的版本号
可以定义多个范围,用逗号隔开,这将被视为一个逻辑AND处理。一个管道符号|将作为逻辑OR处理。 AND 的优先级高于 OR
=1.0: 大于或等于1.0版本
=1.0,<2.0: 大于或等于1.0,且小于2.0
=1.0,<1.1
>=1.2: 大于或等于1.0且等于1.1,或者大于等于1.2
通配符
1.0.*: 只要满足以1.0开头的版本号均可
~下一个重要版本
~1.2 相当于 >=1.2,<2.0
~1.2.3 相当于 >=1.2.3,<1.3
^大于指定的版本
以下用实例演示版本号的区别:
清空根目录,composer.json内容为:
{
“require”: {
“mustache/mustache”: “2.6.0”
}
}
执行composer install
指定版本
接下来,删除vendor目录,将版本号改为~2.6.0, 执行composer install
此时,会发现版本号并没有变化
composer.lock锁文件
composer.json在指定版本时,不一定是精确指定,很多时候是使用范围指定,只有当我们安装了包,才知道最终安装了哪个版本。那么问题来了,如果我们半年后再根据composer.json来安装包,可能有些包的版本已经升级了,且向下不兼容,这就有可能导致程序报错。为避免这种问题,在执行composer install之后,composer会生成composer.lock锁文件。
在锁文件中可以看到完整的包信息,包括包的版本号。
composer install命令会先检查锁文件是否存在,如果存在,它将下载composer.lock指定的版本(忽略 composer.json 文件中的定义)
如果在composer.json中修改了版本号,必须执行composer update命令,这个命令会根据composer.json的定义安装包,并更新composer.lock文件
锁文件非常重要!必须将composer.lock文件上传到代码仓库,这样才能保证团队各成员所安装的包版本是一致的。
创建项目
composer通过create-project 可以直接创建一个完整的项目,将包所在的代码仓库clone下来
以创建laravel项目为例:
learnComposer composer create-project laravel/laravel Laravel –prefer-dist “5.5.*”
开发与生产环境分开
有些包我们仅需要在本地安装,生产环境并不需要,可以在composer.json中通过require-dev进行声明,如:
composer install –no-dev 会忽略require-dev所声明的包
composer install会将require-dev声明的包一并获取
自定义脚本
在Laravel的composer.json文件中,有这么一段声明:
“scripts”: {
“post-root-package-install”: [
“@php -r "file_exists(‘.env’) || copy(‘.env.example’, ‘.env’);"”
],
“post-create-project-cmd”: [
“@php artisan key:generate”
],
“post-autoload-dump”: [
“Illuminate\Foundation\ComposerScripts::postAutoloadDump”,
“@php artisan package:discover”
]
},
表示在执行composer install时的相应阶段,会自动触发运行脚本
发布自己的包
github是新建一个仓库
创建composer.json文件
在项目根目录执行composer init,生成composer.json配置文件
在packagist提交github仓库地址
在packagist注册账号,然后登录。
提交github仓库地址
设置自动同步
如果要实现当github仓库代码更新就触发包的自动更新,需要进行以下设置
在github中添加packagist服务
composer自动加载一个文件后必须执行命令composer dump-autoload
生成composer之后会用如下目录结构
2.在项目的入口文件加载composer
<?php
require DIR . ‘/../vendor/autoload.php’;
3.composer的加载流程
<?php
// autoload.php @generated by Composer
require_once DIR . ‘/composer/autoload_real.php’;
return ComposerAutoloaderInit3af6929e8dfa5c827897257e4f6dbd1d::getLoader();
getLoader():
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
//注册loadClassLoader对象(这个是composer的核心类)
spl_autoload_register(array('ComposerAutoloaderInit3af6929e8dfa5c827897257e4f6dbd1d', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit3af6929e8dfa5c827897257e4f6dbd1d', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
//初始化autoload_static中的生成的变量(不同的生成条件,里面的内容也不相同)
call_user_func(\Composer\Autoload\ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
////注册loadClass
$loader->register(true);
return $loader;
} autoload_static: 这个文件不同的composer dump-auto 条件生成的内容是不一样的。
composer dump-auto –no-dev -o
这个命令生成的文件内容
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d
{
public static $prefixLengthsPsr4 = array (
‘A’ =>
array (
‘App\’ => 4,
),
);
public static $prefixDirsPsr4 = array (
'App\\' =>
array (
0 => __DIR__ . '/../..' . '/apps',
),
);
public static $classMap = array (
'App\\Http\\Controllers\\UserController' => __DIR__ . '/../..' . '/apps/Http/Controllers/UserController.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::$classMap;
}, null, ClassLoader::class);
} }
注意:我们可以看到有一个classMap可以减少后面在注册加载的时候is_file的IO操作,减少一个请求的时间。但是要注意,如果有新的文件,一定要重新用命令生成该文件,不然会出现类无法加载的错误。
对比其他命令生成结果
composer dump-auto
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d
{
public static $prefixLengthsPsr4 = array (
‘P’ =>
array (
‘Predis\’ => 7,
),
‘A’ =>
array (
‘App\’ => 4,
),
);
public static $prefixDirsPsr4 = array (
'Predis\\' =>
array (
0 => __DIR__ . '/..' . '/predis/predis/src',
),
'App\\' =>
array (
0 => __DIR__ . '/../..' . '/apps',
),
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d::$prefixDirsPsr4;
}, null, ClassLoader::class);
} } 这个命令不会生成$classMap
composer dump-auto -o
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit3af6929e8dfa5c827897257e4f6dbd1d
{
public static $prefixLengthsPsr4 = array (
‘P’ =>
array (
‘Predis\’ => 7,
),
‘A’ =>
array (
‘App\’ => 4,
),
);
public static $prefixDirsPsr4 = array (
'Predis\\' =>
array (
0 => __DIR__ . '/..' . '/predis/predis/src',
),
'App\\' =>
array (
0 => __DIR__ . '/../..' . '/apps',
),
);
public static $classMap = array (
'App\\Http\\Controllers\\UserController' => __DIR__ . '/../..' . '/apps/Http/Controllers/UserController.php',
'Predis\\Autoloader' => __DIR__ . '/..' . '/predis/predis/src/Autoloader.php',
'Predis\\Client' => __DIR__ . '/..' . '/predis/predis/src/Client.php',
这个命令会把require-dev中的也生成到$classMap中
生成环境建议使用composer dump-auto –no-dev -o
init(初始化)
该命令用于创建 composer.json 文件,并进行基础信息配置:
$ composer init
可以配置Package name、Description、Author、Minimum、Package Type、License、dependencies 及 dev dependencies 信息。
完成后配置文件内容如下:
{
“name”: “test/test”,
“description”: “test init”,
“type”: “library”,
“license”: “License Description”,
“authors”: [
{
“name”: “mayanlong”,
“email”: “json_vip@163.com”
}
],
“require”: {}
}
search(搜索)
根据名称搜索相关的包,成功后会列出符合的相关包的信息,本处以搜索 monolog 为例:
$ composer search monolog
monolog/monolog Sends your logs to files, sockets, inboxes, databases and various web services
kdyby/monolog Integration of Monolog into Nette Framework
show(详情)
根据包的名称,列出包的相关信息,本处以查看 monolog/monolog 为例:
$ composer show -all monolog/monolog
name : monolog/monolog
descrip. : Sends your logs to files, sockets, inboxes, databases and various web services
keywords : log, logging, psr-3
versions : dev-master, 2.0.x-dev, 1.x-dev, 1.21.0, 1.20.0, 1.19.0, 1.18.2, 1.18.1, 1.18.0, 1.17.2, 1.17.1, 1.17.0, 1.16.0, 1.15.0, 1.14.0, 1.13.1, 1.13.0, 1.12.0, 1.11.0, 1.10.0, 1.9.1, 1.9.0, 1.8.0, 1.7.0, 1.6.0, 1.5.0, 1.4.1, 1.4.0, 1.3.1, 1.3.0, 1.2.1, 1.2.0, 1.1.0, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
想查看更多信息,就亲自将该命令复制到命令行执行吧。
install (安装)
我们先在 composer.json 配置中添加一个 monolog/monolog 依赖库,如下:
{
“name”: “test/test”,
“description”: “test init”,
“type”: “library”,
“license”: “License Description”,
“authors”: [
{
“name”: “mayanlong”,
“email”: “json_vip@163.com”
}
],
“require”: {
“monolog/monolog”: “1.21.*”,
}
}
然后通过如下命令进行安装依赖
$ composer install
update (更新)
如果我们新增或者删除了某个依赖,可以通过如下命令进行更新
$ composer update
require (申明依赖)
我们也可以用命令直接添加依赖,执行该命令后将自动下载,命令如下:
$ composer require symfony/http-foundation
composer的install和require和update指令 到底什么时候用什么指令
在同一个项目中,假如使用3个类库,想一次一次安装。那么composer.json中最开始只有一个键值对类库。第一次使用composer install指令安装。添加第2个类库键值对开始后的每一次都是使用composer update指令安装
你只要执行require那行后,你vendor中假如100个类库,这100个类库就全部自动加载了,直接使用即可(不需要一个一个引入)
首先,我们先看看Composer的源码从哪里看起。当然,请您先准备好源码。
composer init或者直接install之后,自动生成了一个vendor目录,这时您需要在文件中手动的require这个vendor目录下的autoload.php文件,其实这个文件又载入了vendor/composer/autoload_real.php。
在autoload_real.php中,我们发现了熟悉的spl_autoload_register函数。但这个文件最大的作用是去加载ClassLoader.php这个文件和一些目录文件,也在同级目录下。这个文件就值得大家好好研究下了,不过核心也无外乎前面三篇文章中的内容。但是在autoload_real.php中,大家可以发现在调用ClassLoader的register()函数前,还加载了几个目录相关的文件:
autoload_static.php,静态加载方式,顶级类加载命名空间
autoload_psr4.php,遵守PSR4规范的包目录映射数组文件
autoload_namespaces.php,命名空间映射,PSR0规范
autoload_classmap.php,类图映射,命名空间直接映射路径
好深奥的感觉,不过PSR4您一定已经很了解了。其他的其实就是对应的没有遵守PSR4规范的一些类库。而在ClassLoader中的register()函数就是加载的这些文件中对应的路径文件。在这里,最好的方式是您可以多下载一些包,然后看看这些文件发生了什么改变。比如我安装了一个monolog后,autoload_psr4.php的内容变成了这样:
composer require endroid/qr-code
指定版本
composer require endroid/qr-code 1.9.3
./composer.json has been updated
接下来,composer这个命令干了什么您应该也就了解了。当您进行composer require时,首先修改了composer.json文件,然后下载包,完成后根据包里的composer.json文件中所对应的规范来修改对应的autoload_xxx.php文件。完成了文件命名空间相关内容的映射。当register()进行加载的时候,自然就得心应手了。
ClassLoader源码中重点阅读的一些函数内容包括:
findFile()
findFileWithExtension()
addPsr4()
add()
Tip #1: 阅读文档
我是真心这样认为的. 文档 是一个非常有用的东西并且在长远看来几个小时的阅读将会为您节省更多的时间. 你会惊讶原来 Composer 可以做这么多事情.
Tip #2: 注意 项目 和 库 之间的区别
无论你是创建 项目 还是 库, 了解这一点非常重要, 它们每一个都需要单独的一套做法。
一个库是一个可重用的包,你可以添加一个依赖
比如: symfony / symfony,doctrine / orm 或者 elasticsearch/elasticsearch.
一个项目通常是一个应用程序,依赖于几个库. 它通常是不可重用的(没有其它项目会要求它作为依赖. 典型的例子是电子商务网站,客户支持系统等)。
我将在下面的提示中区分库和项目。
Tip #3: 使用特定的依赖关系#关于应用程序的版本
如果你正在创建一个应用程序,您应该使用最具体的版本来定义依赖项,假如你需要解析YAML文件,你应该指定这样的依赖版本“symfony / YAML”:“4.0.2”。
即使你遵循了库的依赖版本控制,在次要版本和补丁版本中也有可能中断向后兼容性。例如,如果你使用的是“symfony / Symfony”:“^ 3.1”,会有3.2的版本有可能会破坏你的应用程序测试用列。或者可能新版本有bug没有修正,那么php_codesniffer检测你的代码格式的时候会导致新问题,这在很大程度上可能会破坏一个应用的构建。
依赖关系的更新应该是深思熟虑的,而不是偶然的。其中的一个技巧更详细地讨论了它。
这听起来像一个多余的,但它可以防止你的同事不小心在项目中添加一个新的库文件或更新所有依赖的时候出错,(不然的话,你们有可能会导致浪费大量的时间在审核代码上)。
Tip #4: 对库依赖项使用版本范围
创建库时,应尽可能定义最大的可用版本范围。比如创建了一个库,要使用 symfony/yaml 库进行 YAML 解析,就应这样写:
“symfony/yaml”: “^3.0 | ^4.0” |
这表示该库能从 Symfony 3.x 或 4.x 中任意版本中使用 symfony/yaml 。这相当重要,因为这个版本约束会传递给使用该库的应用程序。
万一有两个库的请求存在冲突,比如一个要 ~3.1.0 ,另一个需要 ~3.2.0 ,则安装会失败。
Tip #5: 开发应用程序要提交 composer.lock 文件到 git 版本库中
创建了 一个项目,一定要把 composer.lock 文件提交到 git 中。 这会确保每一个人——你、你的合作伙伴、你的 CI 服务器以及你的产品服务器——所运行的应用程序拥有相同依赖的版本。
乍一看有些画蛇添足,在 Tip #3 中已经提过要使用明确的版本号的约束了啊。这并不多余,要知道你使用的依赖项的依赖项并不受这些约束绑定(如 symfony/console 还依赖 symfony/polyfill-mbstring)。如果不提交 composer.lock 文件,就不会获取到相同版本的依赖集合。
Tip #6: 开发库要把 composer.lock 文件添加到 .gitignore 文件中
创建 一个库 (比如说叫 acme/my-library), 这就不应该把 composer.lock 文件提交到 git 库中了。该文件对使用该库的项目 It 不会有任何影响 。
假设 acme/my-library 使用 monolog/monolog 作依赖项。你已经在版本库中提交了 composer.lock,开发 acme/my-library 的每个人都可能在使用 Monolog 的老旧版本。该库开发完成后,在实际项目中使用该库,就可能存在安装的 Monolog 是一个新版本 , 而此时就会和该库存在不兼容。可是你在之前根本就不会注意到兼容问题就因为这个 composer.lock!
因此,最佳处理方式就是把 composer.lock 添加到 .gitignore 文件中,这样就避免了不小心提交它到版本库中引发的问题。
如果还想确保该库与它的依赖项的不同版本保持兼容性,那继续阅读下一个 Tip !
Tip #7: Travis CI 构建依赖项的不同版本
当前 Tip 仅适合库(对于应用程序要指明具体的版本号)。
如果你在构建开源的库,很有可能你会使用 Travis CI 来跑构建过程。
默认情况下,在 composer.json 文件约束允许的条件下,composer 安装会安装依赖的最新可能版本。这就意味着对于 ^3.0 | ^4.0 这样的依赖约束,构建安装总是使用最新的 v4 版本发行包。 而 3.0 版本根本不会测试,所构建的库就可能与该版本不兼容,你的用户要哭了。 |
幸好,composer 为安装低版本依赖项提供了一个开关 –prefer-lowest (应使用 –prefer-stable ,可阻止不稳定版本的安装)。
已上传的 .travis.yml 配置类似下面的格式:
language: php
php:
env:
matrix:
- PREFER_LOWEST=”–prefer-lowest –prefer-stable”
- PREFER_LOWEST=””
before_script:
script:
代码详见 my mhujer/fio-api-php library 及 the build matrix on Travis CI
虽然这解决了多数的不兼容问题,不过仍然要记得,依赖项的最低和最高版本间有太多的组合。他们仍旧可能存在不兼容的情况。
Tip #8: 按名称对 require 和 require-dev 中的包排序
按名称对 require 及 require-dev 中的包排序是非常好的实践。这在衍合一个分支时可以避免不必要的合并冲突。假如你把一个包添加到两个分支文件中的列表末尾,那每次合并都可能遇到冲突。
手动进行包排序的话会很乏味,所以最好办法就是在 composer.json 中 配置一下 即可:
{
…
“config”: {
“sort-packages”: true
},
…
}
以后再要 require 一个新的包,它会自动添加到一个正确位置(不会跑到尾部)。
Tip #9: 进行版本衍合或合并时不要合并 composer.lock
如果你在 composer.json (和 composer.lock)中添加了一个新依赖项,并且在该分支被合并前主分支中添加另一个依赖项,此时就需要对你的分支进行衍合处理。那么 composer.lock 文件就会得到一个合并冲突。
千万别试图手动解决冲突,这是因为 composer.lock 文件包含了定义 composer.json 中依赖项的哈希值。所以即使你解决了冲突,这个最终合并结果的lock文件仍是错误的。
最佳方案应该这样做,用下面一行代码在项目根目录创建一个 .gitattributes 文件,它会告诉 git 不要试图对 composer.lock 文件进行合并操作:
/composer.lock -merge
推荐 Trunk Based Development 方式(常用佳品,不会有错),使用临时的特性分支纠正这种问题。当你有个临时分支需要即时合并时,因此导致的 composer.lock 文件合并冲突的风险极小。你甚至可以仅仅为添加一个依赖项而创建分支,然后马上进行合并。
假如在衍合过程中 composer.lock 遇到合并冲突又当如何呢? 使用主分支版本解决,这样仅仅修改 composer.json 文件即可(新增一个包)。然后运行 composer update –lock ,就会把 composer.json 文件的修改更新到 composer.lock 文件中。现在把已经更新的 composer.lock 文件提交到版本暂存区,然后继续衍合操作。
Tip #10:了解 require 和 require-dev之间的区别
能够意识到require 和require-dev模块之间的区别是非常重要的。
需要运行在应用中或者库中的包都应该被定义在 require (例如: Symfony, Doctrine, Twig, Guzzle, …)中。如果你正在创建一个库, 注意将什么内容定义为 require。因为这个部分的 每个依赖项同时也是使用了该库的应用的依赖。
开发应用程序(或库)所需的包应该定义在require-dev (例如:PHPUnit, PHP_CodeSniffer, PHPStan)中。
Tip #11: 安全地升级依赖项
我想大家对如下事实存有共识:应该定期对依赖项升级。 此处我想讨论的是依赖项的升级应该放在明处且慎之又慎,而不能是因其他活计的需要才顺手为之。如果在重构应用的同时又升级了库,那么就很难区分应用崩溃的原因是重构还是升级带来的。
可用 composer outdated 命令查看哪些依赖项需要升级。追加一个 –direct (或 -D)参数开关是个聪明之举,这只会查看 composer.json 指定的依赖项。还有一个 -m 参数开关,只查看次版本号的升级列表。
对每一个老版本的依赖项进行升级都要尊循如下步骤:
创建新分支
在 composer.json 文件中更新该依赖项版本到最新版本号
运行 composer update phpunit/phpunit –with-dependencies (使用升级过的库替换 phpunit/phpunit)
检查 Github 上库的版本库中 CHANGELOG 文件,检查是否存在重大变化。 如果存在就升级应用程序
本地测试应用程序(使用 Symfony 的话还能在调试栏看到弃用警告)
提交修改(包括 composer.json 、 composer.lock 及其他新版本正常运行所做的必要修改)
等 CI 构建结束
合并然后部署
有时需要一次升级多个依赖项,比如升级 Doctrine 或 Symfony。这种情况下,就要在升级命令中把他们全部罗列出来:
composer update symfony/symfony symfony/monolog-bundle –with-dependencies
或者使用通配符升级所有指定命名空间的依赖:
composer update symfony/* –with-dependencies
这全都是很乏味的工作,但相对于不小心升级依赖项而言,这提供了额外保障。
一个可接受的简捷方式就是一次升级所有 require-dev 中的依赖项(如果程序代码没有修改的话,否则还是建议创建独立分支以便代码审查)。
Tip #12: 在 composer.json 中定义其他类型的依赖
除了定义库作为依赖项外,也以在这儿定义其他东西。
可以定义应用程序和库所支持的 PHP 版本:
“require”: {
“php”: “7.1.* || 7.2.*”,
},
也能定义应用程序和库所需要的扩展。在尝试 docker 化自己的应用时,或是你的同伴头一次设置应用环境时,这招超级实用。
“require”: {
“ext-mbstring”: “”,
“ext-pdo_mysql”: “”,
},
(当 扩展版本不一致 时,版本号要用 * )。
Tip #13: 在CI构建期间验证 composer.json
composer.json 和 composer.lock 应当一直保持同步. 因此, 一直为他们保持自动核对是一个好主意. 将此添加成为你的构建脚本的一部分将会确保 composer.lock 与 composer.json 保持同步:
composer validate –no-check-all –strict
Tip #14: 在 PHPStorm 中使用 Composer 插件
这里有一个 composer.json plugin for PHPStorm. 当手动修改 composer.json 时,插件会自动完成及执行一些验证.
如果你在使用其他 IDE (或者只是一个编辑器), 你可以使用 its JSON schema 设置验证.
Tip #15: 在 composer.json 中指明生产环境的PHP版本号
如果你和我一样,有时还 在本地环境跑PHP最新预释版本, 那么就会处于升级依赖项的版本不能运行于生产环境的风险。现在我就在使用 PHP 7.2.0 ,也就意味着我安装的库可能在 7.1 版本中运行不了。如果生产环境跑的是 7.1 版本,安装就会失败。
不过不用担心,有个非常简单的解决办法,在 composer.json 文件的config 部分指明生产环境的 PHP 版本号即可:
“config”: {
“platform”: {
“php”: “7.1”
}
}
别把它和 require 部分的设置搞混了,它的作用不同。你的应用就可以运行 7.1 或 7.2 版本下,而且同时指定了平台版本为 7.1 (这意味着依赖项的升级版本要和 平台版本 7.1 保持兼容):
“require”: {
“php”: “7.1.* || 7.2.*”
},
“config”: {
“platform”: {
“php”: “7.1”
}
},
Tip #16: 使用自有托管 Gitlab 上的私有包
推荐使用 vcs 作为版本库类型,并且 Composer 决定获取包的合适的方法。比如,从Github上添加一个 fork,使用它的 API 下载整个版本库的 .zip 文件,而不用克隆。
不过对一个私有的 Gitlab 安装来讲会更复杂。如果用 vcs 作版本库类型,Composer 会检测到它是个 Gitlab 类型的安装,会尝试使用 API 下载包(这要求有 API key。我不想设置,所以我只用 SSH 克隆安装了) :
首先指明版本库类型是 git:
“repositories”: [
{
“type”: “git”,
“url”: “git@gitlab.mycompany.cz:package-namespace/package-name.git”
}
]
然后指明常用的包:
“require”: {
“package-namespace/package-name”: “1.0.0”
}
Tip #17: 临时使用 fork 下 bug 修复分支的方法
如果在某个公共的库中找到一个 bug,并且在Github上自己的 fork 中修复了它, 这就需要从自己的版本库里安装这个库,而不是官方版本库(要到修复合并且修复的版本释出才行)。
使用 内嵌别名 可轻松搞定:
{
“repositories”: [
{
“type”: “vcs”,
“url”: “https://github.com/you/monolog”
}
],
“require”: {
“symfony/monolog-bundle”: “2.0”,
“monolog/monolog”: “dev-bugfix as 1.0.x-dev”
}
}
可以通过 设置 path 作为版本库类型 在本地测试这次修复,然后再 push 更新版本库。
更新于 2018-01-08:
文章发布后,我收到了一些建议,提供了更多的使用技巧。它们分别是:
Tip #18:使用 prestissimo 加速你的包安装
Composer 有个 hirak/prestissimo 插件,通过该插件能够以并行的方式进行下载,从而提高依赖包的安装速度。
那么,这么好的东西,你现在该如何做?你仅仅需要马上全局安装这个插件,然后就可以自动地在所有项目中使用。
composer global require hirak/prestissimo
Tip #19: 当你不确定时,测试你的版本约束
即使在阅读 the documentation 之后,书写正确的版本约束在一些时候也是很棘手的.
幸运的是, 这里有 Packagist Semver Checker 可以用来检查哪个本部匹配特定的约束. 他不是仅仅的分析版本约束, 他从 Packagist 下载数据以来展示实际的发布版本.
查看 the result for symfony/symfony:^3.1.
Tip #20: 在生产环境中使用使用权威类映射文件
应该在生产环境中 生成权威类映射文件 。这会让类映射文件中包含的所有类快速加载,而不必到磁盘文件系统进行任何检查。
可以在生产环境构建时运行以下命令:
composer dump-autoload –classmap-authoritative
Tip #21: 为测试配置 autoload-dev
你也不想在生产环境中加载测试文件(考虑到测试文件的大小和内存使用)。这可以通过配置 autoload-dev 解决(与 autoload 相似):
“autoload”: {
“psr-4”: {
“Acme\”: “src/”
}
},
“autoload-dev”: {
“psr-4”: {
“Acme\”: “tests/”
}
},
Tip #22: 尝试 Composer 脚本
Composer 脚本是一个创建构建脚本的轻量级工具
require-dev和require、autoload-dev和autoload使用方式一致,用途不同
require列出的软件包列表必须安装,都为正式部署所需要;
require-dev列出的软件包一般用于开发或测试,是额外列出的依赖;
执行composer install/update命令时用--no-dev参数跳过require-dev列出的软件包;
autoload自动加载映射,正式部署使用;
autoload-dev自动加载映射,一般多用于测试和开发;
执行composer dump-autoload命令式可通过--no-dev参数来忽略autoload-dev指定的命名空间;
"require-dev": {
"phpunit/phpunit": "~4.0",
"phpspec/phpspec": "~2.1",
"barryvdh/laravel-ide-helper": "~2.0"
}
https://blog.martinhujer.cz/have-you-tried-composer-scripts/