名人之声
就算它工作不正常也别担心。如果一切正常,你早该失业了
——Mosher的软件工程定律
来源/https://www.startutorial.com/articles/view/modern-php-developer-composer
翻译/Lemon黄
现代化的PHP开发,一定要知道包管理,即Composer。
1 包管理
通常来说,一组代码块组成一个方法,一组方法组成一个类,一组类形成一个包(package)。
可重用的包可以放入任何一个项目中,并且无需再添加任何功能即可使用。
一个包能为客户端提供API来实现单一的目标。
包能够帮助我们的项目实现“DRY(Don't Repeat Yourself--不要重复)”,软件开发的一个原则,就是减少各种信息(代码)的重复。
在大多数情况下,包是有依赖关系的。例,当“包A”需要 “包B”才能运行时,可以说“包A”依赖于“包B”。一个包有一系列的依赖关系是很常见的(例,A依赖于B,B依赖于C)。
假设没有包管理器,我们需要做什么使得依赖于B包的A包能工作起来?当我们下载A包的源代码时,发现A依赖于B包,以致于我们又要去下载B包的源代码。找到B包的源代码后,可能A还是无法工作,因为我们还要确保下载了B包的正确版本。这种依赖的关系的故事还可以继续下去。我们只讨论一个依赖关系,如果包A有多个依赖关系或者有一系列依赖关系,真很快就会变成一个噩梦。
所以,我们需要一个包管理器,一个可以解决所有依赖关系的管理器。
2 Composer vs. PEAR
PEAR:
在Composer之前,有一个叫做 PEAR 的东西。如果你很早就开始接触PHP,那你可能知道PEAR,因为它自1999年就已经存在。
PEAR的产生也是为了能重复使用包,这和Composer是类似的。但由于以下几个原因,它并不被开发者们推崇:
- 与Composer不同,PEAR是一个系统范畴的包管理器。当有很多个项目 ,它们共享相同的依赖项,但每一个依赖项都有不同的版本时,PEAR这种方法会造成很多混乱和挫折。
- 为了能让你的代码被PEAR的存储库所接受,需要一定数量的UP投票。这种方式抑制减缓了PEAR存储卡的增长。归根结底,开发者是为了编写代码,而不是为了提升代码而关注UP投票。
Composer:
Composer是PHP中应用程序级别的包管理器。它的灵感来自Node.js中的Npm和Ruby中的Bundler,是目前社区公认的包管理器。
Composer的生态系统由两部分组成:composer(用于安装包的命令行程序) 和 packagist(默认包的存储库)。
应用程序级别的包管理器意味着它以项目为基础来管理依赖项。这就很容易来管理很多个的项目,并能保持计算机的干净,因为它只将包下载到对应的项目目录中。
与PEAR不同的是,不需要获得任何的UP投票。所以,每个人都喜欢提交他们的代码包到Packagist存储库中。只要有人喜欢你的Packagist中提交的包,你就可以开始了(创作,开发package)。
Packagist:
如上所述,Packagist(packagist.org)是composer的默认包存储库。截止到2015年9月,Packagist已经提供了69568个包。下一次,我们需要一个PHP包,我们很可能能在Packagist上找到我们要的包,而不用重头开始构建一个。作为开发人员,建议你用包的力量,因为它将节省你无数的时间和精力。截止到2015年9月,包装商提供了69568个包裹。
3 安装Composer
以下安装,基于MAC用户。
安装Composer的方式有两种作用域:本地作用域(也叫本地安装)和全局作用域(也叫全局安装)。根据专业的经验,我们建议在您的系统上(也就是全局安装)安装composer。毕竟,我们的系统上会有很多个PHP项目,我们很可能会使用composer来管理每个PHP项目的依赖关系。全局安装为我们节省了很多麻烦。
全局安装:
在终端(Terminal )中运行以下命令来全局安装composer:
代码语言:javascript复制curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
如果遇到与权限相关的任何错误,请在sudo模式下运行上面的命令(将sudo附加到每个命令)
本地安装:
在项目的根目录打开终端(Terminal )运行以下命令来本地安装composer:
代码语言:javascript复制curl -sS https://getcomposer.org/installer | php -- --filename=composer
有关composer的更详细的安装指南,请访问:
https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx
验证是否安装成功:
要验证是否正确安装了composer,请从安装了composer的目录运行下面的命令(如果composer是全局安装的,则在任何位置运行)。
代码语言:javascript复制composer about
如果看到类似于下面的输出,则说明安装成功。
代码语言:javascript复制Composer - Package Management for PHP
See https://getcomposer.org/ for more information.
4 使用Composer
Composer现在可以使用了,我们通过一个简单的例子来演示它的用法:
想象我们已经完成了一个完美的项目,我们希望生成模拟数据,例如,显示我们的客户的姓名和地址。如果数据是随机的,而且有意义的话,这会很酷,所以演示会看起来很真实。一种解决方案是键入一些假名称和地址,将它们存储在一个数组中,然后使用array_rand从数组中随机选择条目。正如你可能已经意识到的,这个解决方案听起来很乏味,不切实际。如果我们需要数百个用户的数据,会发生什么?我们需要一个救世主。
在Packagist上刚好有我们想要的包,这个很棒的包叫做 Faker。
接下来,我们就可以使用composer来安装 Faker。
在项目的根目录中,运行以下命令:
代码语言:javascript复制composer require fzaninotto/faker
composer需要几秒钟(毕竟是国外人开发的,在国内通常需要好几分钟,这个可以通过更改composer镜像来完成,大家百度一下)才能下载所需的文件。在composer下载引擎的作用下,composer从github下载faker的zip文件。除了下载所需的包,composer还将创建一些内部文件,我们稍后将对其进行研究。
现在我们去看看我们的项目目录,我们应该能够发现一些新创建的文件夹和文件,如下所示:
- composer.json
- composer.lock
- vender
composer.json:
这个文件用来描述项目的依赖项。这是一个简单的json文件,向我们展示项目中安装了哪些包。
无论何时在命令行中运行composer require 命令,composer.json和composer.lock文件都将自动更新以反映项目中包的更改。
相反,如果将包添加到composer.json文件,则要运行 composer install 命令来下载新的包。如果要将所有包的版本更新为其版本约束指定的最新版本,可以运行composer update。如果要将所有包的版本更新为其版本约束指定的最新版本,可以运行composer update。
这就是composer的三个基本命令:
composer require:
这个命令用于将单个包添加到项目依赖项中。只要我们需要一个新的包,我们就可以运行它。这个真的很方便,因为我们根本不需要接触 composer.json文件。
此命令的另外一个用法是更新现有包的版本。例如,我们使用 composer require fzaninotto/faker 就已经安装了Faker的最新版本,如果我们不指定它的版本约束,则下载的是包的最新版本。但是我们的应用与Faker 1.4.0的版本无法兼容,我们需要Faker 1.2.0的版本,这时候我们就可以使用命令:composer require fzaninotto/faker:1.2 0,来安装。它将会下载我们指定的版本并相应更新项目中composer的相关文件。
composer install:
这个命令运行,首先会查找项目中是否有composer.lock文件,如果文件存在,则安装按文件中定义的包的确切版本,然后忽略composer.json文件。如果不存在,该命令将检查composer.json文件中定义的包,并下载与提供的版本约束匹配的包的最新版本。你能看出区别吗?使用composer.lock时,会下载准确的版本,而使用composer.json时,composer将始终尝试检索与提供的版本约束匹配的包的最新版本。当版本约束被定义为一个确切的数字时,两个动作都有相同的结果。然而,这种情况很少发生。
当我们的一个新的项目中已经定义了依赖项列表,当我们在这个项目中运行这个命令,这个命令回去安装所有列出的依赖项的包。或者我们从github上去下载别人的项目,在项目中运行此命令,也会自动下载项目中所列的依赖项的包。
在某些部署策略中,我们在生产环境中运行此命令,以便在从存储库中提取应用程序的源代码后来安装该应用程序。
composer update:
这个命令与composer install 不同,此命令此读取composer.json文件。它将现有的包更新到与composer.json文件中定义的提供的版本约束相匹配的最新版本。
我们可以使用这个命令来更新现有包的版本,类似于composer require。不同的是composer require不需要我们手动触发composer.json文件,它感觉更直观。
这个命令只从composer.json读取的事实带来了一个常见的陷阱,尤其是在生产环境中使用这个命令。我们在生产环境中不应该使用这个命令,以下是为什么的原因:
如果您的应用程序在本地开发环境中与Faker 1.2.0配合得很好,则可以将代码推送到生产环境并运行composer update。
由于我们的认知有限,我们不知道Faker的最新版本已经更新到了1.4.0。所以,composer会在生产环境中下载1.4.0的版本,因为我们在composer.json中定义Faker的版本约束为“fzaninotto/faker: 1.*”。因此,生产环境包的版本和开发环境的包的版本不一致,这不是我们预期的结果。
我们建议将composer.lock与composer.json一起部署到生产环境中,并在生产环境中使用composer install安装依赖项。
composer.lock:
虽然composer.json文件允许我们使用版本约束定义所需的包,但composer.lock会跟踪项目中安装的包的确切版本。换句话说,它存储了我们项目的当前状态。这是很重要的一点。
composer install首先读取composer.lock,这使得它成为一个更安全的命令,以下是为什么的理由:
如果从项目中完全删除vender文件夹,则将删除composer下载的所有包。现在再次运行composer install,它将获得与以前相同的软件包版本。
这就引出了我们的下一点。如果我们使用的是像git这样的版本控制系统,我们应该提交composer.lock吗?
答案是“这取决于项目的需要”。大多数时候,我们希望确保每个人在任何时候都共享相同的源代码。所以我们应当提交composer.lock。这是很常见的,因为我们大多数人都和一个团队一起工作。很少有不提交composer.lock的情况发生在我们开发包(库)时,因为用户很少需要在我们的包中运行composer安装。
composer在使用命令方面给了我们很大的灵活性,但是我们需要有以下一些规则来防止出现不必要的麻烦:
- composer install是我们的朋友——在生产部署使用它。
一个标准的composer工作流:
- 在composer.json中定义了一些依赖项:运行composer安装
- 需要一个单独的包,运行:composer require some/package
- 需要多个包:在composer.json文件中定义它们并运行composer update
- 想要测试一个新发布的包,运行:composer require some/package:new-version
- 准备测试发布的所有最新版本的包,运行:composer update
5 自动加载--Autoloading
在PHP中,我们可以使用了很多的include/require语句。这些语句的问题是,它们使我们的代码变得凌乱。最糟糕的是,每当我们更新目录结构时,我们都会做很多查找和替换工作。
解决方案是自动加载。它允许您定义搜索类的路径,这样就不必使用include/require手动执行。但当然,我们应该记住,实际上,自动加载仍在使用include/require。
现在,让我们回到我们的项目。有一个地方我们还没有真正探索过,那就是composer创建的vender目录。默认情况下,composer会将所有包下载到此目录。
composer实际上还生成了一个 vendor/autoload.php 的文件,该文件可以自由地为我们自动填充,使我们很容易的使用vender中的代码。
在我们的例子中,我们希望使用faker,这样我们可以简单地包含下面的文件,faker将被自动加载。
代码语言:javascript复制require __DIR__ . '/vendor/autoload.php';
现在,我们可以开始使用Faker:
代码语言:javascript复制$faker = FakerFactory::create();
echo $faker->name;
6 社区的力量
你现在应该对composer有一个相当的了解。开始使用它来管理项目的依赖关系。我们保证它会使你和你的同事的生活更容易。下一次你的项目需要什么,开始在Packagist上寻找它们。拥抱社区的力量!