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


86
function foo () {
    global $var;
    // rest of code
}

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

这是不好的做法吗?


19
全局变量是不良习惯的代名词
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ 2010年


2
尝试对它进行单元/验收测试,您会很快发现为什么全局变量是一个问题:当您多次执行填充操作时,它们会使您的代码不可靠。
Kzqai 2015年

Answers:


102

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

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

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

有关更多信息,请参见回调

关键是对象已被栓接到PHP上,并且在某些方面导致尴尬。

不要在将标准或不同语言的构造应用于PHP时过度担心自己。另一个常见的陷阱是,通过将对象模型置于一切之上,试图将PHP变成纯OOP语言。

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


8
应当指出,PHP 5.3确实使用lambda函数解决了其中一些问题,从而使您可以避免在全局范围内声明的函数用于回调。+1以获得可维护,易读的代码建议
Jonathan Fingland 2009年

您不能array ($obj, 'callbackMethod')在对的调用中使用表单的回调preg_replace_callback()吗?(我知道,我已经成为这个OOP陷阱的猎物...)
grossvogel 2010年

24
问题不是“是否应该使用全局变量?”。答案将是“必要时确保”。问题是他们是不好的做法。答案是“是的,有时候”。对于张贴者这个微小的项目,可能不会有不好的结果;但是,对于拥有许多团队成员且活动部件很多的大型项目,大量使用全局变量将使代码难以调试,几乎无法重构,甚至痛苦不堪。读。有时候您可以使用它们吗..确定-它们会吸吮吗..是的!
eddiemoya 2012年

@eddiemoya好,埃迪说。有很多人为不当行为辩护,例如使用全局变量。您应该像瘟疫一样避免它们。任何体面的软件工程学位都可以使您深入了解……讲师们不仅会告诉您它的地狱……他们从数十年的经验中知道。你应该在WordPress的使用等。如果可能的话来访问值,你需要的成员函数,例如get_query_var()

27

如果不仔细使用全局变量,将很难发现问题。假设您请求一个php脚本,并且收到一条警告,提示您正在尝试访问某个函数中不存在的数组的索引。

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

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

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


但是错误主要显示脚本在哪个文件/行中中断,所以..我在这里看不到问题
samayo

8
脚本损坏的地方!=犯错误的地方。
HonoredMule 2015年

16

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

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

最好的问候,唐


1
耶,我总是在要全局使用的变量前加下划线。
KRTac

9
@KRTac但$ _testVariable通常被理解为一个私有变量-这是定义私有变量而不是全局变量的非正式标准。
Aditya MP

6
一种常见的做法是使用ALL CAPS定义全局变量。例如:$DB = 'foo';
pixeline

7

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

即使只在程序的全局区域中使用全局变量,也可能是不好的选择,但不要忘了那些只想获得乐趣并使某些东西起作用的人

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

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

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

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


3

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

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

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

您的第一个问题很明显

哪里$bob来的?

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

我们如何避免Globals?

避免全局变量的最好方法是一种称为“依赖注入”的哲学。这是我们将所需的工具传递给函数或类的地方。

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

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


除了必须在各处传递$ bob之外,一种替代方法是使Bar类成为单例,在Bar本身中静态存储Bar的实例,并使用静态方法实例化/获取对象。然后,您可以$bob = Bar::instance();随时随地使用它。
酷航

1
请注意,单例被认为是反模式。依赖注入避免了这些陷阱
Machavity

2
您链接到的帖子中存在一定程度的争用(例如,对已接受答案的评价最高的评论和投票率第二高的答案都强烈不同意已接受的答案),这使我倾向于主张使用Singletons应该视情况而定,而不是立即被解雇。
酷航

0

如:

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';

不是错误,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 ... ';

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.