PHP全局函数

全局关键字的用途是什么?

有什么理由更喜欢一种方法?

  • 安全?
  • 性能?
  • 还要别的吗?

方法1:

function exempleConcat($str1, $str2) { return $str1.$str2; } 

方法2:

 function exempleConcat() { global $str1, $str2; return $str1.$str2; } 

什么时候使用global有意义的?

对我来说,这似乎是危险的 ,但可能只是缺乏知识。 我对文档感兴趣(例如代码示例,文档链接…)技术原因。

提前致谢!


赏金

这是一个很好的关于这个话题的一般性问题,我(@Gordon)提供了一个赏金来获得额外的答案。 无论你的答案是与我的一致,还是给出不同的观点并不重要。 既然global话题不时出现,我们就可以用一个好的“规范”的答案去联系。

全球化是邪恶的

global关键字以及从本地范围到全局范围(静态,单例,registry,常量)的其他所有内容都是如此。 你不想使用它们。 函数调用不应该依赖任何外部的东西,例如

 function fn() { global $foo; // never ever use that $a = SOME_CONSTANT // do not use that $b = Foo::SOME_CONSTANT; // do not use that unless self:: $c = $GLOBALS['foo']; // incl. any other superglobal ($_GET, …) $d = Foo::bar(); // any static call, incl. Singletons and Registries } 

所有这些将使您的代码依赖于外部。 这意味着,您必须知道您的应用程序所处的完整全局状态,然后才能可靠地调用这些状态。 没有这个环境,该function就不能存在。

使用超全球可能不是一个明显的缺陷,但如果您从命令行调用您的代码,您没有$_GET$_POST 。 如果你的代码依赖于这些input,你将自己限制在一个Web环境中。 只需将请求抽象为一个对象,然后使用它。

在硬编码类名(静态,常量)耦合的情况下,你的函数也不能存在没有可用的类。 当它是来自同一个命名空间的类时,这不是一个问题,但是当你从不同的命名空间开始混合时,你正在创build一个混乱的混乱。

以上所有情况严重阻碍了重复使用。 unit testing也是如此 。

而且,当你连接到全局范围时,你的函数签名是撒谎的

 function fn() 

是一个骗子,因为它声称我可以调用该函数而不传递任何东西。 只有当我看到我学习的function体,我必须把环境设置成一定的状态。

如果你的函数需要运行的参数,使其明确,并通过它们:

 function fn($arg1, $arg2) { // do sth with $arguments } 

明确expression了它所要求的内容。 它不依赖于处于特定状态的环境。 你不必这样做

 $arg1 = 'foo'; $arg2 = 'bar'; fn(); 

这是拉(全球关键字)与推(参数)的问题。 当你推入/注入依赖关系时,函数不再依赖外部。 当你做fn(1)你不必有一个variables在外面的某个地方。 但是当你在函数内部引用全局$one ,你会耦合到全局范围,并期望它有一个在某处定义的variables。 那function就不再独立了。

更糟的是,当你在函数内部改变全局variables时,你的代码很快就会变得难以理解,因为你的函数在各处都有副作用。

缺乏一个更好的例子,考虑

 function fn() { global $foo; echo $foo; // side effect: echo'ing $foo = 'bar'; // side effect: changing } 

然后你呢

 $foo = 'foo'; fn(); // prints foo fn(); // prints bar <-- WTF!! 

没有办法看到$foo从这三行改变了。 为什么用相同的参数调用相同的函数会突然改变它的输出或者改变全局状态的值? 一个函数应该为定义的inputY做X.总是。

这在使用OOP时会变得更加严重,因为OOP是关于封装和通过伸向全球的范围,你正在破坏封装。 在框架中看到的所有这些单例和registry都是代码气味,应该删除代码气味以支持dependency injection。 解耦你的代码。

更多资源:

  • http://c2.com/cgi/wiki?GlobalVariablesAreBad
  • 如何在PHP中testingregistry模式或单例?
  • 缺陷:脆弱的全球状态和单身
  • static视为有害的
  • 为什么Singletons在PHP中没有用处
  • SOLID(面向对象devise)

反对global的一个重要原因就是它的function依赖于另一个范围。 这将很快变得凌乱。

 $str1 = 'foo'; $str2 = 'bar'; $str3 = exampleConcat(); 

 $str = exampleConcat('foo', 'bar'); 

需要在调用范围中设置$str1$str2以使该函数正常工作意味着引入了不必要的依赖关系。 你不能在这个范围内重命名这些variables,也不能在函数中重命名这些variables,因此在你使用这个函数的其他范围中也是如此。 当你试图跟踪你的variables名时,很快就会陷入混乱。

即使包含全局的东西,例如$db资源, global也是一个糟糕的模式。 有一天,当你想重命名$db但不能,因为你的整个应用程序取决于名称。

限制和分离variables的范围对于编写任何中途复杂的应用程序是至关重要的。

全局是不可避免的。

这是一个古老的讨论,但我仍然想补充一些想法,因为我想念他们在上面提到的答案。 这些答案简化了一个全球性太多的问题,并提出了完全不能解决问题的解决scheme。 问题是:处理一个全局variables和关键字global的使用的正确方法是什么? 为此,我们首先必须检查和描述全球化是什么。

看看这个Zend的代码 – 请理解,我不build议Zend写得不好:

 class DecoratorPluginManager extends AbstractPluginManager { /** * Default set of decorators * * @var array */ protected $invokableClasses = array( 'htmlcloud' => 'Zend\Tag\Cloud\Decorator\HtmlCloud', 'htmltag' => 'Zend\Tag\Cloud\Decorator\HtmlTag', 'tag' => 'Zend\Tag\Cloud\Decorator\HtmlTag', ); 

这里有很多隐形的依赖关系。 那些常量实际上是类。 你也可以在这个框架的一些页面中看到require_once。 Require_once是一个全局依赖项,因此创build了外部依赖项。 这对框架来说是不可避免的。 你怎么能创build一个像DecoratorPluginManager类没有很多外部代码,它依赖于? 它不能没有很多的额外function。 使用Zend框架,你有没有改变一个接口的实现? 一个接口实际上是一个全球性的。

另一个全球使用的应用程序是Drupal。 他们非常关心正确的devise,但是就像任何大的框架一样,他们有很多的外部依赖。 看看这个页面中的全局variables:

 /** * @file * Initiates a browser-based installation of Drupal. */ /** * Root directory of Drupal installation. */ define('DRUPAL_ROOT', getcwd()); /** * Global flag to indicate that site is in installation mode. */ define('MAINTENANCE_MODE', 'install'); // Exit early if running an incompatible PHP version to avoid fatal errors. if (version_compare(PHP_VERSION, '5.2.4') < 0) { print 'Your PHP installation is too old. Drupal requires at least PHP 5.2.4. See the <a href="http://drupal.org/requirements">system requirements</a> page for more information.'; exit; } // Start the installer. require_once DRUPAL_ROOT . '/includes/install.core.inc'; install_drupal(); 

曾经写过redirect到login页面? 这正在改变全球价值。 (然后你是不是说'WTF',我认为这是对你的应用程序的错误文档的一个很好的反应。)全局问题并不是它们是全局的,你需要它们来获得一个有意义的应用程序。 问题是整个应用程序的复杂性,可以使它成为一个噩梦处理。 会话是全局variables,$ _POST是全局variables,DRUPAL_ROOT是全局variables,includes / install.core.inc是一个不可修改的全局variables。 为了让这个function发挥作用,任何function都需要外部的大世界。

戈登的回答是不正确的,因为他高估了一个函数的独立性,并且称一个函数是一个骗子,这样会使情况过于简单化。 函数不会说谎,当你看他的例子时,函数的devise不当 – 他的例子是一个错误。 (顺便说一句,我同意这个结论,应该解耦代码。)欺骗的答案并不是一个真正的定义。 函数总是在更广的范围内运作,他的例子太简单了。 我们都会同意他这个函数是完全无用的,因为它返回一个常量。 该function无论如何是不好的devise。 如果你想表明这种做法是不好的,请附上一个相关的例子。 在整个应用程序中重命名variables对于一个好的IDE(或者一个工具)来说没有什么大不了的。 问题是关于variables的范围,而不是范围与函数的区别。 函数在这个过程中有一个适当的时间来执行它的作用(这就是为什么它被创build的原因),并且在适当的时候可能会影响整个应用程序的运行,因此也要处理全局variables。 xzyfer的答案是一个没有论证的陈述。 如果您有程序function或OOPdevise,全局应用程序就像在应用程序中一样。 接下来两种改变全球价值的方式基本上是一样的:

 function xzy($var){ global $z; $z = $var; } function setZ($var){ $this->z = $var; } 

在这两种情况下,都是在特定函数中更改的$ z的值。 在这两种编程方式中,你可以在代码中的其他地方做出这些改变。 你可以说,使用全局你可以调用$ z任何地方,并在那里改变。 是的你可以。 但是,你会吗? 而当在inapt的地方完成,那么它不应该被称为bug?

Bob Fanger评论xzyfer。

任何人都应该使用任何东西,特别是关键字'全球'? 不,但就像任何types的devise一样,试着分析它的依赖和依赖。 试着找出它何时改变以及如何改变。 只有在每个请求/响应可以改变的variables的情况下才能改变全局值。 也就是说,只对那些属于过程functionstream程的variables,而不涉及其技术实现。 将URLredirect到login页面属于进程的functionstream程,实现类用于技术实现的接口。 您可以在应用程序的不同版本中更改后者,但不应随每个请求/响应而更改。

为了进一步了解什么时候使用全局variables和全局关键字的问题,什么时候不会引入Wim de Bie在撰写博客时提到的下一句话:“个人是,私人没有”。 当一个函数为了自己的function而改变一个全局variables的值的时候,我会调用这个私有的全局variables和一个bug。 但是,当全局variables的变化是为了整个应用程序的正确处理,如用户redirect到login页面,那么在我看来可能是好的devise,而不是定义不好,当然不是反模式。

回想Gordon,deceze和xzyfer的回答:他们都有“私人的”(bug)作为例子。 这就是为什么他们反对使用全局variables。 我也会。 但是,他们不是像我在这个答案中多次做过的那样,是“私人的,私人的”的例子。

简单地说,在现代PHP代码恕我直言,很less有一个global ,从来没有好的理由。 特别是如果你使用PHP 5的话。特别是如果你开发面向对象的代码。

全局性对代码的可维护性,可读性和可testing性有负面影响。 global许多用途都可以,并且应该用dependency injection来取代,或者简单地把全局对象作为parameter passing。

 function getCustomer($db, $id) { $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id)); return $row; } 

不要犹豫在PHP中使用全局关键字内的函数。 特别是不要采取异乎寻常地讲道/大喊全球性是“邪恶”和什么的人。

首先,因为你所使用的东西完全取决于情况和问题,在编码方面没有任何解决方法/办法。 完全撇开不确定的,主观的,像“邪恶”的宗教形容词的谬误。

例如:

WordPress及其生态系统在其function中使用全局关键字。 是代码OOP或不OOP。

而截至目前,WordPress的基本上是互联网的18.9%,它运行从路透社到索尼,到纽约时报,到CNN等无数巨头的巨大巨人/应用程序。

它做得很好。

在函数内部使用全局关键字可以将Wordpress从巨大的臃肿中释放出来,因为它的庞大的生态系统将会发生。 想象一下,每个函数都要求/传递从另一个插件,核心和返回所需的任何variables。 增加了插件的相互依赖关系,最终会导致variables的噩梦,或者是作为variables传递的数组的恶梦。 跟踪地狱,地狱debugging,地狱发展。 由于代码膨胀和variables膨胀造成的巨大内存占用。 更难写。

可能有人会提出和批评Wordpress,其生态系统,他们的做法和那些地方发生的事情。

毫无意义,因为这个生态系统几乎是整个互联网的20%。 显然,它工作,它的工作和更多。 这意味着对于全局关键字它是相同的。

另一个很好的例子是“内乱是邪恶的”原教旨主义。 十年前,使用iframe是异端邪说。 有成千上万的人在互联网上传播他们。 然后是脸谱,然后来社会,现在iframes无处不在,从“喜欢”框authentication,瞧 – 每个人都闭嘴。 还有那些还没有闭嘴 – 无论是正确的还是错误的。 但是你知道吗,尽pipe有这样的观点,生活还是会继续下去的,甚至十年前那些反对iframe的人现在也不得不用它们把各种社交应用程序整合到他们自己的应用程序中,而不用说一句话。

……

编码原教旨主义是非常非常糟糕的。 我们中的一小部分人可以在一个稳固的整体公司中享受舒适的工作,这个公司有足够的影响力来承受信息技术的不断变化以及它在竞争,时间,预算和其他方面带来的压力,因此可以练习原教旨主义,严格遵守“罪恶”或“货物”。 舒适的位置让人联想到这些老年人,即使占领者还年轻。

然而,对于大多数人来说,这个世界是一个不断变化的世界,他们需要开放和实际。 没有原教旨主义的地方,在信息技术的前沿战线上放下像“邪恶”这样荒谬的关键词。

只要使用最合适的手段来解决问题,并考虑近,中,远期的问题。 不要回避使用任何特征或方法,因为它在任何给定的编码器子集中都具有猖獗的意识形态上的敌意。

他们不会做你的工作。 你会。 根据你的情况行事。

我想每个人都已经详细阐述了全局的消极方面。 所以我会加上积极的和正确使用全局的说明:

  1. 全局的主要目的是在函数之间共享信息。 当没有什么像类的时候,php代码由一堆函数组成。 有时你需要在function之间共享信息。 通常情况下,全球被用来做到这一点,因为全球化导致数据被破坏的风险。

    现在,在快乐幸运之前,简单的开始一个关于dependency injection的评论我想问你,像例子get_post(1)这样的函数的用户将如何知道函数的所有依赖关系。 还要考虑依赖关系可能不同于
    版本到版本和服务器到服务器。 dependency injection的主要问题是依赖关系必须事先知道。 在这种情况下,这是不可能的,或者不必要的全局variables是实现这一目标的唯一方法。

    由于该类的创build,现在常见的function可以很容易地分组在一个类和共享数据。 通过像Mediators这样的实现,即使不相关的对象也可以共享信息。 这不再是必要的。

  2. 全局的另一个用途是用于configuration目的。 大部分是在任何自动加载器加载之前的脚本开始之前,进行数据库连接等。

    在加载资源的过程中,可以使用全局variables来configuration数据(即在哪个库文件所在的位置使用哪个数据库,服务器的URL等)。 这样做的最好方法是使用define()函数,因为这些值不会经常更改,并且可以轻松放置在configuration文件中。

  3. 全局的最终用途是保存公共数据(即CRLF,IMAGE_DIR,IMAGE_DIR_URL),可读的状态标志(即ITERATOR_IS_RECURSIVE)。 这里使用全局variables来存储应用程序使用的信息,从而可以更改它们,并使这些变化显示在应用程序范围内。

  4. 在php4中,当一个对象的每个实例占用内存时,单例模式在php中变得stream行起来。 单身人士通过仅允许创build一个对象的一个​​实例来帮助保存公羊。 在引用之前甚至dependency injection是一个坏主意。

    来自PHP 5.4+的对象的新PHP实现将解决大部分这些问题,您可以安全地传递对象,而且几乎不再受到任何惩罚。 这不再是必要的。

    单例的另一个用途是特殊的实例,一次只有一个对象实例存在,这个实例可能在脚本执行之前/之后存在,并且这个对象在不同的​​脚本/服务器/语言等之间共享。在这里,单例模式解决了解决scheme相当好。

所以最后如果你在位置1,2或3,那么使用全局是合理的。 但是在其他情况下,应该使用方法1。

随意更新任何其他应用全局的实例。

使用global关键字进行concat函数是没有意义的。

它用于访问全局variables,如数据库对象。

例:

 function getCustomer($id) { global $db; $row = $db->fetchRow('SELECT * FROM customer WHERE id = '.$db->quote($id)); return $row; } 

它可以用作Singleton模式的变体