当前位置: 首页 > 面试题库 >

停止在PHP中使用`global`

晏华奥
2023-03-14
问题内容

我有一个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来访问它- 就是我要摆脱的东西!

如何$config在不使用代码的情况下访问代码的其他部分global

谁能解释, 为什么 我不应该global在示例中使用?有人说这是一个不好的语气,有人说这是不安全的?

编辑1:

我在哪里以及如何使用它的示例:

function conversion($Exec, $Param = array(), $Log = '') {
        global $config;
        $cmd = $config['phppath'] . ' ' . $config['base_path'] . '/' . $Exec;
                foreach ($Param as $s)
                {
                    $cmd .= ' ' . $s;
                }
 }

编辑2:

按照Vilx的建议,将所有这些内容放入类中会很酷,但是在这种情况下,我如何将其与以下从配置中提取keyvalue从数据库中提取的循环联系起来。
我简化了分配$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();
    }
}

编辑3:

此外,我还必须vars从config中设置的功能访问其他内容,例如:$db$language等。

如果我将它们放在班上,真的可以解决什么问题?如果我使用global,它将真正改变什么?

编辑4:

我在函数中读过PHP global,其中Gordon很好地解释了为什么不应该使用global。我对所有事情都表示同意,但是global在我的案例中,我不使用它来重新分配变量,这将导致如他所说的<-- WTF!!;,))是的,这很疯狂。但是,如果我只需要通过使用函数访问数据库,global $db在这种情况下,问题出在哪里?不使用该怎么办global

编辑5:

deceze在同一PHP全局函数中表示:
“反对global的一个重要原因是,这意味着该函数依赖于另一个作用域。这将很快变得混乱。”

但是我在这里谈论的是基本的“ INIT”。我基本上设置define但使用vars-嗯,这在技术上是错误的。但是您的函数不依赖于任何东西-
而是$db您可以记住的一个var的名称?这确实是全球性的需求$db,这里的依赖关系在哪里,否则如何使用?

PS 我只是有一个想法,我们在这里面临着两种不同思想的冲突,例如: 我的
(但还不太了解面向对象的编程)和那些在OOP中可以称为大师的人(从我当前的观点来看)
-对我来说对他们来说显而易见的是新的问题。我认为这就是为什么一遍又一遍地问这个问题的原因。就我个人而言,它已经变得更加清晰了,但是仍然有一些事情需要澄清。


问题答案:

反对global变量的重点是它们非常紧密地耦合代码。您的 整个代码库 取决于a)变量 名称
$config和b)该变量的存在。如果要重命名变量(无论出于何种原因),则必须 整个代码库中的 任何地方
进行重命名。您也不能再使用任何依赖于变量的代码了。

带有global变量的示例:

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

在上述各行中的任何地方,您都可能会出错,因为类或某些代码SomeClass.php隐式依赖于全局变量$config。尽管只看了课,却没有任何迹象表明。要解决此问题,您必须执行以下操作:

$config = array(...);

require 'SomeClass.php';

$class = new SomeClass;
$class->doSomething();

如果未在其中设置正确的键,则此代码可能 仍会
失败$config。由于不清楚配置数组的哪些部分SomeClass需要或不需要以及何时需要它们,因此很难为其创建正确的环境以使其正常运行。如果您碰巧已经有一个变量$config要用在其他任何地方,它也会产生冲突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无需了解任何信息。

示例实现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(),则基本上还会遇到相同的问题:尝试调用该函数时需要定义该函数,如果要用其他函数替换它,则存在命名冲突。通过使用对象,代码的关键部分移至此处:

$config = $configManager->loadConfigurationFromDatabase($db);

您可以$configManager在这里替换同样具有method的
任何其他对象loadConfigurationFromDatabase。那就是“鸭子打字”。$configManager只要它有一个方法,您都不关心它到底是什么loadConfigurationFromDatabase。如果它走路像鸭子,而嘎嘎叫鸭子,那它就是鸭子。或者说,如果它有一个loadConfigurationFromDatabase方法并返回有效的配置数组,则它是某种ConfigManager。您已将代码与一个特定变量$config,一个特定loadConfigurationFromDatabase函数甚至一个特定变量解耦ConfigManager。可以从任何地方动态更改和替换所有零件,并进行替换和加载,因为代码不依赖于任何其他特定零件。

loadConfigurationFromDatabase方法本身也不依赖于任何一个特定的数据库连接,只要它可以调用query它并获取结果即可。该$db对象被传递到它可能是完全伪造的,并从XML文件中读取其数据或其他地方代替,只要它的
界面 仍然表现相同。



 类似资料:
  • 我有一个包含到每个页面。在配置中,我创建了一个类似于以下内容的数组: 然后我有,它也包含在几乎每个页面中,在那里我必须使用来访问它-这就是我想要摆脱的! 如何在不使用的情况下访问代码的其他部分? 谁能解释一下,为什么我不应该在我的示例中使用?有人说这是一个坏的语气,其他人说这是不安全的? 我在哪里以及如何使用它的示例: 按照Vilx的建议,将所有这些都放在类中会很酷,但在这种情况下,我将如何将其与

  • 问题内容: 如果我在命令行中键入,则可以看到以下行: 要 启动 此cronjob,我需要使用命令编辑文件,删除该行开头的注释字符,保存已编辑的文件,然后退出编辑器。 要 停止 此cronjob,请执行相同的步骤,但在该行的开头添加注释字符。 我想使用 PHP脚本 实现完全相同的效果,而不是手动编辑文件。 问题答案: 我进行了一些研究,并在论坛中找到以下消息: 将EDITOR环境变量设置为php脚本

  • 问题内容: 在装有PHP 5.5.3的新Macbook上安装MAMP。 重新加载和刷新无济于事。依然没有。Google过了几分钟,试图找出问题所在,然后回来刷新。有用。有没有搞错? 我进入php.ini并禁用了所有新的OPcache,并将默认缓存时间设置为0。向文档添加了标头以强制不进行缓存。还是同样的问题。这里到底发生了什么? 网络标签显示的是HTTP 200请求,因此文件中的任何新HTML都可

  • 启动无限循环后,我无法关闭JFrame。 我想停止无限循环使用停止按钮。 我用开始按钮开始一个无限循环。我想用“停止”按钮关闭那个回路。 > if(stop.getModel(). isP的()){中断;}不工作 actionListener用于识别按钮单击并在循环也不起作用时使用中断语句终止 点击停止按钮,无限循环必须终止。在使用start Button启动无限循环后,我无法使用JFrame中的

  • 我有一个简单的UILabel,当你长按它时,它就会开始旋转。我使用以下代码成功地实现了这一点: 用户只需双击标签,就可以在任何时候停止这个动画。下面是代码: 问题是,下次你长按标签时,它没有反应。旋转动画不会再次启动。你必须再次点击它才能让它再次运行。 所以模式是: 1)长按-动画开始 2)双击-动画停止 3)长按-什么都没有发生 4)再次长按-动画再次开始 我该怎么解决这个问题?

  • 我有一个程序,它每秒钟响一次,直到停止。问题是,在我按下“开始”并发出嘟嘟声后,我无法单击“停止”按钮,因为窗口冻结。欢迎任何帮助。