如何掌握创建新的环境

3.4 版本
维护中的版本

每一个应用程序都是由代码和一组“规定了代码如何执行”的配置信息组合而成。配置能够定义使用的数据库,或者是否应该缓存一些内容,又或冗长的日志应该如何处理。

在Symfony中,“环境”的思想就是令相同的代码库可以运行在不同的配置之下。例如,dev环境应当使用可令开发简单、友好的配置信息,然而prod环境就应当使用一组优化了速度的配置。

不同的环境,不同的配置文件 

典型的Symfony程序始于三个环境:dev, prod, 和test。就像之前提到的,每种环境都是一种“以不同配置信息执行相同代码库”的呈现方式。因此每种环境要加载它自己的配置文件并不奇怪。如果你使用YAML配置格式,下面的文件将(在加载时)被使用:

  • 对于dev环境:app/config/config_dev.yml
  • 对于prod环境:app/config/config_prod.yml
  • 对于test环境:app/config/config_test.yml

这种加载是通过AppKernel类中默认要用到的一个简单标准所实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
// app/AppKernel.php
 
// ...
 
class AppKernel extends Kernel
{
    // ...
 
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');
    }
}

你可以看到,当Symofny被加载时,它使用了给定的环境来决定“加载哪种配置”。这就漂亮地解决了“多环境”的问题,以一种强大而透明的方式。

当然,现实中,每种环境都只是部分不同于其他。基本上,所有环境都将共享大面积的常规配置。打开config_dev.yml配置文件,你可以看到这是多么轻松而透明地被实现:

1
2
3
4
imports:
    - { resource: config.yml }
 
# ...
1
2
3
4
5
<imports>
    <import resource="config.xml" />
</imports>
 
<!-- ... -->
1
2
3
$loader->import('config.php');
 
// ...

要共享配置信息,每种环境的配置文件首先要导入一个中央配置文件(central configuration file,config.yml)。(原有)文件的其余部分,可以通过覆写个别参数来脱离(中央)默认配置。例如,默认时web_profiler是关闭的。然而在dev环境中,通过修改toolbar选项的值,可以在config_dev.yml配置文件中激活工具条:

1
2
3
4
5
6
7
# app/config/config_dev.yml
imports:
    - { resource: config.yml }

web_profiler:
    toolbar: true
    # ...
1
2
3
4
5
6
<!-- app/config/config_dev.xml -->
<imports>
    <import resource="config.xml" />
</imports>
 
<webprofiler:config toolbar="true" />
1
2
3
4
5
6
7
8
// app/config/config_dev.php
$loader->import('config.php');
 
$container->loadFromExtension('web_profiler', array(
    'toolbar' => true,
 
    // ...
));

在不同的环境下执行程序 

要在每个环境中执行程序,使用app.php (对于 prod 环境) 或者 app_dev.php (对于 dev 环境) 的前端控制器:

1
2
http://localhost/app.php      -> *prod* environment
http://localhost/app_dev.php  -> *dev* environment

如果你的URL中的文件名中没有两者中的一个,那么将由web服务器来决定哪个 文件在背后被执行。如果你用了PHP内建的web server,它知道要去执行app_dev.php文件。在生产环境下,配置你的web服务器来使用app.php这个文件。另外,两者中必有其一被执行(译注:一个环境执行时,另一个将停止)。

上面给定的URL,假设你的web服务器被配置为使用web/ 目录作为程序的根目录,参考安装Symofny以了解更多。

如果你打开这些(前端控制器)文件,你会立即看到被它们所使用的环境都被显式地设置了:

1
2
3
4
5
6
// web/app.php
// ...
 
$kernel = new AppKernel('prod', false);
 
// ...

prod这个值指定了程序将要运行在prod环境下。一个Symfony的程序,可以靠着使用这行代码并改变“环境字符串”来执行任何一种环境。

test环境,在编写功能测试时要用到,它不能通过控制器在浏览器里直接访问。换句话说,不像其他环境,并没有一个app_test.php前端控制器文件。

一个重要但却不相关的环境话题,是false 参数作为了AppKernel构造器的第二个参数。这指定了程序是否运行于“debug mode”(调试模式)。 不管环境是什么,Symfony程序都可以运行于debug mode被设为truefalse的条件之下。这影响到了程序的很多方面,比如在错误页显示“追踪信息”(stacktraces)时,或者缓存文件是否被动态地重置于每一次请求。虽然不是必须,但debug mode在devtest环境下一般被设置为true,而在prod环境下设为false

从内部看,debug mode的值,将成为kernel.debug参数,参数是给service container服务容器用的。如果你查看程序级配置文件,你可以看到参数的使用,比如,在使用Doctrine DBAL时关闭日志功能:

1
2
3
4
doctrine:
   dbal:
       logging: '%kernel.debug%'
       # ...
1
<doctrine:dbal logging="%kernel.debug%" />
1
2
3
4
5
6
7
$container->loadFromExtension('doctrine', array(
    'dbal' => array(
        'logging'  => '%kernel.debug%',
        // ...
    ),
    // ...
));

为命令行选择环境 

默认时,Symfony的命令行在dev环境下执行,同时开启debug mode。使用--env--no-debug选项来调整这种行为:

1
2
3
4
5
6
7
8
# 'dev' environment and debug enabled
$  php bin/console command_name
 
# 'prod' environment (debug is always disabled for 'prod')
$  php bin/console command_name --env=prod
 
# 'test' environment and debug disabled
$  php bin/console command_name --env=test --no-debug

除了--env--debug 选项之外,Symfony命令行的行为也被环境变量(environment variables)所控制。在执行命令之前,Symfony控制台程序要检查环境变量是否存在以及它们的值:

SYMFONY_ENV
把命令的“执行环境”设置为这个变量(译注:指的是--env)的值(dev, prod, test等等);
SYMFONY_DEBUG
如果是0,debug mode关闭。否则debug mode将被开启。

对于生产环境下的服务器来说这个环境变量特别有用,因为它们能够让你确保那条命令始终运行在prod环境下而毋须添加任何辅助的命令选项。

创建一个新环境 

Symfony程序的三个默认环境可以满足多数使用场景。当然,由于一个环境就是一个字符串,对应于一组配置信息,那么创建一个全新环境也就相当容易了。

假设,例如在部署之前,你需要对程序进行检测(benchmark)。一种方式就是使用接近生产环境的配置,但却开启Symfony的web_profiler。这可以让Symfony记录下你的程序在接受检查过程中的相关信息。

完成这个的最佳方式,则是通过一个全新环境,比如称其为benchmark。先创建一个新的配置文件:

1
2
3
4
5
6
# app/config/config_benchmark.yml
imports:
    - { resource: config_prod.yml }

framework:
    profiler: { only_exceptions: false }
1
2
3
4
5
6
7
8
<!-- app/config/config_benchmark.xml -->
<imports>
    <import resource="config_prod.xml" />
</imports>
 
<framework:config>
    <framework:profiler only-exceptions="false" />
</framework:config>
1
2
3
4
5
6
// app/config/config_benchmark.php
$loader->import('config_prod.php')
 
$container->loadFromExtension('framework', array(
    'profiler' => array('only-exceptions' => false),
));

由于(容器)参数被解析的方式,你不能使用它们来动态构建“被导入的路径”。这意味着下面这种配置将无法工作:

1
2
3
# app/config/config.yml
imports:
    - { resource: "%kernel.root_dir%/parameters.yml" }
1
2
3
4
5
6
7
8
9
10
11
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services
        http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <imports>
        <import resource="%kernel.root_dir%/parameters.yml" />
    </imports>
</container>
1
2
// app/config/config.php
$loader->import('%kernel.root_dir%/parameters.yml');

由于有了这个简单的扩展,程序现在可以支持一个全新环境即benchmark了。

这个新配置文件从prod环境中导入配置,然后更改(覆写)了它。这可以确保全新环境不同于prod环境,除了文件中那些被显式修改了的地方之外。

因为你希望这个环境可以通过浏览器来访问,你应该为它创建一个前端控制器。拷贝web/app.php文件到web/app_benchmark.php,然后把“环境”编辑为benchmark

1
2
3
4
5
6
7
// web/app_benchmark.php
// ...
 
// change just this line
$kernel = new AppKernel('benchmark', false);
 
// ...
1
http://localhost/app_benchmark.php

新环境已经可以访问到,通过:

一些环境,像是dev,永远不可以在“已部署的服务器”上被公开访问到。这是因为特定的环境皆有调试(debugging)之目的,会给出“程序/程序背后的基础设施”的大量信息。为了确保这些环境不被访问到,前端控制器一般通过下面这种“基于控制器”的代码来防范外部IP地址:

1
2
3
if (!in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
    die('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}

环境和缓存目录 

Symfony在很多方面都利用到缓存:程序配置、路由配置、Twig模板以及其他许多内容,都是被缓存为PHP对象并以文件方式存于文件系统中。

默认时,这些缓存文件大量被存入var/cache目录下。然而,每种环境都在对自己的文件进行缓存:

1
2
3
4
5
6
your-project/
├─ var/
│  ├─ cache/
│  │  ├─ dev/   # cache directory for the *dev* environment
│  │  └─ prod/  # cache directory for the *prod* environment
│  ├─ ...

有时,在debugging的时候“通过检查缓存文件来了解某些东西是如何运作的”是有用的。这样做的时候,记得在当前所使用的环境之目录下进行寻找(开发和调试中,多数时候都是dev)。但也可能不同,var/cache/dev目录包括以下内容:

appDevDebugProjectContainer.php
缓存版的“服务容器”,里面有缓存的程序级配置。
appDevUrlGenerator.php
从路由配置信息中生成的PHP类,用于生成URL。
appDevUrlMatcher.php
用于匹配路由的PHP类——在这里可以找到被编译过的正则表达式,用于匹配传入的URLs(并映射)到不同的路由。
twig/
这个目录包含了所有的缓存了的Twig模板。

你可以很容易地改变目录位置及其名称。更多内容请参考如何覆写Symfony默认的目录结构一文。

Going Further 

参考如何在服务容器中设置外部参数

本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。

登录symfonychina 发表评论或留下问题(我们会尽量回复)