我有一个config.php
包含在每个页面.在配置中我创建一个类似于下列的数组:
$config = array();
$config['site_name'] = 'Site Name';
$config['base_path'] = '/home/docs/public_html/';
$config['libraries_path'] = $config['base_path'] . '/libraries';
//etc...
Run Code Online (Sandbox Code Playgroud)
然后我有function.php
,几乎每个页面都包括,我必须使用它global $config
来访问它 - 这就是我想要摆脱的!
如何$config
在不使用代码的情况下访问代码的其他部分global
?
任何人都可以解释一下,为什么我不应该global
在我的例子中使用?有人说这是一个糟糕的语气,其他人说它不安全?
我在何处以及如何使用它的示例:
function conversion($Exec, $Param = array(), $Log = '') {
global $config;
$cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
foreach ($Param as $s)
{
$cmd .= ' ' . $s;
}
}
Run Code Online (Sandbox Code Playgroud)
按照Vilx的建议,将所有这些放在类中会很酷但在这种情况下,我如何将它与从提取配置key
和value
数据库的以下循环联系起来.
我过分简化了分配$config
数组的想法,这是一个例子:
$sql = "SELECT * from settings";
$rsc = $db->Execute($sql);
if ( $rsc ) {
while(!$rsc->EOF) {
$field = $rsc->fields['setting_options'];
$config[$field] = $rsc->fields['setting_values'];
@$rsc->MoveNext();
}
}
Run Code Online (Sandbox Code Playgroud)
此外,我必须vars
从配置中设置的功能中访问其他功能,而且它们很少,例如:$db
,$language
等等.
如果我把它们放在课堂上它会解决任何问题吗?如果我使用global
它真正改变了什么?
我在函数中阅读了PHP全局,其中Gordon以非常好的方式解释了为什么你不应该使用它global
.我同意一切,但我不会global
在我的情况下重新分配变量,这将导致,就像他说的那样<-- WTF!!
;))是的,同意,这很疯狂.但是,如果我只是需要通过使用函数来访问数据库,global $db
在这种情况下问题出在哪里?如果不使用,你怎么做global
呢?
在函数deceze的同一个PHP全局中说:"反对全局的一个重要原因是它意味着函数依赖于另一个范围.这将很快变得混乱."
但我在这里谈的是基本的'INIT'.我基本上设置define
但使用vars
- 这在技术方面是错误的.但是你的功能不依赖于任何东西 - 但是$db
你可以记住一个变量的名称?这真的是全球需要使用$db
,这里的依赖性在哪里以及如何使用它?
PS我刚才有一个想法,我们正在面对两个不同思想的冲突,例如:我的(但不太了解面向对象的编程)和那些可以在OOP中被称为大师(从我目前的观点来看)的人 - 对我来说看起来很明显的东西引发了新的问题.我想这就是为什么一遍又一遍地问这个问题的原因.就个人而言,它毕竟变得更加清晰,但仍有一些事情需要澄清.
dec*_*eze 51
反对global
变量的要点是他们非常紧密地结合代码.您的整个代码库依赖于a)变量名称 $config
和b)该变量的存在.如果要重命名变量(无论出于何种原因),则必须在整个代码库中的任何位置执行此操作.您也不能再使用依赖于变量的任何代码.
带global
变量的示例:
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
Run Code Online (Sandbox Code Playgroud)
上述行中的任何位置都可能会出现错误,因为类或某些代码SomeClass.php
隐式依赖于全局变量$config
.只是看着课堂,没有任何迹象表明这一点.要解决这个问题,你必须这样做:
$config = array(...);
require 'SomeClass.php';
$class = new SomeClass;
$class->doSomething();
Run Code Online (Sandbox Code Playgroud)
如果您未在内部设置正确的密钥,此代码可能仍会失败$config
.由于配置阵列的哪些部分SomeClass
需要或不需要以及何时需要它们并不明显,因此很难为正确运行重新创建正确的环境.如果你碰巧已经有一个变量$config
用于其他任何你想要使用的东西,它也会产生冲突SomeClass
.
因此,不是创建隐式的,不可见的依赖项,而是注入所有依赖项:
require 'SomeClass.php';
$arbitraryConfigVariableName = array(...);
$class = new SomeClass($arbitraryConfigVariableName);
$class->doSomething();
Run Code Online (Sandbox Code Playgroud)
通过明确地将配置数组作为参数传递,解决了上述所有问题.这就像在应用程序内部处理所需信息一样简单.它还使应用程序的结构和流程与更清晰的内容进行了对话.要达到这种状态,如果你的申请目前是泥泞的大球,可能会进行一些重组.
代码库越大,就越需要将各个部分相互分离.如果每个部分都依赖于代码库中的每个其他部分,那么您根本无法单独测试,使用或重用它的任何部分.这简直就是陷入了混乱.要将部件彼此分开,请将它们编码为将所有必需数据作为参数的类或函数.这样可以在代码的不同部分之间创建干净的接缝(接口).
试着将你的问题结合在一起作为一个例子:
require_once 'Database.php';
require_once 'ConfigManager.php';
require_once 'Log.php';
require_once 'Foo.php';
// establishes a database connection
$db = new Database('localhost', 'user', 'pass');
// loads the configuration from the database,
// the dependency on the database is explicit without `global`
$configManager = new ConfigManager;
$config = $configManager->loadConfigurationFromDatabase($db);
// creates a new logger which logs to the database,
// note that it reuses the same $db as earlier
$log = new Log($db);
// creates a new Foo instance with explicit configuration passed,
// which was loaded from the database (or anywhere else) earlier
$foo = new Foo($config);
// executes the conversion function, which has access to the configuration
// passed at instantiation time, and also the logger which we created earlier
$foo->conversion('foo', array('bar', 'baz'), $log);
Run Code Online (Sandbox Code Playgroud)
我将把各个班级的实施作为读者的练习.当您尝试实现它们时,您会注意到它们非常容易实现,并且不需要单个global
.每个函数和类都以函数参数的形式获取所有必需的数据.显而易见的是,上述组件可以以任何其他组合插在一起,或者依赖性可以容易地替代其他组件.例如,配置根本不需要来自数据库,或者记录器可以记录到文件而不是数据库,而Foo::conversion
不必知道任何这些.
示例实现ConfigManager
:
class ConfigManager {
public function loadConfigurationFromDatabase(Database $db) {
$result = $db->query('SELECT ...');
$config = array();
while ($row = $result->fetchRow()) {
$config[$row['name']] = $row['value'];
}
return $config;
}
}
Run Code Online (Sandbox Code Playgroud)
这是一段非常简单的代码甚至没有做太多.您可能会问为什么要将此作为面向对象的代码.关键在于,这使得使用此代码非常灵活,因为它将其与其他所有代码完美隔离.你给一个数据库连接,你得到一个具有特定语法的数组.输入→输出.清晰的接缝,清晰的界面,最小的,明确的职责.您可以使用简单的功能执行相同的操作.
对象的额外优势在于它甚至可以进一步解耦loadConfigurationFromDatabase
从该函数的任何特定实现调用的代码.如果你只是使用全局function loadConfigurationFromDatabase()
,你基本上会再次遇到同样的问题:当你试图调用它时需要定义该函数,如果你想用其他东西替换它就有命名冲突.通过使用对象,代码的关键部分在此处移动:
$config = $configManager->loadConfigurationFromDatabase($db);
Run Code Online (Sandbox Code Playgroud)
您可以$configManager
在此替换任何其他具有方法的对象loadConfigurationFromDatabase
.这是"打字打字".你不在乎究竟$configManager
是什么,只要它有一个方法loadConfigurationFromDatabase
.如果它像鸭子一样走路,像鸭子一样嘎嘎叫,它就是一只鸭子.或者更确切地说,如果它有一个loadConfigurationFromDatabase
方法并返回一个有效的配置数组,它就是某种ConfigManager.您已将代码与一个特定变量$config
,一个特定loadConfigurationFromDatabase
函数甚至一个特定变量分离ConfigManager
.所有部件都可以更改和换出,并从任何地方动态加载和加载,因为代码不依赖于任何一个特定的其他部分.
该loadConfigurationFromDatabase
方法本身也不依赖于任何一个特定的数据库连接,只要它可以调用query
它并获取结果.$db
传递给它的对象可能完全是伪造的,只要其界面仍然表现相同,就可以从XML文件或其他任何地方读取数据.
我用课程解决了这个问题:
class Config
{
public static $SiteName = 'My Cool Site';
}
function SomeFunction
{
echo 'Welcome to ' , Config::$SiteName;
}
Run Code Online (Sandbox Code Playgroud)
fcortes建议使用常量也是一个很好的建议.我只想建议给所有常量一个前缀,比如CFG_SITE_NAME
,以避免与其他常量的意外名称冲突.
对于您的情况,我将创建一个constants.php
包含定义的唯一文件(如果您的目的是这些"变量"永远不会在执行时间中更改):
define('SITE_NAME','site name');
define('BASE_PATH','/home/docs/public_html/');
...
Run Code Online (Sandbox Code Playgroud)
将其包含constants.php
在您需要的所有文件中:
include_once('constants.php');
Run Code Online (Sandbox Code Playgroud)