目前,我使用一个名为$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'];