PHP中的全局变量是否被视为不良做法?如果是这样,为什么?

KRT*_*Tac 84 php global global-variables

function foo () {
    global $var;
    // rest of code
}
Run Code Online (Sandbox Code Playgroud)

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

这是不好的做法吗?

cle*_*tus 99

当人们谈论其他语言中的全局变量时,它意味着与PHP中的变量不同.那是因为PHP中的变量并不是真正的全局变量.典型PHP程序的范围是一个HTTP请求.会话变量实际上具有比PHP"全局"变量更广的范围,因为它们通常包含许多HTTP请求.

通常(总是?)你可以用这样的方法调用成员函数preg_replace_callback():

preg_replace_callback('!pattern!', array($obj, 'method'), $str);
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅回调.

重点是对象已经用螺栓固定在PHP上,并在某些方面导致一些尴尬.

不要过分关注将不同语言的标准或构造应用于PHP.另一个常见的陷阱是试图通过将对象模型放在所有内容之上来将PHP转换为纯OOP语言.

像其他任何东西一样,使用"全局"变量,过程代码,特定框架和OOP,因为它有意义,解决问题,减少您需要编写的代码量或使其更易于维护和更易于理解,而不是因为您认为你应该.

  • 问题不是"应该使用全局变量吗?".答案就是"必要时确保必要".问题是他们是不好的做法.答案是"是的,有时候".对于海报的小项目,没有什么不好的可能会带来什么 - 但是,对于许多团队成员和许多移动部件,大量使用全局变量的较大项目将使代码很难调试,几乎是不可能的重构,并有疼痛感,甚至读.你有时可以使用它们吗...当然 - 他们真的很傻,是的! (21认同)
  • 应该注意的是,PHP 5.3确实通过lambda函数解决了一些问题,允许您避免使用在全局范围内声明的函数进行回调.+1表示可维护,可读的代码建议 (8认同)

roj*_*oca 27

如果不仔细使用全局变量会使问题更难找到.假设您请求一个php脚本,并且您收到一条警告,说您正在尝试访问某个函数中不存在的数组的索引.

如果您尝试访问的数组是函数的本地数组,则检查函数以查看是否在那里犯了错误.输入函数可能会出现问题,因此您需要检查函数调用的位置.

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

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

  • 脚本破坏的地方!=错误的地方. (8认同)

Don*_*son 16

我同意cletus.我会添加两件事:

  1. 使用前缀,以便您可以立即将其标识为全局(例如$ g_)
  2. 在一个地方声明它们,不要在代码周围撒上它们.

最好的问候,不要

  • @KRTac但$ _testVariable通常被理解为私有变量 - 这是定义私有变量的非正式标准,而不是全局变量. (9认同)
  • 通常的做法是使用ALL CAPS定义全局变量.例如:`$ DB ='foo';` (5认同)

Ant*_*dge 6

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

即使只在程序的全局区域中使用全局变量,也可能会很糟糕,但不要忘了那些只想获得乐趣并使某事起作用的人

如果这意味着在全局名称空间中使用几个变量(<10),则只能在程序的全局区域中使用它,那就这样吧。是的,是的,MVC,依赖项注入,外部代码,等等,等等,等等。但是,如果您已经将99.99%的代码包含在名称空间和类中,并且外部代码被沙盒化,则使用全局变量的世界将不会结束(我再说一遍,世界不会结束)。

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

计算机科学领域广阔。如果我们因为标记不好而使每个人都害怕做某事,那么他们就会失去真正理解标签背后原因的乐趣。

如果需要,请使用全局变量,然后查看是否可以在没有全局变量的情况下解决问题。当您深入了解问题的真实本质时,冲突,测试和调试的意义就更大了,而不仅仅是对问题的描述。


Mac*_*ity 5

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

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

function foo() {
     global $bob;
     $bob->doSomething();
}
Run Code Online (Sandbox Code Playgroud)

你的第一个问题很明显

哪儿$bob来的呢?

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

我们如何避免全局变量?

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

function foo(\Bar $bob) {
    $bob->doSomething();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 您链接到的那篇文章中存在一定程度的争论(例如,对已接受答案的评分最高的评论和第二高的答案都强烈反对已接受的答案),这使我倾向于认为应使用单例被逐案考虑,而不是立即被驳回。 (2认同)