跳转到内容

仓库

本章将解释包和仓库的概念、可用的仓库类型以及它们的工作原理。

在我们了解现有的不同类型的代码库之前,我们需要理解Composer所基于的一些基本概念。

Composer是一个依赖管理工具。它在本地安装包。包本质上是一个包含某些内容的目录。在这种情况下,它是PHP代码,但理论上可以是任何东西。并且它包含一个具有名称和版本的包描述。名称和版本用于识别该包。

实际上,在内部,Composer 会将每个版本视为一个独立的包。虽然在使用 Composer 时,这种区别并不重要,但当你想要对其进行更改时,这一点就非常关键了。

除了名称和版本之外,还有一些有用的元数据。与安装最相关的信息是源定义,它描述了从何处获取包内容。包数据指向包的内容。这里有两个选项:dist(发行版)和source(源代码)。

**发行版:**发行版是软件包数据的打包版本。通常是已发布的版本,通常为稳定版本。

**来源:**来源用于开发。它通常源自源代码仓库,例如git。当你想要修改下载的包时,可以获取它。

软件包可以提供其中一种,甚至两种都提供。根据某些因素,例如用户提供的选项和软件包的稳定性,会优先选择其中一种。

仓库是一个包源。它是一个包/版本的列表。Composer会在你所有的仓库中查找你的项目所需的包。

默认情况下,只有Packagist.org仓库在Composer中注册。你可以通过在composer.json中声明更多仓库来将它们添加到你的项目中。

仓库仅对根包可用,并且在您的依赖项中定义的仓库将不会被加载。如果您想了解原因,请阅读FAQ条目

解析依赖项时,会从上到下从各个仓库中查找包,默认情况下,一旦在某个仓库中找到包,Composer就会停止在其他仓库中查找。有关更多详细信息以及如何更改此行为,请阅读仓库优先级文章。

主要的仓库类型是composer仓库。它使用一个包含所有包元数据的packages.json文件。

这也是packagist所使用的仓库类型。要引用composer仓库,请提供packages.json文件之前的路径。就packagist而言,该文件位于/packages.json,因此仓库的URL为repo.packagist.org。对于example.org/packages.json,仓库的URL为example.org

{
"repositories": [
{
"type": "composer",
"url": "https://example.org"
}
]
}

唯一必填字段是packages。JSON结构如下:

{
"packages": {
"vendor/package-name": {
"dev-master": { @composer.json },
"1.0.x-dev": { @composer.json },
"0.0.1": { @composer.json },
"1.0.0": { @composer.json }
}
}
}

@composer.json标记将是该包版本中composer.json的内容,至少应包含:

  • 名称
  • 版本
  • 分发或源

以下是一个最小化的包定义:

{
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
}
}

它可能包含模式中指定的任何其他字段。

notify-batch字段允许你指定一个URL,每当用户安装软件包时,该URL都会被调用。此URL可以是绝对路径(将使用与仓库相同的域名),也可以是完整的URL。

示例值:

{
"notify-batch": "/downloads/"
}

对于包含monolog/monolog包的example.org/packages.json,这会向example.org/downloads/发送一个POST请求,请求体包含以下JSON内容:

{
"downloads": [
{"name": "monolog/monolog", "version": "1.2.1.0"}
]
}

version字段将包含版本号的标准化表示形式。

此字段为可选。

metadata-url、available-packages 和 available-package-patterns

Section titled “metadata-url、available-packages 和 available-package-patterns”

metadata-url字段允许你提供一个URL模板,以服务存储库中的所有包。它必须包含占位符%package%

此字段是Composer v2中的新增字段,如果同时存在provider-includesproviders-url字段,此字段的优先级更高。为了同时兼容Composer v1和v2,理想情况下,你应该同时提供这两个字段。不过,新的仓库实现可能只需要支持v2。

示例:

{
"metadata-url": "/p2/%package%.json"
}

每当Composer查找某个包时,它会将%package%替换为包名,并获取该URL。如果允许该包使用开发稳定性版本,它还会再次加载带有$packageName~dev的URL(例如,/p2/foo/bar~dev.json用于查找foo/bar的开发版本)。

包含包版本的foo/bar.jsonfoo/bar~dev.json文件必须仅包含foo/bar包的版本,格式为{"packages":{"foo/bar":[ ... versions here ... ]}}

缓存是通过使用If-Modified-Since头部来实现的,因此请确保返回Last-Modified头部,并且这些头部信息是准确的。

版本数组也可以选择使用来自composer/metadata-minifierComposer\MetadataMinifier\MetadataMinifier::minify()进行压缩。如果这样做,你应该在顶层添加一个"minified": "composer/2.0"键,以向Composer表明它必须将版本列表扩展回原始数据。示例请参见https://repo.packagist.org/p2/monolog/monolog.json。

任何不存在的请求包都必须返回404状态码,这将向Composer表明该包在你的仓库中不存在。确保404响应速度快,以避免阻塞Composer。避免重定向到其他404页面。

如果你的仓库只包含少量包,并且你想避免404请求,你也可以在packages.json中指定一个"available-packages"键,该键应该是一个包含你的仓库中所有包名称的数组。或者,你可以指定一个"available-package-patterns"键,它是一个包名称模式的数组(其中*匹配任何字符串,例如vendor/*会让Composer在这个仓库中查找所有匹配的包名称)。

此字段为可选。

providers-api字段允许您提供一个URL模板,用于提供所有提供特定包名的包,但不包括具有该名称的包(即使它存在)。该模板必须包含占位符%package%

例如,https://packagist.org/providers/psr/log-implementation.json 列出了一些对 psr/log-implementation 有“provide”规则的包。

{
"providers-api": "https://packagist.org/providers/%package%.json",
}

此字段为可选。

list字段允许返回与给定筛选条件匹配的包名称(如果没有筛选条件,则返回所有名称)。它应接受一个可选的?filter=xx查询参数,其中可以包含*作为通配符,以匹配任何子字符串。

此处不应考虑替换/提供规则。

它必须返回一个包名称数组:

{
"packageNames": [
"a/b",
"c/d"
]
}

例如,参见https://packagist.org/packages/list.json?filter=composer/*。

此字段为可选。

provider-includes字段允许您列出一组文件,这些文件列出了此存储库提供的包名称。在这种情况下,哈希值应为文件的sha256值。

providers-url 描述了如何在服务器上找到提供程序文件。它是从存储库根目录开始的绝对路径。它必须包含占位符 %package%</b1 和 %hash%</b2。

这些字段供Composer v1使用,或者在您的仓库未设置metadata-url字段时使用。

示例:

{
"provider-includes": {
"providers-a.json": {
"sha256": "f5b4bc0b354108ef08614e569c1ed01a2782e67641744864a74e788982886f4c"
},
"providers-b.json": {
"sha256": "b38372163fac0573053536f5b8ef11b86f804ea8b016d239e706191203f6efac"
}
},
"providers-url": "/p/%package%$%hash%.json"
}

这些文件包含用于验证文件完整性的包名称和哈希值列表,例如:

{
"providers": {
"acme/foo": {
"sha256": "38968de1305c2e17f4de33aea164515bc787c42c7e2d6e25948539a14268bb82"
},
"acme/bar": {
"sha256": "4dd24c930bd6e1103251306d6336ac813b563a220d9ca14f4743c032fb047233"
}
}
}

上述文件声明,通过加载providers-url所引用的文件,并将%package%替换为供应商命名空间的包名称,将%hash%替换为sha256字段,即可在该仓库中找到acme/foo和acme/bar。这些文件本身包含如上文所述的包定义。

这些字段是可选的。对于你自己的自定义仓库,你可能不需要它们。

可以通过cURL(启用了ext-curl的Composer 2)或PHP流来访问该代码库。你可以使用options参数设置额外选项。对于PHP流,你可以设置任何有效的PHP流上下文选项。有关更多信息,请参阅上下文选项和参数。使用cURL时,只能配置有限的httpssl选项。

{
"repositories": [
{
"type": "composer",
"url": "https://example.org",
"options": {
"http": {
"timeout": 60
}
}
}
],
"require": {
"acme/package": "^1.0"
}
}

VCS是版本控制系统的缩写。这包括git、svn、fossil或hg等版本控制系统。Composer有一个存储库类型,用于从这些系统安装包。

这有几个使用场景。最常见的是维护第三方库的个人分支。如果你在项目中使用某个库,并且决定对该库进行一些修改,你会希望自己的项目使用这个经过修改的版本。如果该库托管在GitHub上(大多数情况下都是如此),你可以在那里创建它的分支,并将你的修改推送到自己的分支。之后,更新项目的composer.json即可。你只需将自己的分支添加为一个仓库,并更新版本约束以指向你的自定义分支。仅在composer.json中,你应该在自定义分支名称前加上"dev-"(不要将其作为实际分支名称的一部分)。有关版本约束的命名约定,请参阅以获取更多信息。

假设你在 bugfix 分支中修补了 monolog 以修复某个 bug 的示例:

{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/igorw/monolog"
}
],
"require": {
"monolog/monolog": "dev-bugfix"
}
}

当你运行php composer.phar update时,你应该会得到修改后的monolog/monolog版本,而不是来自packagist的版本。

请注意,除非你真的打算长期派生该包,并完全脱离原始包,否则不应重命名该包。由于自定义仓库的优先级高于packagist,Composer会正确选择你的包而不是原始包。如果你想重命名该包,应该在默认(通常是master)分支中进行,而不是在功能分支中,因为包名称是从默认分支获取的。

还要注意,如果你在派生仓库的composer.json文件中修改了name属性,覆盖将无法生效,因为该属性需要与原始属性匹配才能使覆盖生效。

如果其他依赖项依赖于你分叉的包,可以对其进行内联别名处理,使其符合原本不符合的约束条件。有关更多信息,请参阅别名文章。

完全相同的解决方案允许您处理GitHub和Bitbucket上的私有仓库:

{
"repositories": [
{
"type": "vcs",
"url": "git@bitbucket.org:vendor/my-private-repo.git"
}
],
"require": {
"vendor/my-private-repo": "dev-master"
}
}

唯一的要求是为git客户端安装SSH密钥。

Git并非VCS仓库支持的唯一版本控制系统。支持的系统如下:

要从这些系统获取包,你需要安装它们各自的客户端。这可能不太方便。出于这个原因,系统对GitHub和Bitbucket提供了特殊支持,它们利用这些网站提供的API来获取包,而无需安装版本控制系统。版本控制系统仓库为它们提供了dist,这些发行版以压缩包的形式获取包。

  • GitHub: github.com(Git)
  • 钻头桶:bitbucket.org(G it)

将根据URL自动检测要使用的VCS驱动程序。不过,若因任何原因需要指定一个驱动程序,你可以使用bitbucketgithubgitlabperforcefossilgitsvnhg作为存储库类型,而非vcs

如果你在GitHub仓库中将no-api键设置为true,它会像克隆其他任何git仓库一样克隆该仓库,而不是使用GitHub API。但与直接使用git驱动不同,Composer仍会尝试使用GitHub的zip文件。

请注意:

  • 要让Composer选择使用哪个驱动程序,需要将仓库类型定义为“vcs”
  • 如果您已经使用过私有仓库,这意味着Composer应该已经将其克隆到缓存中。如果您想安装带有驱动程序的相同包,请记住先执行命令composer clearcache,然后执行命令composer update,以更新Composer缓存并从发行版安装该包。
  • VCS驱动程序git-bitbucket已被弃用,建议使用bitbucket

请注意,Bitbucket的仓库端点需要是https而不是git。

设置好你的Bitbucket仓库后,你还需要设置身份验证

由于Subversion本身没有分支和标签的概念,Composer默认假设代码位于$url/trunk$url/branches$url/tags。如果你的仓库有不同的布局,可以修改这些值。例如,如果你使用大写名称,可以这样配置仓库:

{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"trunk-path": "Trunk",
"branches-path": "Branches",
"tags-path": "Tags"
}
]
}

如果没有分支或标签目录,你可以通过将branches-pathtags-path设置为false来完全禁用它们。

如果包位于子目录中,例如/trunk/foo/bar/composer.json/tags/1.0/foo/bar/composer.json,那么你可以通过将"package-path"选项设置为该子目录,让Composer访问它,在这个例子中,设置应为"package-path": "foo/bar/"

如果你有一个私人的Subversion代码库,可以在配置的http-basic部分保存凭据(参见配置):

{
"http-basic": {
"svn.example.org": {
"username": "username",
"password": "password"
}
}
}

如果您的Subversion客户端默认配置为存储凭据,这些凭据将为当前用户保存,并且该服务器现有的已保存凭据将被覆盖。要更改此行为,请在您的仓库配置中设置"svn-cache-credentials"选项:

{
"repositories": [
{
"type": "vcs",
"url": "http://svn.example.org/projectA/",
"svn-cache-credentials": false
}
]
}

如果你想使用一个无法通过上述任何方式支持Composer的项目,你仍然可以通过使用package仓库来自行定义该包。

基本上,你需要定义与composer仓库的packages.json中包含的相同信息,但仅针对单个包。同样,最低要求的字段是nameversion,以及distsource中的任意一个。

以下是Smarty模板引擎的示例:

{
"repositories": [
{
"type": "package",
"package": {
"name": "smarty/smarty",
"version": "3.1.7",
"dist": {
"url": "https://www.smarty.net/files/Smarty-3.1.7.zip",
"type": "zip"
},
"source": {
"url": "http://smarty-php.googlecode.com/svn/",
"type": "svn",
"reference": "tags/Smarty_3_1_7/distribution/"
},
"autoload": {
"classmap": ["libs/"]
}
}
}
],
"require": {
"smarty/smarty": "3.1.*"
}
}

通常情况下,你可以省略源部分,因为你其实并不需要它。

如果包含源密钥,引用字段应指向将要安装的版本。当类型字段为git时,该引用将是提交ID、分支或标签名称。

注意:不建议将git分支名称用于引用字段。虽然这是有效的,因为git checkout支持它,但分支名称是可变的,因此无法锁定。

当类型字段为svn时,引用字段应包含在运行svn co时附加到URL的引用。

注意:这种仓库类型存在一些限制,应尽可能避免使用:

  • 除非你更改version字段,否则Composer不会更新该包。
  • Composer不会更新提交引用,所以如果你使用master作为引用,你将不得不删除该包以强制更新,并且还得处理一个不稳定的锁定文件。

包仓库中的"package"键可以设置为一个数组,以定义一个包的多个版本:

{
"repositories": [
{
"type": "package",
"package": [
{
"name": "foo/bar",
"version": "1.0.0",
...
},
{
"name": "foo/bar",
"version": "2.0.0",
...
}
]
}
]
}

虽然大多数时候你可能希望将自己的包放在Packagist上,但在某些情况下,托管自己的代码库也是有必要的。

  • **私营公司软件包:**如果您所在的公司在内部使用Composer管理其软件包,您可能希望将这些软件包设为私有。
  • **独立的生态系统:**如果你有一个拥有自身生态系统的项目,且其包实际上无法被更广泛的PHP社区复用,那么你可能希望将它们与packagist分开存放。WordPress插件就是一个例子。

如果要托管您自己的包,建议使用原生的composer类型仓库,它能提供最佳性能。

有一些工具可以帮助你创建一个composer仓库。

私有 Packagist 是一款托管式或自托管应用程序,提供私有包托管以及 GitHub、Packagist.org 和其他包仓库的镜像服务。

查看 Packagist.com</b0 以获取更多信息。

Satis 是一个静态的 composer 仓库生成器。它有点像一个超轻量级、基于静态文件的 packagist 版本。

你给它一个包含仓库(通常是版本控制系统和包仓库定义)的composer.json文件。它会获取所有被require的包,并生成一个packages.json文件,这就是你的composer仓库。

查看satis GitHub 仓库处理私有包的文章以获取更多信息。

在某些情况下,无法将前面提到的任何一种仓库类型(甚至是VCS类型)置于在线状态。一个典型的例子是通过构建制品进行跨组织的库交换。当然,大多数情况下这些都是私有的。要直接使用这些归档文件,可以使用artifact类型的仓库,该仓库包含一个文件夹,里面存放着这些私有包的ZIP或TAR归档文件:

{
"repositories": [
{
"type": "artifact",
"url": "path/to/directory/with/zips/"
}
],
"require": {
"private-vendor-one/core": "15.6.2",
"private-vendor-two/connectivity": "*",
"acme-corp/parser": "10.3.5"
}
}

每个 zip 制品都是一个 ZIP 归档文件,其根文件夹中包含 composer.json

unzip -l acme-corp-parser-10.3.5.zip
composer.json
...

如果存在两个包含同一软件包不同版本的归档文件,它们都会被导入。当在工件文件夹中添加了一个包含更新版本的归档文件,并且你运行update时,该版本也会被导入,而Composer将更新到最新版本。

除了制品仓库外,你还可以使用路径方式,这允许你依赖本地目录,无论是绝对路径还是相对路径。在处理单体仓库时,这可能会特别有用。

例如,如果你的代码仓库中有以下目录结构:

...
├── apps
│ └── my-app
│ └── composer.json
├── packages
│ └── my-package
│ └── composer.json
...

然后,要将包my/package添加为依赖项,你可以在apps/my-app/composer.json文件中使用以下配置:

{
"repositories": [
{
"type": "path",
"url": "../../packages/my-package"
}
],
"require": {
"my/package": "*"
}
}

如果该包是本地版本控制系统(VCS)仓库,版本可能会通过当前检出的分支或标签来推断。否则,版本应在该包的composer.json文件中明确定义。如果通过这些方式无法解析版本,则默认其为dev-master

当无法从本地版本控制系统(VCS)仓库中推断出版本,或者你想要覆盖该版本时,可以在声明仓库时使用versions选项:

{
"repositories": [
{
"type": "path",
"url": "../../packages/my-package",
"options": {
"versions": {
"my/package": "4.2-dev"
}
}
}
]
}

如果可能,本地包将被符号链接,此时控制台中的输出将显示Symlinking from ../../packages/my-package。如果无法创建符号链接,将复制该包。在这种情况下,控制台将输出Mirrored from ../../packages/my-package

你可以使用"symlink": true强制使用符号链接,或者使用"symlink": false选项强制使用镜像,而不是使用默认的回退策略。从单体仓库部署或生成包时,强制镜像可能会很有用。

**注意:**在Windows系统上,目录符号链接是通过NTFS连接点实现的,因为非管理员用户也可以创建它们。在Windows 7以下版本或proc_open已被禁用的情况下,将始终使用镜像功能。

{
"repositories": [
{
"type": "path",
"url": "../../packages/*",
"options": {
"symlink": false
}
}
]
}

前导波浪号会扩展为当前用户的主文件夹,环境变量会以Windows和Linux/Mac两种符号格式进行解析。例如,~/git/mypackage会自动从/home/<username>/git/mypackage加载mypackage克隆,这等同于$HOME/git/mypackage%USERPROFILE%/git/mypackage

注意: 仓库路径也可以包含通配符,如 *?。有关详细信息,请参阅 PHP glob 函数

您可以配置包的发行版引用(出现在composer.lock文件中)的构建方式。

存在以下模式:

  • none - 引用将始终为 null。这有助于减少锁定文件中的冲突,但会降低清晰度,无法知晓上次更新的时间以及包是否处于最新状态。
  • config - 引用基于包的composer.json和仓库配置的哈希值构建
  • auto(默认使用)- 引用基于类似config的哈希构建,但如果包文件夹包含git仓库,则使用HEAD提交的哈希作为引用。
{
"repositories": [
{
"type": "path",
"url": "../../packages/*",
"options": {
"reference": "config"
}
}
]
}

你可以通过在你的composer.json中添加以下内容来禁用默认的Packagist.org仓库:

{
"repositories": [
{
"packagist.org": false
}
]
}

您可以使用全局配置标志全局禁用Packagist.org:

php composer.phar config -g repo.packagist.org false