为什么要引入环境的概念呢?
让我们假设这种非常典型的情形:你们是一个开发团队,成员有Alice, Bob, Charlie等很多人, 你们在自己的计算机上进行开发。其中,Alice使用Linux操作系统,Bob使用Mac OS,而Charlie使用Windows, 每个人使用的IDE也不尽相同。这都没问题,PHP可以在不同平台上跑得很好。
问题其实不在PHP,不在Yii。 问题在于每个成员在开发的同时,都在自己本地进行编程,都在本地搭建了自己的开发和测试环境。 因此,每个人用于所用的数据库名称、用户名、密码都是根据自己的喜好命名的。 这此信息是作为配置项保存在Yii应用的配置文件中的。
这在代码的版本控制上,出现了麻烦。 每次各成员向代码库提交代码时,由于他们都修改了配置文件中的用户名、密码等信息为本地信息。 因此,在提交代码时,产生了冲突。虽然这个冲突不难解决,但是负责维护代码库的Alice觉得这很无聊,也很苦恼。 同时,Bob是个敏感的人,他认为自己本地的数据库连接信息包含了用户名、密码等信息,关系到自己的安全, 不应该提交到团队的代码仓库中去。于是,每次提交前,他都要把这些信息从配置文件中删除,然后再提交。 提交完了再改回来。不然代码连不上数据库呀。因此,Bob也很苦恼。 而Charlie是负责把经过测试的代码部署到产品服务器上。 由于开发端的环境和产品端的完全不同,于是每次部署前,他不得不小心地对照原来产品端的配置文件, 将开发端的配置,改成产品端的。
于是Bob提议,将配置文件排除在代码库之外,不对配置文件进行版本控制。 这样Alice不用去解决无聊的代码冲突了,Bob也不用每次都删除配置再提交了, Charlie也不用每次拉取代码后第一件事就是把数据库连接信息改成服务端的了。
这确实解决了一些问题。但不得不说,你们的代码库是不完整的,里面缺少一个环境配置文件。 更为要命的事,某天你们的团队经过讨论,认为需要在配置文件中加入新的配置项,或者修改一个配置项, 那么你们将不得不通知所有成员,自己手动更新。因为你们未能将该配置文件纳入版本管理,不能实现自动分发。
毫无疑问,这些来回来去运行环境的切换,一定烦透你了。于是贴心的Yii引入了环境的概念来解决这个问题。 其实,Yii1.1中还没有这个特性,Yii2.0的基础模板应用也没这个功能。 这是Yii2.0高级模板引入的技术。 在实际运用中,各开发团队,特别是大型团队,已经摸索出自己的一套环境配置和部署的操作办法了, Yii2.0环境功能,也是需要与团队现有的环境部署规则相契合的,并非另起一炉。
首先了解Yii各环境文件。前面我们讲到,每个Yii环境就是一组配置文件, 包含了入口脚本 index.php和各类配置文件。 其实他们都放在 /path/to/digpage.com/environments 目录下面,我们看看这个目录都有哪些东西
.
├── dev
│ ├── backend
│ │ ├── config
│ │ │ ├── main-local.php
│ │ │ └── params-local.php
│ │ └── web
│ │ ├── index-test.php
│ │ └── index.php
│ ├── common
│ │ └── config
│ │ ├── main-local.php
│ │ └── params-local.php
│ ├── console
│ │ └── config
│ │ ├── main-local.php
│ │ └── params-local.php
│ ├── frontend
│ │ ├── config
│ │ │ ├── main-local.php
│ │ │ └── params-local.php
│ │ └── web
│ │ ├── index-test.php
│ │ └── index.php
│ └── yii
├── prod
│ ├── backend
│ │ ├── config
│ │ │ ├── main-local.php
│ │ │ └── params-local.php
│ │ └── web
│ │ └── index.php
│ ├── common
│ │ └── config
│ │ ├── main-local.php
│ │ └── params-local.php
│ ├── console
│ │ └── config
│ │ ├── main-local.php
│ │ └── params-local.php
│ ├── frontend
│ │ ├── config
│ │ │ ├── main-local.php
│ │ │ └── params-local.php
│ │ └── web
│ │ └── index.php
│ └── yii
└── index.php
从上面的目录结构图中,可以看到,环境目录下有3个东东:
其中, dev 和 prod 结构相同,分别又包含了4个目录和1个文件:
环境目录下的 index.php 并不是通常所说的web入口脚本,它其实是个定义文件。 定义了可以使用的环境,打开这个文件看一眼:
return [
'Development' => [
'path' => 'dev',
'setWritable' => [
'backend/runtime',
'backend/web/assets',
'frontend/runtime',
'frontend/web/assets',
],
'setExecutable' => [
'yii',
],
'setCookieValidationKey' => [
'backend/config/main-local.php',
'frontend/config/main-local.php',
],
],
'Production' => [
'path' => 'prod',
'setWritable' => [
'backend/runtime',
'backend/web/assets',
'frontend/runtime',
'frontend/web/assets',
],
'setExecutable' => [
'yii',
],
'setCookieValidationKey' => [
'backend/config/main-local.php',
'frontend/config/main-local.php',
],
],
];
不用深入去研究,也可以大致猜到它定义了 Development Production 两个环境, 聪明如你,肯定用脚都能猜得出来。其中:
对于分散于各处的 web 和 config 目录而言,它们也是有共性的。
说了这么多,现在串起来看。运行 init 脚本就会将某一环境的系列文件复制到当前的文件中, 这些文件就是 index.php yii 入口文件和 *-local.php 配置文件。 复制到哪呢?复制到了/path/to/digpage.com/ 目录下面, 并覆盖 frontend backend console common 中对应的 config 目录和入口脚本 ( index.php 或 yii , common 中没有入口脚本) 。
在初始情况下,即未运行过 init 脚本之前,各应用目录(frontend, beckend, console)和通用目录(common), 都是已经有一些配置文件了。就是config目录下的 bootstrap.php main.phpparams.php 。
总的讲, *.php 与环境、配置相关的文件都有哪些呢?有表示主配置的 main.php main-local.php , 有表示全局参数的 params.php params-local.php ,表示引导阶段的 bootstrap.php , 有表示入口脚本的 yii 和 index.php 。其中, bootstrap.php 在 别名(Alias) 中有介绍, 且在优先顺序与其他配置文件的原则是一样的,下面就不再重复讲了。
运行了 init 脚本后,环境中的文件也被复制出来。 这些配置文件成套地分布在各应用目录和通用目录的 config 目录下。 而 index.php 入口脚本则分布在各应用目录的 web 目录下, yii 入口脚本则只放在应用根目录下。
入口脚本我们在 入口文件index.php 已经讲了,这里就不讲。剩下的,来看看配置文件们。 其中,所有的 *-local.php 都来自于你选用的环境,表示本地配置的意思。他们不会被写入到代码仓库中。 当然,这些环境,也就是整个 /path/to/digpage.com/environments 目录都会被写入代码仓库。
而所有不带 *-local.php 的main和params配置文件,都不是环境的内容。但在最终的运行环境中,他们是起作用的。
上面讲到的配置文件有很多,有前台、后台、命令行和common的,有带local的、不带local的, 有params、main等,看起来好复杂的样子。那么一个环境发生作用时,这些文件是怎么个顺序呢? 这要看看 入口文件index.php 部分的内容,但总的原则是:
环境在具体使用上,把握这么几个原则:
这样做能达到什么效果呢?
不足之处就是每次 pull 代码时,如果配置文件有更新,需要团队成员调用 init 将新的配置文件覆盖本地配置。 然后需要手动填入敏感信息。但这种情况在初期配置不太稳定的情况下,根据团队迭代频率,一般一天一次。 而等开发进入正常阶段后,配置文件相对稳定,极少有需要修改的。
另外还有一个需要读者朋友们留意的地方,就是每次调用 init 脚本切换、更新环境配置时,cookieValidationKey 都会被重新生成的随机串所覆盖。这往往会导致更新前后 cookieValidationKey不一致。 从安全角度来讲,定时不定时地更新 cookieValidationKey 也是无可厚非的。 但同时我们也要看到由此产生的一个副作用,那就是原先保存在用户机器上的cookie在下次访问时,会全部失效。 一个简单表现就是已经设置了自动登录的用户,在更新 cookieValidationKey 后,全部需要重新登录。
这在大部分情况下,我们认为这是可以接受的。相信大家在使用互联网的过程中,也有遇到过重新登录的情况。 因此,通常我们也可以很放心的使用 init 脚本,而不必特别地去关注自动生成的cookieValidationKey 。
这种情况下, cookieValidationKey 是一个纯粹的、与本地环境密切相关的配置项,我们不用太操心它。
但是,有的情况下, cookieValidationKey 则不宜由 init 脚本来自动生成,而需要运维人员人工进行干预。
需要人工干预的情况,一般出现在对cookie要求比较严格的场景。 如,当你的应用采用分布式架构提供服务,同时运行在多个节点的时候。 有的负载均衡策略会将同一用户的先后2次请求随机分配给不同的节点进行处理。 而如果这两个节点的 cookieValidationKey 不一致,那么就会出现用户就会收到很奇怪的错误信息。
因此,在分布式情况下,最简便的处理方式还是通过人工干预,确保各节点的 cookieValidationKey始终一致。
这种情况下, cookieValidationKey 已经不是一个纯粹的本地环境配置项了,最多算是一个环境级别的配置项, 而与本地、本机没有多大关系了。这种情况下, 应当将 cookieValidationKey 与其他诸如数据库密码等敏感信息等同视之,由具有权限的、负责运维的人员掌握。 并在每次调用 init 脚本后,将所掌握的 cookieValidationKey 填写到相应的配置项中去。
这里舍弃掉了init脚本自动生成的 cookieValidationKey 。 更新前后, cookieValidationKey 未发生改变,因此,对于应用的用户而言没什么影响。
幸运的是,绝大多数情况下,我们还无需对于 cookieValidationKey 操太多的心,可以完全由init脚本自动生成。
其实,对于Yii开发团队而言,刚开始的时候,是将 cookieValidationKey 放在非环境配置文件 main.php 中的。 后来,觉得这个配置项与虽然不一定是本地相关的,但起码与环境有关, 因此后来还是放在环境配置文件main-local.php中。
只是,由于安全上的考虑,对于未设置 cookieValidationKey 的情况会抛出异常。 同时,又为了方便开发者,又采用了让 init 脚本生成随机串的方式来自动配置。
如果觉得《深入理解Yii2.0》对您有所帮助,也请帮助《深入理解Yii2.0》。 谢谢!