PSR-1:2.3。副作用:配置文件中的变量

PSR-1: 2.3. Side Effects: variable inside config file

PSR-1 includes recommendation 2.3. Side Effects:

A file SHOULD declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it SHOULD execute logic with side effects, but SHOULD NOT do both.

考虑 config.php 文件中的这个例子(我自己的):

/**
 * Parsing the database URL.
 * DATABASE_URL is in the form:
 *  postgres://user:password@hostname:port/database
 * e.g.:
 *  postgres://u123:pabc@ec2.eu-west-1.compute.amazonaws.com:5432/dxyz
 */
$url = parse_url(getenv('DATABASE_URL'));
define('DB_HOST', $url['host']);
define('DB_NAME', substr($url['path'], 1)); // get rid of initial slash
define('DB_USER', $url['user']);
define('DB_PASSWORD', $url['pass']);

如果我这样做,我实际上是在不尊重建议。 phpcs 会理所当然地抱怨它,因为变量:

FILE: config.php
-----------------------------------------------------------------------------------------------------------------
FOUND 0 ERRORS AND 1 WARNING AFFECTING 1 LINE
-----------------------------------------------------------------------------------------------------------------
 1 | WARNING | A file should declare new symbols (classes, functions, constants, etc.) and cause no other side
   |         | effects, or it should execute logic with side effects, but should not do both. The first symbol
   |         | is defined on line 17 and the first side effect is on line 162.
-----------------------------------------------------------------------------------------------------------------

另一种选择是:

define('DB_HOST', parse_url(getenv('DATABASE_URL'))['host']);
define('DB_NAME', substr(parse_url(getenv('DATABASE_URL'))['path'], 1));
define('DB_USER', parse_url(getenv('DATABASE_URL'))['user']);
define('DB_PASSWORD', parse_url(getenv('DATABASE_URL'))['pass']);

没有变量,没问题。但这是湿的,难以阅读。

我明白这个建议就是这样,它说的是 "SHOULD",而不是 "MUST"。但这仍然困扰着我……一方面,每当我检查文件时,phpcs 都会抱怨它,但每行只报告一次,为添加更多配置文件中没有位置的 "side effects" 敞开大门。

我对整个 PSR 还是个新手。

我是否错过了在保持可读性的同时摆脱变量的任何巧妙方法?

一个推论是:坚持不折不扣地遵循建议的严肃项目如何处理这个问题?

1。没关系,别出汗

你已经在你的问题中提到了它,但这个建议是应该的,而不是必须的。 如果这是您整个项目中唯一的 PSR-1 问题:干得好!

但您的问题是:其他项目是如何处理这个问题的?

2。远离配置定义

全局常量,如果使用不当,就会成为依赖磁铁。它们引入了耦合并使您的代码更难理解。 This Q&A is a very good read on why you should move away from them.

改为使用依赖注入(是的,标量配置常量也是依赖)。

3。案例研究:Symfony

基于

Symfony的项目使用:

  • YAML(推荐)或 XML 配置文件到 configure 依赖注入容器,以及
  • environment variables,以设置特定于应用程序应在其中的每个环境的配置选项 运行。这些 env vars 在特定于环境的 .env 文件中定义。

例如,要在 Symfony 项目中配置数据库服务,您需要创建一个包含以下内容的 YAML 文件:

services:
    My\Database\Factory: # <-- the class we are configuring
        arguments:
            $url: '%env(DATABASE_URL)' # <-- configure the $url constructor argument

Symfony 将其编译成 PHP 代码,将 DATABASE_URL 环境变量注入到需要它的 class 中。

然后您将在 My\Database\Factory class 的构造函数中解析 DATABASE_URL,并使用结果构建您的数据库 class。

优点:

  • 配置与代码分离
  • 配置很容易改变
  • 配置易于阅读

缺点:

  • 依赖注入和使用 DI 容器有一个学习曲线,需要改变您对构建对象的思考方式。

twelve-factors app 方法中所述:

Apps sometimes store config as constants in the code. This is a violation of twelve-factor, which requires strict separation of config from code. Config varies substantially across deploys, code does not.

The twelve-factor app stores config in environment variables. Env vars are easy to change between deploys without changing any code

关于最佳实践,您的方向是正确的,您只需要纠正一些错误。

1。为环境变量使用变量

您想对不是的东西使用常量。数据库名称的值可能因环境而异。它不是一个常量,它是一个(环境)变量,你应该使用 $dbName = getenv('DB_NAME').
相反,数字 π 是一个常数,它永远不会改变,可以硬编码。

您可以查看 Composer 或 Symfony 组件等开源项目的源代码,您会看到 getenv() 仅用于填充变量。

2。直接使用配置中预期的元素

在您的情况下,您不应将完整的数据库 URL 用作单个配置项。您应该按照配置的预期,将环境变量中的每个元素分开,例如 DB_HOSTDB_NAMEDB_PORT