composer-docs/cn-introduction/05-repositories.md

18 KiB
Raw Blame History

资源库

本章将解释包和库的概念,什么样的存储库是可用的,以及它们如何工作。

概述

在此之前,我们看到存在不同类型的资源库,我们需要了解一些基本概念,以理解 Composer 是如何构建于其上的。

Composer 是一个依赖管理工具。它在本地安装一些资源包。一个包本质上就是一个包含东西的目录。通常情况下它存储 PHP 代码,但在理论上它可以是任何东西。并且它包含一个描述,其中有一个名称和一个版本号,这个名称和版本号用于识别该包。

事实上,在 composer 内部将每一个版本都视为一个单独的包。尽管在你使用 composer 时这种区别无关紧要,但当你想改变它时,这就显得至关重要。

除了名称和版本号,还存放了有用的元数据。与安装关系最密切的是 source 信息它申明了在哪里可以获得资源包的内容。包数据指向包内容并有两种指向方式dist 和 source。

Dist: dist 指向一个存档,该存档是对一个资源包的某个版本的数据进行的打包。通常是已经发行的稳定版本。

Source: source 指向一个开发中的源。这通常是一个源代码仓库,例如 git。当你想要对下载下来的资源包进行修改时可以这样获取。

你可以使用其中任意一个或者同时使用。这取决于其它的一些因素比如“user-supplied 选项”和“包的稳定性”,前者将会被优先考虑。

资源库

一个资源库是一个包的来源。它是一个 packages/versions 的列表。Composer 将查看所有你定义的 repositories 以找到你项目需要的资源包。

默认情况下已经将 Packagist.org 注册到 Composer。你可以在 composer.json 中申明更多的资源库,把它们加入你的项目中。

资源库的定义仅可用于“root 包”,而在你依赖的包中定义的资源库将不会被加载。如果你想了解其中的原因,请阅读 FAQ entry

Types

Composer

主资源库的类型为 composer。它使用一个单一的 packages.json 文件,包含了所有的资源包元数据。

这也是 packagist.org 所使用的资源类型。要引用一个 composer 资源库,只需要提供一个存放 packages.json 文件的 目录路径。比如要引用 packagist.org 下的 /packages.json,它的 URL 就应该是 packagist.org。而 example.org/packages.json 的 URL 应该是 example.org

packages

唯一必须的字段是 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
  • version
  • dist or source

这是一个最简单的包定义:

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

它还可以包含任何在 架构 中介绍的字段。

notify-batch

notify-batch 字段允许你指定一个 URL它将会在用户安装每一个包时被调用。该 URL 可以是(与其资源库相同域名的)绝对路径或者一个完整的 URL 地址。

例如使用下面的值:

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

对于 example.org/packages.json 包含的 monolog/monolog 包,它将会发送一个 POST 请求到 example.org/downloads/,使用下面的 JSON request body

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

version 字段将包含标准化的版本号。

notify-batch 字段是可选的。

includes

对于较大的资源库,可以拆分 packages.json 为多个文件。includes 字段允许你引用这些额外的文件。

例:

{
    "includes": {
        "packages-2011.json": {
            "sha1": "525a85fb37edd1ad71040d429928c2c0edec9d17"
        },
        "packages-2012-01.json": {
            "sha1": "897cde726f8a3918faf27c803b336da223d400dd"
        },
        "packages-2012-02.json": {
            "sha1": "26f911ad717da26bbcac3f8f435280d13917efa5"
        }
    }
}

文件的 SHA-1 码允许它被缓存,仅在 hash 值改变时重新请求。

此字段是可选的。你也许并不需要它来自定义存储库。

provider-includes and providers-url

的对于非常大的资源库,像 packagist.org 使用 so-called provider 文件是首选方法。provider-includes 字段允许你设置一个列表,来申明这个资源库提供的包名称。在这种情况下文件的哈希算法必须使用 sha256。

providers-url 描述了如何在服务器上找到这些 provider 文件。它是以资源库的根目录为起点的绝对路径。

例:

{
    "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"
        }
    }
}

上述文件申明了 acme/fooacme/bar 可以在这个资源库找到,通过加载由 providers-url 引用的文件,替换 %name% 为包名并且替换 %hash% 为 sha256 的值。这些文件本身只包含上文提到的 packages 的定义。

这些字段是可选的。你也许并不需要它们来自定义存储库。

stream options

packages.json 文件是用一个 PHP 流加载的。你可以使用 options 参数来设定额外的流信息。你可以设置任何有效的PHP 流上下文选项。更多相关信息请查看 Context options and parameters

VCS

VCS 表示版本控制系统。这包括像 git、svn 或 hg 这样的版本管理系统。Composer 有一个资源类型可以从这些系统安装软件包。

从 VCS 资源库加载一个包

这里有几个用例。最常见的是维护自己 fork 的第三方库。如果你在项目中使用某些库,并且你决定改变这些库内的某些东西,你会希望你项目中使用的是你自己的修正版本。如果这个库是在 GitHub 上(这种情况经常出现),你可以简单的 fork 它并 push 你的变更到这个 fork 里。在这之后你更新项目的 composer.json 文件,添加你的 fork 作为一个资源库,变更版本约束来指向你的自定义分支。关于版本约束的命名约定请查看 库(资源包)

例如,假设你 fork 了 monologbugfix 分支修复了一个 bug

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

当你运行 php composer.phar update 时,你应该得到你修改的版本,而不是 packagist.org 上的 monolog/monolog

注意,你不应该对包进行重命名,除非你真的打算摆脱原来的包,并长期的使用你自己的 fork。这样 Composer 就会正确获取你的包了。如果你确定要重命名这个包,你应该在默认分支(通常是 master 分支)上操作,而不是特性分支,因为包的名字取自默认分支。

如果其它包依赖你 fork 的这个分支,可能要对它做版本号的行内别名设置,才能够准确的识别版本约束。更多相关信息请查看 别名

使用私有资源库

完全相同的解决方案,也可以让你使用你 GitHub 和 BitBucket 上的私人代码库进行工作:

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

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

Git 的备选方案

Git 并不是 VCS 资源库唯一支持的版本管理系统。

以下几种都是被支持的:

为了从这些系统获取资源包,你必须安装对应的客户端,这可能是不方便的。基于这个原因,这里提供了 GitHub 和 BitBucket 的 API 的特殊支持,以便在无需安装版本控制系统的情况下获取资源包。在 VCS 资源库提供的 dist 中获取 zip 存档。

VCS 驱动将基于 URL 自动检测版本库类型。但如果可能,你需要明确的指定一个 gitsvnhg 作为资源库类型,而不是 vcs

Subversion Options

Since Subversion has no native concept of branches and tags, Composer assumes by default that code is located in $url/trunk, $url/branches and $url/tags. If your repository has a different layout you can change those values. For example if you used capitalized names you could configure the repository like this:

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

If you have no branches or tags directory you can disable them entirely by setting the branches-path or tags-path to false.

If the package is in a sub-directory, e.g. /trunk/foo/bar/composer.json and /tags/1.0/foo/bar/composer.json, then you can make composer access it by setting the "package-path" option to the sub-directory, in this example it would be "package-path": "foo/bar/".

PEAR

It is possible to install packages from any PEAR channel by using the pear repository. Composer will prefix all package names with pear-{channelName}/ to avoid conflicts. All packages are also aliased with prefix pear-{channelAlias}/

Example using pear2.php.net:

{
    "repositories": [
        {
            "type": "pear",
            "url": "http://pear2.php.net"
        }
    ],
    "require": {
        "pear-pear2.php.net/PEAR2_Text_Markdown": "*",
        "pear-pear2/PEAR2_HTTP_Request": "*"
    }
}

In this case the short name of the channel is pear2, so the PEAR2_HTTP_Request package name becomes pear-pear2/PEAR2_HTTP_Request.

Note: The pear repository requires doing quite a few requests per package, so this may considerably slow down the installation process.

Custom vendor alias

It is possible to alias PEAR channel packages with a custom vendor name.

Example:

Suppose you have a private PEAR repository and wish to use Composer to incorporate dependencies from a VCS. Your PEAR repository contains the following packages:

  • BasePackage
  • IntermediatePackage, which depends on BasePackage
  • TopLevelPackage1 and TopLevelPackage2 which both depend on IntermediatePackage

Without a vendor alias, Composer will use the PEAR channel name as the vendor portion of the package name:

  • pear-pear.foobar.repo/BasePackage
  • pear-pear.foobar.repo/IntermediatePackage
  • pear-pear.foobar.repo/TopLevelPackage1
  • pear-pear.foobar.repo/TopLevelPackage2

Suppose at a later time you wish to migrate your PEAR packages to a Composer repository and naming scheme, and adopt the vendor name of foobar. Projects using your PEAR packages would not see the updated packages, since they have a different vendor name (foobar/IntermediatePackage vs pear-pear.foobar.repo/IntermediatePackage).

By specifying vendor-alias for the PEAR repository from the start, you can avoid this scenario and future-proof your package names.

To illustrate, the following example would get the BasePackage, TopLevelPackage1, and TopLevelPackage2 packages from your PEAR repository and IntermediatePackage from a Github repository:

{
    "repositories": [
        {
            "type": "git",
            "url": "https://github.com/foobar/intermediate.git"
        },
        {
            "type": "pear",
            "url": "http://pear.foobar.repo",
            "vendor-alias": "foobar"
        }
    ],
    "require": {
        "foobar/TopLevelPackage1": "*",
        "foobar/TopLevelPackage2": "*"
    }
}

Package

If you want to use a project that does not support composer through any of the means above, you still can define the package yourself by using a package repository.

Basically, you define the same information that is included in the composer repository's packages.json, but only for a single package. Again, the minimum required fields are name, version, and either of dist or source.

Here is an example for the smarty template engine:

{
    "repositories": [
        {
            "type": "package",
            "package": {
                "name": "smarty/smarty",
                "version": "3.1.7",
                "dist": {
                    "url": "http://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.*"
    }
}

Typically you would leave the source part off, as you don't really need it.

Note: This repository type has a few limitations and should be avoided whenever possible:

  • Composer will not update the package unless you change the version field.
  • Composer will not update the commit references, so if you use master as reference you will have to delete the package to force an update, and will have to deal with an unstable lock file.

Hosting your own

While you will probably want to put your packages on packagist most of the time, there are some use cases for hosting your own repository.

  • Private company packages: If you are part of a company that uses composer for their packages internally, you might want to keep those packages private.

  • Separate ecosystem: If you have a project which has its own ecosystem, and the packages aren't really reusable by the greater PHP community, you might want to keep them separate to packagist. An example of this would be wordpress plugins.

For hosting your own packages, a native composer type of repository is recommended, which provides the best performance.

There are a few tools that can help you create a composer repository.

Packagist

The underlying application used by packagist is open source. This means that you can just install your own copy of packagist, re-brand, and use it. It's really quite straight-forward to do. However due to its size and complexity, for most small and medium sized companies willing to track a few packages will be better off using Satis.

Packagist is a Symfony2 application, and it is available on GitHub. It uses composer internally and acts as a proxy between VCS repositories and the composer users. It holds a list of all VCS packages, periodically re-crawls them, and exposes them as a composer repository.

To set your own copy, simply follow the instructions from the packagist github repository.

Satis

Satis is a static composer repository generator. It is a bit like an ultra- lightweight, static file-based version of packagist.

You give it a composer.json containing repositories, typically VCS and package repository definitions. It will fetch all the packages that are required and dump a packages.json that is your composer repository.

Check the satis GitHub repository and the Satis article for more information.

Artifact

There are some cases, when there is no ability to have one of the previously mentioned repository types online, even the VCS one. Typical example could be cross-organisation library exchange through built artifacts. Of course, most of the times they are private. To simplify maintenance, one can simply use a repository of type artifact with a folder containing ZIP archives of those private packages:

{
    "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"
    }
}

Each zip artifact is just a ZIP archive with composer.json in root folder:

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

If there are two archives with different versions of a package, they are both imported. When an archive with a newer version is added in the artifact folder and you run update, that version will be imported as well and Composer will update to the latest version.

Disabling Packagist

You can disable the default Packagist repository by adding this to your composer.json:

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

Schema | Community