【问题标题】:Are global variables in PHP considered bad practice? If so, why?PHP中的全局变量被认为是不好的做法吗?如果是这样,为什么?
【发布时间】:2010-12-06 04:18:42
【问题描述】:
function foo () {
    global $var;
    // rest of code
}

在我的小型 PHP 项目中,我通常采用程序化方式。我通常有一个包含系统配置的变量,当我需要在函数中访问这个变量时,我会使用global $var;

这是不好的做法吗?

【问题讨论】:

标签: php global-variables global


【解决方案1】:

当人们在其他语言中谈论全局变量时,它的含义与它在 PHP 中所做的不同。这是因为变量在 PHP 中并不是真正全局的。一个典型的 PHP 程序的范围是一个 HTTP 请求。会话变量实际上比 PHP 的“全局”变量范围更广,因为它们通常包含许多 HTTP 请求。

通常(总是?)您可以像 preg_replace_callback() 这样的方法调用成员函数,如下所示:

preg_replace_callback('!pattern!', array($obj, 'method'), $str);

请参阅callbacks 了解更多信息。

关键是对象已经被绑定到 PHP 上,并且在某些方面会导致一些尴尬。

不要过分关注将不同语言的标准或结构应用于 PHP。另一个常见的陷阱是试图将 PHP 变成纯 OOP 语言,方法是在一切之上添加对象模型。

与其他任何事物一样,使用“全局”变量、过程代码、特定框架和 OOP,因为它有意义、解决问题、减少您需要编写的代码量或使其更易于维护和理解,而不是因为你认为你应该这样做。

【讨论】:

  • 应该注意的是,PHP 5.3 确实通过 lambda 函数解决了其中的一些问题,允许您避免使用在全局范围内声明的函数进行回调。 +1 可维护、可读的代码建议
  • 在对preg_replace_callback() 的调用中不能使用array ($obj, 'callbackMethod') 形式的回调吗? (我知道,我已经成为这个 OOP 陷阱的牺牲品......)
  • 问题不是“应该使用全局变量吗?”。对此的回答是,“必要时确定”。问题是他们是不好的做法。答案是“是的,有时”。对于海报的小项目,它可能不会有什么坏处 - 但是对于具有许多团队成员和许多移动部件的大型项目,大量使用全局变量会使代码难以调试,几乎不可能重构,甚至是痛苦的读。你有时可以使用它们吗,.. 当然 - 它们很烂吗,.. 是的!
  • @eddiemoya Eddie 说得好。有很多人为使用全局变量等不良做法辩护。你应该像躲避瘟疫一样避开它们。任何像样的软件工程学位都会让你明白这一点......讲师不仅仅是告诉你它的地狱......他们从几十年的经验中知道。您应该尽可能使用成员函数来访问您需要的值,例如 Wordpress 中的 get_query_var() 等。
【解决方案2】:

如果不小心使用全局变量,可能会使问题更难发现。假设您请求一个 php 脚本,然后收到一条警告,说您正在尝试访问某个函数中不存在的数组的索引。

如果您尝试访问的数组是函数的本地数组,请检查该函数以查看您是否在此处出错。函数的输入可能有问题,因此您检查调用函数的位置。

但如果该数组是全局的,您需要检查使用该全局变量的所有位置,不仅如此,您还必须弄清楚这些对全局变量的引用的访问顺序。

如果您在一段代码中有一个全局变量,则很难隔离该代码的功能。为什么要隔离功能?因此,您可以对其进行测试并在其他地方重用它。如果你有一些代码不需要测试也不需要重用,那么使用全局变量就可以了。

【讨论】:

  • 但错误主要显示脚本在哪个文件/行中中断,所以..我在这里看不到问题
  • 脚本中断的地方!= 出错的地方。
【解决方案3】:

我同意the accepted answer。我要补充两点:

  1. 使用前缀,以便您可以立即将其识别为全局(例如 $g_)

  2. 在一个地方声明它们,不要在代码周围散布它们。

【讨论】:

  • 天啊,我总是在我打算全局使用的变量前加上下划线。
  • @KRTac 但 $_testVariable 通常被理解为私有变量——这是定义私有变量而非全局变量的非正式标准。
  • 一种常见的做法是使用全部大写字母来定义全局变量。例如:$DB = 'foo';
【解决方案4】:

从已结束的 SO 文档 Beta 中重新发布

我们可以用下面的伪代码来说明这个问题

function foo() {
     global $bob;
     $bob->doSomething();
}

你的第一个问题很明显

$bob 是从哪里来的?

你感到困惑吗?好的。您刚刚了解了为什么全局变量令人困惑并被认为是一种不好的做法。如果这是一个真正的程序,那么您的下一个乐趣就是追踪$bob 的所有实例并希望您找到正确的实例(如果到处都使用$bob,情况会变得更糟)。更糟糕的是,如果其他人去定义$bob(或者您忘记并重用了该变量),您的代码可能会中断(在上面的代码示例中,有错误的对象,或者根本没有对象,会导致致命错误)。由于几乎所有 PHP 程序都使用像 include('file.php'); 这样的代码,因此您添加的文件越多,维护这样的代码的工作就越难。

我们如何避免全局变量?

避免全局变量的最佳方法是一种称为 Dependency Injection 的哲学。这是我们将所需工具传递给函数或类的地方。

function foo(\Bar $bob) {
    $bob->doSomething();
}

非常更容易理解和维护。无法猜测 $bob 的设置位置,因为调用者负责知道这一点(它向我们传递了我们需要知道的信息)。更好的是,我们可以使用type declarations 来限制正在传递的内容。所以我们知道$bob 要么是Bar 类的实例,要么是Bar 的子类的实例,这意味着我们知道我们可以使用该类的方法。结合标准自动加载器(自 PHP 5.3 起可用),我们现在可以追踪定义 Bar 的位置。 PHP 7.0 或更高版本包含扩展类型声明,您还可以在其中使用标量类型(如intstring)。

【讨论】:

  • 除了必须在任何地方传递 $bob 之外,另一种方法是使 Bar 类成为单例,在 Bar 本身中静态存储 Bar 的实例,并使用静态方法实例化/获取对象。然后你可以在需要的时候$bob = Bar::instance();
  • 请注意单身人士是considered an anti-pattern。依赖注入避免了这些陷阱
  • 您链接到的帖子中存在一定程度的争论(例如,对已接受答案的评价最高的评论和第二受欢迎的答案都强烈反对已接受的答案),这让我倾向于争辩说,应该根据具体情况考虑使用单例,而不是立即放弃。
【解决方案5】:

谁能反对经验、大学学位和软件工程?不是我。我只想说,在开发面向对象的单页 PHP 应用程序时,当我知道我可以从头开始构建整个东西而不必担心命名空间冲突时,我会更开心。从头开始构建是许多人不再做的事情。他们有工作、最后期限、奖金或声誉需要关心。这些类型倾向于使用大量预先构建的高风险代码,因此他们根本无法冒险使用全局变量。

使用全局变量可能不好,即使它们只用于程序的全局区域,但我们不要忘记那些只想玩得开心并让某些东西工作的人。

如果这意味着在全局命名空间中使用一些变量(

一般来说,我不会说使用全局变量是不好的做法。我会说在程序的全局区域之外使用全局变量(标志等)是自找麻烦并且(从长远来看)不明智因为你很容易忘记他们的状态。另外,我想说你学得越多,你对全局变量的依赖就越少,因为你会体验到追踪与它们的使用相关的错误的“乐趣”。仅此一项就会激励您找到解决同一问题的另一种方法。巧合的是,这倾向于将 PHP 人推向学习如何使用命名空间和类(静态成员等)的方向。

计算机科学领域广阔。如果我们因为我们标记它不好而吓唬所有人不敢做某事,那么他们就会失去真正理解标签背后的原因的乐趣。

如果必须使用全局变量,然后看看是否可以在没有它们的情况下解决问题。当您深入了解问题的真实性质,而不仅仅是对问题的描述时,碰撞、测试和调试就意味着更多。

【讨论】:

  • 谢谢你,我很欣赏这里的实用智慧。作为一个没有得到老年人太多支持或不得不进来“证明自己”作为新手的人,我不会说你说的那些话,但在一天结束时。如果您需要使用它,我认为它是一个很好的工具,但它不应该被依赖为您的主要工具,正如您所暗示的那样(学习和扩展如何解决问题,这是一个美妙的旅程) .
【解决方案6】:

作为:

global $my_global; 
$my_global = 'Transport me between functions';
Equals $GLOBALS['my_global']

是不好的做法(比如 Wordpress $pagenow)...嗯

考虑一下:

$my-global = 'Transport me between functions';

是 PHP 错误但是:

$GLOBALS['my-global'] = 'Transport me between functions';

NOT 错误,hypens 不会与“常见”用户声明的变量发生冲突,例如 $pagenow。并且使用大写表示正在使用的超全局,易于在代码中发现,或使用 在文件中查找

进行跟踪

如果我懒得为单个解决方案构建所有内容的类,我会使用连字符,例如:

$GLOBALS['PREFIX-MY-GLOBAL'] = 'Transport me ... ';

但在更广泛使用的情况下,我使用 ONE 全局变量作为数组:

$GLOBALS['PREFIX-MY-GLOBAL']['context-something'] = 'Transport me ... ';
$GLOBALS['PREFIX-MY-GLOBAL']['context-something-else']['numbers'][] = 'Transport me ... ';

后者对我来说,良好实践关于“可乐灯”目标或使用,而不是每次都用单例类来“缓存”一些数据。如果我错了或在这里遗漏了一些愚蠢的东西,请发表评论...

【讨论】:

    猜你喜欢
    • 2012-05-18
    • 2016-05-30
    • 1970-01-01
    • 2011-12-01
    • 2010-10-22
    • 2011-11-20
    • 2011-02-14
    相关资源
    最近更新 更多