使用全局$config变量是不是一种糟糕的做法


Is it bad practice to use a global $config variable?

目前,我使用一个名为$config的变量,这是一个大的关联数组,包含应用程序的设置,如文件夹配置、数据库连接、要匹配的URL等。该变量在其自己的文件system/config.php中定义,该文件包含在主文件system/index.php中。

位于例如system/request.php中的一些系统功能通过global $config; 使用此全局配置变量

这种做法不好吗?为什么?处理全局配置的更好方法是什么?

从任何地方(全局命名空间)引入变量都是糟糕的,因为你不知道它们是如何获得值的,而且每个类或函数都可以更改它们。因此,除非你使用某种final、const等来保护它们,否则从长远来看,它们会破坏系统,你会花几个小时来了解它们是如何获得价值的。

对于OOP,另一种解决方案是使用容器和依赖项注入。

$container = new Container(array(
    'x' => array(
        'a' => 8,
        'y' => array(
            'b' => 123,
            'z' => 456
        )
    )
));
$x = $container->getX();
$x->doSomething();

如果类之间存在强耦合,因为您不希望它们是通用的,那么您可以这样做:

class Container {
    public function __construct(array $config){
        $this->config = $config;
    }
    public function getX(){
        return new X($this->$config['x']);
    }
}
class X {
    public function __construct(array $config){
        $this->a = $config['a'];
        $this->y = new Y($config['y']);
    }
    public function doSomething(){}
}
class Y {
    public function __construct(array $config){
        $this->b = $config['b'];
        $this->z = new Z($config['z']);
    }
}
class Z {
    public function __construct($number){
        $this->number = $number;
    }
}

如果这些类是松散耦合的,但您希望在配置中保留层次结构(例如,因为您的容器中有多个具有不同配置的Y和Z实例),那么您可以这样做:

class Container {
    public function __construct(array $config){
        $this->config = $config;
    }
    public function getX(){
        return new X($this->$config['x']['a'], $this->getXY());
    }
    public function getXY(){
        return new Y($config['x']['y']['b'], $this->getXYZ());
    }
    public function getXYZ(){
        return new Z($config['x']['y']['z']);
    }
}
class X {
    public function __construct($a, Y $y){
        $this->a = $a;
        $this->y = $y;
    }
    public function doSomething(){}
}
class Y {
    public function __construct($b, Z $z){
        $this->b = $b;
        $this->z = $z
    }
}
class Z {
    public function __construct($number){
        $this->number = $number;
    }
}

即使您在没有OOP的情况下进行开发,也应该将依赖项作为参数而不是全局变量发送。

function main($config){
    $x = $config['x'];
    $a = $x['a'];
    $y = $x['y'];
    doXSomething($a, $y);
}
function doXSomething($a, array $y){
    $b = $y['b'];
    $z = $y['z'];
    $p = doY($b, $z);
    // ...
}
function doY($b, $z){
    $q = doZ($z);
    // ...
}
function doZ($z){
    // ...
}

$config = array(
    'x' => array(
        'a' => 8,
        'y' => array(
            'b' => 123,
            'z' => 456
        )
    )
);
main($config);

你的代码应该只依赖于局部变量,所以你可以确信,当应用程序运行时,它们不会从不同的函数中更改。

使用全局变量不好的另一个原因是可测试性。如果你为一个函数编写一个测试,那么你必须设置一个配置变量。如果它嵌套很深,那么你可以做一些类似的事情

$config = array('x' => array('y' => array('z' => 123)))`;
$result = doZ();
expect($result)->toBe(435);

没有全局变量,就可以进行

$result = doZ(123);
expect($result)->toBe(435);

根据文件名,我猜5年前你有某种意大利面条代码。

IMHO,只要一个全局变量没有被不同的进程模拟地修改(即它对所有进程都是只读的),就可以将它放在全局范围内。

模拟只读变量的一种方法是将其封装在一个全局对象中,其中包含一个私有变量和一个返回该变量的公共方法。

示例:

class Config {
    private $conf = array(/*read from file?*/...);
    function getconf() {
        //AFAIK, any attempt to write into the returned array will make PHP's
        //core to duplicate it, so the original config won't be modified
        //(as long as $conf does not contain objects, which are always addressed
        //by reference)
        return $conf; 
    }
} 

从PHP开始,7数组常量可以使用define()定义。您可以在常量标量表达式中使用数组,因此设置$conf值并在常量中定义

    $conf = ['thekey'=>'the value'];
    define(conf, $conf);
    echo conf['thekey'];
相关文章: