我有一个config.php包含在每个页面中。在配置中,我创建一个看起来像这样的数组:
config.php
$config = array(); $config['site_name'] = 'Site Name'; $config['base_path'] = '/home/docs/public_html/'; $config['libraries_path'] = $config['base_path'] . '/libraries'; //etc...
然后,我有了function.php,几乎每个页面也都包含,我必须使用它global $config来访问它- 这 就是我要摆脱的东西!
function.php
global $config
如何$config在不使用代码的情况下访问代码的其他部分global?
$config
global
谁能解释, 为什么 我不应该global在示例中使用?有人说这是一个不好的语气,有人说这是不安全的?
我在哪里以及如何使用它的示例:
function conversion($Exec, $Param = array(), $Log = '') { global $config; $cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec; foreach ($Param as $s) { $cmd .= ' ' . $s; } }
按照Vilx的建议,将所有这些内容放入类中会很酷,但是在这种情况下,我如何将其与以下从配置中提取key并value从数据库中提取的循环联系起来。 我简化了分配$config数组的想法,这是一个示例:
key
value
$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(); } }
此外,我还必须vars从config中设置的功能访问其他内容,例如:$db,$language等。
vars
$db
$language
如果我将它们放在班上,真的可以解决什么问题?如果我使用global,它将真正改变什么?
我在函数中读过PHP global,其中Gordon很好地解释了为什么不应该使用global。我对所有事情都表示同意,但是global在我的案例中,我不使用它来重新分配变量,这将导致如他所说的<-- WTF!!;,))是的,这很疯狂。但是,如果我只需要通过使用函数访问数据库,global $db在这种情况下,问题出在哪里?不使用该怎么办global?
<-- WTF!!
global $db
deceze在同一PHP全局函数中表示: “反对global的一个重要原因是,这意味着该函数依赖于另一个作用域。这将很快变得混乱。”
但是我在这里谈论的是基本的“ INIT”。我基本上设置define但使用vars-嗯,这在技术上是错误的。但是您的函数不依赖于任何东西- 而是$db您可以记住的一个var的名称?这确实是全球性的需求$db,这里的依赖关系在哪里,否则如何使用?
define
PS 我只是有一个想法,我们在这里面临着两种不同思想的冲突,例如: 我的 (但还不太了解面向对象的编程)和那些在OOP中可以称为大师的人(从我当前的观点来看) -对我来说对他们来说显而易见的是新的问题。我认为这就是为什么一遍又一遍地问这个问题的原因。就我个人而言,它已经变得更加清晰了,但是仍然有一些事情需要澄清。
反对global变量的重点是它们非常紧密地耦合代码。您的 整个代码库 取决于a)变量 名称 $config和b)该变量的存在。如果要重命名变量(无论出于何种原因),则必须 在 整个代码库中的 任何地方 进行重命名。您也不能再使用任何依赖于变量的代码了。
带有global变量的示例:
require 'SomeClass.php'; $class = new SomeClass; $class->doSomething();
在上述各行中的任何地方,您都可能会出错,因为类或某些代码SomeClass.php隐式依赖于全局变量$config。尽管只看了课,却没有任何迹象表明。要解决此问题,您必须执行以下操作:
SomeClass.php
$config = array(...); require 'SomeClass.php'; $class = new SomeClass; $class->doSomething();
如果未在其中设置正确的键,则此代码可能 仍会 失败$config。由于不清楚配置数组的哪些部分SomeClass需要或不需要以及何时需要它们,因此很难为其创建正确的环境以使其正常运行。如果您碰巧已经有一个变量$config要用在其他任何地方,它也会产生冲突SomeClass。
SomeClass
因此,不要创建隐式,不可见的依赖关系,而是 注入 所有依赖关系:
require 'SomeClass.php'; $arbitraryConfigVariableName = array(...); $class = new SomeClass($arbitraryConfigVariableName); $class->doSomething();
通过显式传递config数组作为参数,可以解决上述所有问题。这就像在应用程序内部 分发所需的信息 一样简单。它还使应用程序的结构和流程以及讨论的内容变得更加清晰。要达到这种状态(如果您的应用程序当前是一个大问题),可能需要进行一些重组。
您的代码库越大,则您越需要 将 各个部分彼此 分离 。如果每个部分都依赖于代码库中的每个其他部分,则您根本无法单独测试,使用或重用它的任何部分。那简直变成混乱。为了将各个部分彼此分开,请将它们编码为类或函数,这些类或函数将其所有必需数据作为参数。这就在代码的不同部分之间创建了清晰的接缝(接口)。
尝试将您的问题归纳为一个示例:
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);
我将把各个类的实现留给读者练习。当您尝试实现它们时,您会注意到它们非常容易实现且清晰明了,不需要一个即可global。每个函数和类都以函数参数的形式传递其所有必要的数据。还应该显而易见的是,上述组件可以以任何其他组合的形式插入在一起,或者可以轻松地替换其他组件。例如,配置完全不需要来自数据库,或者记录器可以登录到文件而不是数据库,而Foo::conversion无需了解任何信息。
Foo::conversion
示例实现ConfigManager:
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; } }
这是一段非常简单的代码,甚至没有做很多事情。您可能会问为什么要将此作为面向对象的代码。关键是,这使该代码的使用极为灵活,因为它可以将其与其他所有代码完美地隔离开来。输入一个数据库连接,返回一个具有特定语法的数组。输入→输出。清晰的接缝,清晰的界面,最少的,明确定义的职责。您可以使用简单的功能执行相同的操作。
对象具有的额外优点是,它甚至可以进一步将调用的代码loadConfigurationFromDatabase与该函数的任何特定实现分离。如果仅使用global function loadConfigurationFromDatabase(),则基本上还会遇到相同的问题:尝试调用该函数时需要定义该函数,如果要用其他函数替换它,则存在命名冲突。通过使用对象,代码的关键部分移至此处:
loadConfigurationFromDatabase
function loadConfigurationFromDatabase()
$config = $configManager->loadConfigurationFromDatabase($db);
您可以$configManager在这里替换同样具有method的 任何其他对象loadConfigurationFromDatabase。那就是“鸭子打字”。$configManager只要它有一个方法,您都不关心它到底是什么loadConfigurationFromDatabase。如果它走路像鸭子,而嘎嘎叫鸭子,那它就是鸭子。或者说,如果它有一个loadConfigurationFromDatabase方法并返回有效的配置数组,则它是某种ConfigManager。您已将代码与一个特定变量$config,一个特定loadConfigurationFromDatabase函数甚至一个特定变量解耦ConfigManager。可以从任何地方动态更改和替换所有零件,并进行替换和加载,因为代码不依赖于任何其他特定零件。
$configManager
该loadConfigurationFromDatabase方法本身也不依赖于任何一个特定的数据库连接,只要它可以调用query它并获取结果即可。该$db对象被传递到它可能是完全伪造的,并从XML文件中读取其数据或其他地方代替,只要它的 界面 仍然表现相同。
query