Composer 的结构

composer-cartoon

这片文章是 composer.json 中各个字段的说明书。

一、Root Package(根目录包)

根目录包就是在你的项目的根目录由 composer.json 定义的包。主要就是由 composer.json 来定义你的项目的依赖。

某些字段只能在根目录包的中使用,比如 config 字段,只有根目录包能定义自己的配置。依赖包中的 config 字段是被忽略的。所以 config 字段是 root-only 的。

如果你克隆了其中一个依赖包并在上面工作,那么这个包就是根目录包。composer.json 还是一样的,但上下文不同。

(注:一个包是不是根目录包,取决于上下文。)

二、composer.json 中的各个属性(字段)

1、name

包的名字。由供应方(vendor)名和项目名组成,用 / 分隔。

在发布包的时候需要填。

2、description

对包的一个简短描述,通常是一行的长度。

在发布包的时候需要填。

3、version

包的版本。

格式必须是 X.Y.Z,选择性后缀:-dev、-alphaN、-betaN、-RCN。

4、type

包的类型,默认为 library。

包类型用于定制安装逻辑。如果你的包的安装需要一些特殊的逻辑,你可以定义一个定制的类型。它可以是一个 symfony-bundle 的类型,或者 wordpress-plugin,或者 typo3-module。这些类型将被特定的项目所用,它们将提供安装器来安装这些类型的包。

Composer 支持 3 种类型:

library:默认值。它将复制文件到 vendor 目录。

project:它表示这是个项目,而不是库。比如像 Symfony 标准版这种应用。

metapackage:一个含有依赖的空包,能触发安装,但不包含文件,不会向文件系统写任何东西。

composer-install:为其他的定制类型的包提供安装器的包。

5、keywords

一个与包相关的关键词数组。用于包的搜索和过滤。

可选。

6、homepage

项目的网站 URL。

可选。

7、time

版本发布时间。必须是 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS 格式。

可选。

8、license

包的许可证。可以是字符串或字符串数组。

可选,但强烈建议加上。

9、authors

包的作者。是个对象数组。

每个 author 对象有这些属性:

name:作者名字
email:作者邮箱
homepage:作者网站 URL
role:作者在项目中的角色(如:developer 或 translator)

10、support

各种关于该项目如何获取支持的信息。包含这些属性:

email:获取支持的邮箱
issues:问题跟踪的 URL
forum:论坛的 URL
wiki:Wiki 的 URL
irc:IRC 的频道
source:查看或下载源码的 URL

可选。

11、Package links

依赖包的映射表,由包名映射版本约束。如:

(1)require

列出包所依赖的包。除非这些依赖已经存在,否则这个包不会被安装。

(2)require-dev(root-only)

列出开发这个包(或跑测试等等)所依赖的包。在使用 install 命令时,只有带上 “–dev” 参数才能安装 dev 包。在使用 update 命令时,带上 “–no-dev” 则不更新。

(3)conflict

列出包会和哪些包发生冲突。它们将不被允许和你的包一起安装。如果约束了版本,则只会针对特定的版本。

(4)replace

列出哪些包要被这个包替代。

(5)provide

这个包所推荐的包列表。这个对公共接口最有用,一个包可以依赖一个虚拟的 logger 包,而实现 logger 接口的库可以放到 provide 字段中。

12、suggest

建议一些能让这个包工作的更好或得到增强的包列表。这些信息只在包安装完成时给出,暗示用户可以添加更多包,虽然不是必须要安装的。

格式是,包名映射文字说明,如:

13、autoload

提供给 PHP autoloader 的自动加载映射。

目前支持的有:PSR-0 自动加载规范,classmap 生成器,还有 files。

PSR-0 是比较推荐的,因为它的优秀的扩展性(在添加新的类的适合,不需要重新生成自动加载器)。

(1)PSR-0

在 psr-0 键名下,定义一个命名空间到路径的映射表,相对于包的根目录。注意,这也同样支持 PEAR-style 的没有命名空间的风格。

请注意命名空间的声明得以 \\ 结尾,确保自动加载器正确响应。

PSR-0 的引用可以在安装或更新时生成的文件中查看:
vendor/composer/autoload_namespaces.php

例子:

如果你需要在多个目录里查找同一个前缀的命名空间,你可以用数组,如:

PSR-0 风格并不局限于加载命名空间的声明的东西,也可以用于类这个层级。当库中只有一个在全局命名空间中的类时,这种方式就能用上。比如你有个 PHP 源文件放在项目的根目录,你可以这样声明:

如果你有个目录下全是用命名空间组织的,你可以用空前缀:

(2)Classmap

classmap 的引用可以在安装或更新时生成的文件中查看:
vendor/composer/autoload_classmap.php

类映射表是通过扫描指定的目录或文件下的所有的 .php 和 .inc 文件生成的。

你可以给任何不支持 PSR-0 的库用 classmap 生成器实现自动加载。配置上只要指定类所在的目录或文件即可:

(3)files

如果你确定需要在任何请求中都加载某些文件,你可以使用 files 自动加载机制。对于那些包中有些 PHP 函数但不能自动加载时特别有用。例如:

14、include-path

(将被弃用,它的功能由 autoload 代替。其实就是设置 include_path,可选)

15、target-dir

指定安装目标路径。

如果包的根目录是在命名空间下,自动加载就不正确了,所以才有 target-dir 来解决这个问题。

Symfony 就是个例子。它由很多组件包组成。Yaml 组件是在 Symfony\Component\Yaml  命名空间下的,它的根目录是 Yaml 目录。要让自动加载正常工作,我们要确保它不是安装在 vendor/symfony/yaml ,而是在 vendor/symfony/yaml/Symfony/Component/Yaml ,这样自动加载器才能从 vendor/symfony/yaml 加载它。

所以要定义 target-dir 如下:

16、minimum-stability(root-only)

定义根据稳定性如何过滤包。默认是 stable,如果你信赖一个 dev 包,你需要指明。

17、prefer-stable(root-only)

如果开启,Composer 会在稳定包和不稳定包中选择前者。

18、repositories(root-only)

定制包的仓库地址。

默认的,Composer 只使用 Packagist 仓库。通过指定仓库地址,你可以从任何地方获取包。

仓库不能递归。你只能将它们添加到主的 composer.json 中。所依赖包中 composer.json 文件中的仓库定义是被忽略的。

支持的仓库的类型有:

(1)composer

composer 仓库通过网络提供 packages.json 文件,它包含一个 composer.json 对象的列表,还有额外的 dist 或 source 信息。packages.json 文件通过 PHP 流加载。

(2)vcs

版本控制系统仓库,如:git、svn、hg。

(3)pear

通过它,你可以导入任何 pear 仓库到你的项目中。

(4)package

如果你依赖一个不支持 composer 的项目,你可以定义一个 package 类型的仓库,然后将 composer.json 对象直接写入。

完整的例子:

19、config(root-only)

针对项目的一些配置。

process-timeout:默认 300 秒,Composer 进程执行超时时间;
use-include-path:默认 false,如果是 true,Composer 自动加载器也会到 PHP 的 include_path 中查找;
preferred-install:默认 auto,设置 Composer 安装方式;
github-protocols:默认 [“git”, “https”],设置与 github 通信协议;
github-oauth:设置 oauth;
vendor-dir:默认 vendor,你可以换成别的;
bin-dir:默认 vendor/bin,如果项目有二进制文件,会链接到这;
cache-dir:默认 $home/cache,存放 Composer 运行时产生的缓存;
cache-files-dir:默认 $cache-dir/files,存放包的 zip 文件;
cache-repo-dir:默认 $cache-dir/repo,存放仓库元数据;
cache-vcs-dir:默认 $cache-dir/vcs,存放 vcs 克隆;
cache-files-ttl:默认六个月,缓存的过期时间;
cache-files-maxsize:默认 300M;
notify-no-install:默认 true,从仓库安装包会有个通知,可以关掉;
discard-changes:默认false,如何处理脏的更新;

20、scripts(root-only)

Composer 允许你在安装进程中安装钩子脚本,钩子是基于事件的;

21、extra

供 scripts 消费的额外数据;

22、bin

指定哪些文件必须被当做二进制文件处理的;

23、archive

设置创建包时的选项,exclude 属性可以设置排除哪些目录,例如:

 

这篇看下来,再看 Symfony 的标准版中的 composer.json,应该了然了吧。

Composer 的库

the_house_of_the_famous_composer

继《Composer 基本用法》,接着翻译官方文档的下一部分:Composer 的库。之所以想到翻译这部分,是因为,之前我的项目是基于 Symfony 2 框架,是框架在用 Composer,而不是我,现在我在准备自己的项目,我要用 Composer,得知道的更多。

适合阅读对象:
1、了解什么是 Composer(不了解的看这里:《Composer (作曲家),PHP 的依赖管理器》)
2、想了解如何让你的包能通过 Composer 安装
3、想了解 Composer 大的体系

一、所有项目都是一个包

如果你的目录里有一个 composer.json,那这个目录就是个包。当你在项目中添加了一项 ”require“,你就创建了一个依赖其他包的包。你的项目和外部库的唯一区别就是:你的项目是一个没有名字的包。

如果想让你的包能通过 Composer 安装,你需要给它指定一个名字。通过给 composer.json 添加 name 字段即可:

例子中,项目的名称为 acme/hello-world,其中 acme 是供应方(vendor)的名字。供应方名是必须强制提供的。

(注:如果你不知道供应方名该怎么取,你的 GitHub 用户名通常是不错的选择。因为包名是大小写敏感的,所以一般用小写字符并在词之间用横杠分隔)

二、平台的包

Composer 也有平台的包,这些包是并不是由 Comoposer 安装,它们是安装在操作系统上的东西,Composer 将它们作为虚拟的包,以方便管理。它们包括:PHP 本身,PHP 的扩展,还有一些系统中的库。

1、php 包
代表用户的 PHP 版本,这样就允许你对 PHP 版本定义依赖,比如,“>=5.4.0”。如果必须要 64 位版本的 PHP,你可以依赖 “php-64bit” 这个包。

2、ext-<name> 包
你可以依赖 PHP 的扩展(包括核心扩展)。至于包的版本,这里有很多不一致,建议就用 “*”。比如你的项目用到 GD 库,就可以依赖 “ext-gd” 这个包。

3、lib-<name> 包
你可以依赖一些 PHP 用到的系统库。比如:curl、iconv、libxml、openssl 等。

你可以用命令 “composer show –platform” 查看你本地的有效的平台包。比如我的结果是:

三、指定版本

你需要通过某种方式指定包的版本。不过当你在 Packagist 上发布包时,它会通过 VCS(version control system, 如:git,svn,hg)推断出版本信息,所以这种情况下就不需要指定版本。

如果你手工创建包,则必须指定它的版本,可以用 version 字段:

1、Tags(标签)

对应每个 tag,都会有一个版本的包创建。tag 名称必须匹配 “X.Y.Z” 或 “vX.Y.Z”,还有一些可选的后缀名,如:RC,beta,alpha,patch 等。

这里有些 tag 名称的例子:

2、Branches(分支)

对应每个分支,都会有一个开发版本的包创建。如果分支的名字看起来像个版本的命名,则版本名将会是 “{branchname}-dev”。比如,一个叫 “2.0” 的分支,会得到一个 “2.0.x-dev” 的版本(“.x” 是因为技术原因而添加的,目的是让它看起来像个分支,“2.0.x” 这种分支也是有效的,也会被转换为 “2.0.x-dev”)。

如果分支名看起来不像版本号,它的版本名会是 “dev-{branchname}”。比如,master 分支的版本名为 “dev-master”。

(注:如果你安装一个开发版本,Composer 将会从来源来安装它)

3、Aliases(别名)

可以将分支名化名为版本名。比如,你可以将 “dev-master” 化名为 “1.0.x-dev”,以后你可以在其他包里依赖 “1.0.x-dev”。

四、锁文件

如果你愿意,可以为你的代码库提交 “composer.lock” 文件。这可以保证你的团队始终在测试同一份依赖的包,不会因为依赖的包的更新带来干扰。但是,这个锁文件不会对其他依赖它的项目造成任何影响,它只影响当前这个项目。

五、发布到 VCS(版本控制系统)

如果你的 VCS 中有一个项目含有 “composer.json” 文件,那你的库已经可以被 Composer 安装了。下面这个例子,假设我们在 GitHub 上发布了 “acme/hello-world”,位置在 “github.com/username/hello-world”。

我们在本地创建一个项目,我们叫它 “acme/blog”。这个项目将依赖 “acme/hello-world”,而后者又依赖了 “monolog/monolog”。我们可以通过下面一两步完成这些处理。

先创建个 “blog” 目录,里面创建个 “composer.json” 文件:

”name“ 字段在这里不是必须的,因为我们没想要将它发布为一个库。

然后我们要告诉这个博客应用怎么去找 ”hello-word“ 这个依赖。我们可以添加一个包的仓库定义语句到 ”composer.json“:

OK 了。现在你可以运行 Composer 的 ”install“ 命令来安装依赖。

(再次提醒:任何 git/svn/hg 仓库,如果含有 ”composer.json“,就能添加到你的项目中,只要你指定包的仓库地址,并声明依赖它)。

六、发布到 Packagist(packagist.org 这个网站)

好了,现在你能够发布你的包了。但是每次指定 VCS 仓库会显得很笨重。而且你也不想强迫你的用户来做这事情。

有件事你可能注意到了,就是上面例子中,我们没有指定 ”monolog/monolog“ 的仓库。那它是如何工作的?答案就是:Packagist。

Packagist 是 Composer 的首要的包的仓库,而且这个仓库默认就是可用的,不需要额外定义。任何发布到 Packagist 的包都能被 Composer 自动使用。monolog 就是在 Packagist 上,所以我们依赖它,可以不需要添加一个额外的仓库地址。

如果你要和世界分享你的 ”hello-world“,你可以将它发布到 Packagist。很简单的。

 

题图:《the house of the famous composer》by ali

Google AppEngine 适合托管 PHP 应用么?

php-gae

翻译一段 GAE 托管 PHP 应用的利弊分析文章。以下为内容。

 

你也许想知道,AppEngine 是否真的对 PHP 网站支持的很好。

现在,GAE 将官方的 PHP-5.4 定制和整合到 Google 云平台。很多常用的扩展已经编译进去,当然,不是所有。

这当然是一个有约束的环境,所以你别指望所有 PHP 特性都能用上。GAE 环境和常规环境会存在一些差异,有利的和不利的都有。

还没有人把真正的使用报告给出来,差异肯定比我们想象的要多,我们可以先从官方文档中观察出一些差异。

有利的:

1、几乎无穷的扩张能力

如果你的站点出现了一个请求的高峰,理论上 AppEngine 会处理这些负载。如果是常规环境下,你就需要小心地添加一些服务器并且分发那些请求到这些服务器上。

2、自动备份

AppEngine 会自动备份你的应用和数据库,你也可以按需要定制备份计划。

3、分布式的 session

AppEngine 模拟了一个 memcached 服务,不管你的应用代码在不在同一台机器,你都可以存取数据。

通过这样来存取 session 数据,比 PHP 原生的本地文件的实现方式要好多了。

4、任务队列

AppEngine 提供了队列接口,你可以将脚本的执行延迟到后端进程。为了方便队列的使用,官方已经提供封装好的类库。

你还可以安排任务的执行时间表,就像使用 crontab 一样。

(译者注:任务队列确实是普通虚拟主机提供商不能提供的东东,嗯,还有 memcached 服务。备份还好说,无穷的扩展能力,一般小站还不奢望能用上。)

不利的:

1、没有本地文件系统可以用了

很多 PHP 应用都是可以读写自己的文件的,比如配置文件、用户上传的文件。这些数据现在都是通过特殊的文件流句柄写到云存储服务中了。

你就不能用 require 或 include 去执行动态创建的文件了。虽然看起来,这提高了安全性,但也给很多 PHP 应用强加了限制。

举个栗子,很多 PHP 应用都用到模板引擎,模板引擎的实现就是将模板文件解析为可执行的 PHP 文件,在显示页面时 include 这些解析好的 PHP 文件。所以很多模板引擎就不能用了。还有,类似 WordPress 自动升级的功能也就跑不起来了。

解决方案似乎也有,把云存储上的文件读取下来,然后赋值给变量,然后用 eval 去执行。当然,很多应用根本还不会这样工作。

(译者注:eval 这种方式太不可取了,可以先放一边。没有本地文件系统确实是硬伤,模板引擎也只能用那些不产生中间文件的。很少有应用是不读写本地文件的,所以要上 AppEngine 的应用都避免不了调整)

2、没有远程资源获取途径(如:sockets、Curl)

至少现在看起来,是不能用 fsockopen 或 Curl 扩展去建立 socket 连接的。

如果要连接到远程站点,你得用 fopen、file_get_contents 等类似函数。

(译者注:这样搞起来,很多远程连接的类库也不能用了,是出于什么目的要禁用这些函数和扩展呢?)

3、URL 由 AppEngine 的配置来映射了,而非 mod_rewrite

因为 AppEngine 运行在 Google 的 Web 服务器上,mod_rewrite 这样的扩展肯定不能用了。

URL 和 PHP 脚本的映射是由特定格式的配置文件管理的,得学。

(译者注:配置文件格式和语法如果和 rewrite 的语法类似,问题倒不大)

4、没有 PHP 错误日志了

至少现在看起来,是没有 PHP 错误日志的入口的。意味着,如果生产环境的 PHP 代码出错了,你会看不到它们。

(译者注:AppEngine 应该提供其他渠道能看到吧?)

 

更多细节可以参考:gaeforphp.appspot.com (官网,被墙了)
文章翻译自:Is Google AppEngine a Good Solution for PHP Application Hosting?