常见问题
- 如何为我的框架自定义一个资源包安装目录?
- 我应该提交 vendor 目录中的依赖包吗?
- 为什么自由的版本约束是坏主意?
- 为什么说“比较符”和“通配符”相结合的版本约束是坏主意?
- 为什么 Composer 不递归加载储存库?
如何为我的框架自定义一个资源包安装目录?
每个框架都可能有一个或多个不同的依赖包安装目录。Composer 可以通过使用 composer/installers 来配置,安装依赖到其它的目录,而不是默认的 vendor
目录。
如果你是一个 包作者 并且希望自己的资源包被安装到自定义的目录中,简单的 require composer/installers
依赖,并设置适当的 type
属性。这是常见的方式,如果你的资源包专门用于一个框架,如 CakePHP、Drupal 或 WordPress。这是一个 WordPress 主题的 composer.json 文件的例子:
{
"name": "you/themename",
"type": "wordpress-theme",
"require": {
"composer/installers": "~1.0"
}
}
现在当你用 Composer 安装这个主题时,它就会被放置在 wp-content/themes/themename/
目录。目前已被支持的 type
类型请查看 current supported types。
对于一个 包使用者 你可以为一个包设置或覆盖安装路径,requires composer/installers 并在 extra 下设置 installer-paths
属性。Drupal 的多站点设置就是一个很好的例子,其中的资源包应该被安装到各自网站的子目录。在这里我们使用 composer/installers 来覆盖安装路径:
{
"extra": {
"installer-paths": {
"sites/example.com/modules/{$name}": ["vendor/package"]
}
}
}
现在该资源包将被安装到你指定的目录,并替换 $name
变量,而不是默认目录。
注意: 你不能通过它改变所有包的安装目录。这仅适用于引入
composer/installers
依赖,自定义了 type 属性的资源包。
我应该提交 vendor 目录中的依赖包吗?
一般情况下 不建议。vendor 目录(或者你安装依赖的其它目录)都应该被添加进 .gitignore
/svn:ignore
/等等。
最好这么做,然后让所有开发人员使用 Composer 来安装依赖包。同样,build server、CI、deployment tools 等等,应进行修改,使运行 Composer 成为其项目引导的一部分。
虽然在某些环境下提交它是很让人心动的,但它将导致一些问题:
- 当你更新代码时,将极大的增加 VCS 仓库的体积和差异。
- 在你自己的 VCS 中将产生与你依赖的资源包重复的历史记录。
- 通过 git 的一个 git 仓库安装添加依赖,将把它们视作子模块。这是有问题的,因为它们并不是真正的子模块,并且你将会遇到这些问题。
如果你真的觉得你必须这样做,你有几个选择:
- 限制自己安装标记版本(无 dev 版本),这样你只会得到 zip 压缩的安装,并避免 git“子模块”出现的问题。
- 使用 --prefer-dist 或在 config 选项中设置
preferred-install
为dist
。 - 在每一个依赖安装后删除其下的
.git
文件夹,然后你就可以添加它们到你的 git repo 中。你可以运行rm -rf vendor/**/.git
命令快捷的操作,但这意味着你在运行 composer update 命令前需要先删除磁盘中的依赖文件。 - 新增一个 .gitignore 规则(
vendor/.git
)来忽略 vendor 下所有.git
目录。这种方法不需要你在运行 composer update 命令前删除你磁盘中的依赖文件。
为什么自由的版本约束是坏主意?
没有上限的版本约束,如*
,> = 3.4
或dev-master
将允许依赖项更新到任何未来版本。这包括突破向后兼容性的主要版本。
一旦你的软件包被标记了,你就不能再修改它的依赖项了,如果依赖性破了BC,你必须做一个新的版本,但是前一个版本会被破坏。
唯一的好选择是定义约束的上限,在测试包与新的依赖项的主要版本兼容后,可以在新版本中增加它的上限。
例如,您应该使用~3.4
,而不是使用>= 3.4
,它允许所有版本最多为3.999
,但不包括4和以上。~
操作符和语义版本控制配合使用很有效。
提示:作为一个包维护器,您可以通过为开发部门提供“别名版本”使其能够匹配绑定约束,从而简化用户的使用。
为什么说“比较符”和“通配符”相结合的版本约束是坏主意?
这是人们常犯的一个错误,定义了类似 >=2.*
或 >=1.1.*
的版本约束。
通过思考它所表示的真正含义,你很快就会发现,它并没有多大意义。如果我们分解 >=2.*
,你会得到两个部分:
>=2
表示资源包应该是 2.0.0 或以上版本。2.*
表示资源包版本应该介于 2.0.0 (含)和 3.0.0(不含)之间。
正如你所看到的,要同时满足这两个规则包版本必须 >=2.0.0,但它是无法判断的,因为当你这么写的时候,你究竟是想要包含 3.0.0 版本还是不包含?它应该进行匹配吗?因为你定义了 >=2
,但同时你又限制它为 2.*
。
出于这个原因,Composer 将抛出一个错误,并告诉你这是无效的。想要确切的表达你意思,最简单的方法就是仅使用“比较符”和“通配符”其中的一种来定义约束。
为什么 Composer 不递归加载储存库?
当你使用自定义库时,你可能会碰到问题,因为 Composer 不会递归加载你要求的储存库,所以你必须修改这些储存库中所有的 composer.json
文件。
在详细说明为什么是这样之前,你需要明白:使用自定义 VCS & 包储存库去尝试某些事情,或者使用你 fork 的一个分支,直到你的 pull request 被合并,等等。你不应该使用它们来跟踪你的私人资源包,关于这点你应该看看 setting up Satis 来为你的公司甚至自己处理私人资源包。
这里有三个途径可以使依赖解析器使用你自定义的储存库:
-
读取根包的存储库,从定义的存储库得到所有的软件包,解析依赖需求。这是目前的状态,它工作得很好,除了有“无法递归的加载储存库”这个限制。
-
读取根包的存储库,同时从定义的 repos 初始化资源包,递归的初始化,根据所有依赖包中定义的 repos,以及这些依赖包所依赖的其它包中定义的 repos,等等,然后再解析依赖需求。这可能可以工作,但会严重影响初始化的速度,因为每读取一个 VCS repos 都需要几秒钟。它可能最终执行失败,因为一个包的不同版本,可能来自一个包资源库中一个相同的包,但来至不同的 dist/source 。这样有太多的可能会出错。
-
读取根包的存储库,然后读取第一级依赖,接着读取这些依赖包所依赖的其它包,等等,然后再解析依赖需求。这样听起来更有效率,但仍然存在第二种解决方案中的问题。因为加载依赖的储存库并不像听起来那么容易。你需要加载所有可能匹配的依赖包的 repos,而这些包的定义又可能是互相冲突的。
转载本站内容时,请务必注明来自W3xue,违者必究。