函数中的PHP全局

9avjhtql  于 2022-12-21  发布在  PHP
关注(0)|答案(7)|浏览(87)

global关键字的作用是什么?
有什么理由喜欢一种方法而不喜欢另一种方法吗?

  • 保安?
  • 表演?
  • 还有别的事吗?

方法一:

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

方法二:

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

什么时候使用global有意义?
对我来说,这看起来很危险...但可能只是缺乏知识。我对文档化(例如,代码示例、文档链接...)技术原因感兴趣。

赏金

这是一个很好的关于这个主题的一般性问题,我(@Gordon)提供了一个奖金来获得更多的答案。无论你的答案是与我的一致还是给出了不同的观点都无关紧要。由于global主题不时出现,我们可以使用一个好的"规范"答案来链接。

hwazgwia

hwazgwia1#

地球人是邪恶的

对于global关键字以及从局部作用域到全局作用域的所有其他东西(静态、单例、注册表、常量)都是如此。您不希望使用它们。函数调用不应该依赖于外部的任何东西,例如:

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
}

所有这些都将使你的代码依赖于外部。这意味着,你必须知道你的应用程序所处的完整的全局状态,然后你才能可靠地调用其中的任何一个。没有那个环境,函数就不可能存在。
使用超级全局变量可能不是一个明显的缺陷,但是如果你从命令行调用你的代码,你就没有$_GET$_POST。如果你的代码依赖于来自这些变量的输入,你就把自己限制在了一个web环境中。只要把请求抽象成一个对象并使用它就可以了。
在耦合硬编码类名(静态、常量)的情况下,如果该类不可用,则函数也无法存在。当类来自相同的名称空间时,这不是什么问题,但当您开始混合来自不同名称空间的类时,就会造成混乱。
上述所有问题严重阻碍了重用。So is unit-testing
此外,当您耦合到全局作用域时,您的函数签名也在说谎

function fn()

是一个骗子,因为它声称我可以不传递任何东西就调用那个函数。只有当我查看函数体时,我才知道我必须将环境设置为某种状态。
如果您的函数需要参数才能运行,请将其显式化并传入:

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

从签名中清楚地传达它需要被调用的内容。它不依赖于处于特定状态的环境。您不必

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

这是一个拉在,(全局关键字)与推入(arguments)。当你推入/注入依赖项时,函数不再依赖于外部。当你执行fn(1)时,你不必在外部的某个地方有一个变量保持1。但是当你在函数内部拉入全局$one时,与全局作用域相结合,并期望它在某处定义一个变量,这样函数就不再是独立的了。
更糟糕的是,当您在函数内部更改全局变量时,您的代码很快就会变得完全不可理解,因为您的函数到处都有副作用。
如果没有更好的示例,请考虑

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发生了变化。为什么用相同的参数调用同一个函数会突然改变它的输出或全局状态中的一个值呢?函数应该为定义的输入Y执行X。总是这样。
当使用OOP的时候,这会变得更加严重,因为OOP是关于封装的,而通过扩展到全局范围,你就破坏了封装。你在框架中看到的所有这些单例和注册表都是代码气味,应该被移除,以利于依赖注入。
更多资源:

laik7k3q

laik7k3q2#

全局错误是不可避免的。

这是一个老的讨论,但我仍然想补充一些想法,因为我错过了上述答案。这些答案简化了什么是一个全球太多,并提出了解决方案,而不是在所有的问题的解决方案。问题是:处理全局变量和使用关键字global的正确方法是什么?2为此,我们首先要检查和描述什么是全局变量。
看一看Zend的这段代码--请理解,我并不是说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是一个全局依赖,因此产生了外部依赖。2这对于一个框架来说是不可避免的。3你怎么能创建一个像DecoratorPluginManager这样的类而没有它所依赖的大量外部代码呢?4如果没有大量的额外代码,它就不能正常工作。在使用Zend框架时,您是否曾经更改过接口的实现?接口实际上是全局的。
另一个全球通用的应用是Drupal,他们非常关注适当的设计,但是就像任何大框架一样,他们有很多外部依赖项,看看这个页面中的全局应用:

/**
 * @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();

曾经写过一个重定向到登录页面吗?那是改变一个全局值。(然后你不是说“WTF”吗,我认为这是对你的应用程序的糟糕文档的一个很好的React。)全局变量的问题不在于它们是全局变量,你需要它们才能有一个有意义的应用程序。问题是整个应用程序的复杂性,这会使它成为一场噩梦。会话是全局的,$_POST是全局的,DRUPAL_ROOT是全局的,includes/install.core.inc '是不可修改的全局的。为了让函数完成它的工作,在任何函数之外都有一个广阔的世界。
Gordon的回答是不正确的,因为他高估了函数的独立性,称函数为骗子是把情况过于简单化了。函数不说谎,当你看他的例子时,函数设计不当-他的例子是一个bug。(顺便说一句,我同意这个结论,即应该解耦代码。)deceze的答案实际上并不是对这种情况的正确定义。函数总是在更大的范围内起作用,他的例子太简单了。我们都会同意他的观点,那个函数完全没用,因为它返回一个常量。那个函数无论如何都是糟糕的设计。如果你想证明实践是糟糕的,请提供一个相关的例子。2在一个应用程序中重命名变量没有什么大不了的(或工具)。问题是关于变量的范围,而不是功能范围的差异。功能在流程中有适当的时间来执行其角色(这就是首先创建它的原因)并且在适当的时候它可以影响应用程序作为整体的功能,因此也在处理全局变量。xzyfer的答案是一个没有论证的语句。如果你有过程函数或OOP设计,全局变量就会出现在应用程序中。下面两种更改全局变量值的方法基本相同:

function xzy($var){
 global $z;
 $z = $var;
}

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

在这两种情况下,$z的值都是在一个特定的函数中改变的。在这两种编程方式中,你能在代码中的其他地方做这些改变吗?你可以说,使用global你可以在任何地方调用$z并在那里改变。是的,你可以。但是你会吗?当在不合适的地方做这些改变时,它是否应该被称为bug?
Bob Fanger对xzyfer的评论。
那么,是否应该使用任何东西,尤其是关键字“global”?不,但就像任何类型的设计一样,尝试分析它依赖于什么,什么依赖于它。尝试找出它何时改变,以及它是如何改变的。改变全局值应该只发生在那些可以随着每个请求/响应而改变的变量上。也就是说,只发生在那些属于流程的功能流的变量上。而不是其技术实现。将URL重定向到登录页面属于流程的功能流,即用于技术实现接口的实现类。您可以在应用程序的不同版本中更改后者,但不应在每个请求/响应中更改它们。

为了进一步理解什么时候使用全局变量和关键字global会有问题,什么时候不会,我将介绍下一句话,这句话来自Wim de Bie在写博客时说的话:“个人的是,私人的不是”。当一个函数为了它自己的功能而改变一个全局变量的值时,那么我将称之为对全局变量的私人使用和bug。但是当全局变量的改变是为了整个应用程序的正确处理时,比如将用户重定向到登录页面,那么在我看来这可能是一个好的设计吗?从定义上讲并不坏,当然也不是反模式。
回顾戈登、德奇斯和谢费尔的回答:他们都有“私人的是”(和bug)作为例子,所以他们反对使用global,我也反对,但他们没有“私人的是,私人的否”--我在这答复中已经举过几次例子。

c86crjj0

c86crjj03#

反对global的一个主要原因是它意味着函数依赖于另一个作用域,这将很快变得混乱。

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

对比

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

要求在函数的调用作用域中设置$str1$str2意味着引入了不必要的依赖关系。如果不在函数中重命名这些变量,就无法在此作用域中重命名它们,因此在使用此函数的所有其他作用域中也是如此。当您试图跟踪变量名时,这很快就会陷入混乱。
global是一个糟糕的模式,即使是包含全局的东西,比如$db资源,总有一天你会想重命名$db,但是不能,因为你的整个应用程序都依赖于这个名字。
限制和分离变量的作用域对于编写任何半复杂的应用程序来说都是必不可少的

de90aj5v

de90aj5v4#

简单地说,在现代PHP代码中,很少有global的理由,也从来没有一个好的理由。特别是如果你使用PHP 5。特别是如果你正在开发面向对象的代码。
全局变量会对代码的可维护性、可读性和可测试性产生负面影响。global的许多用法可以也应该被依赖注入所取代,或者只是将全局对象作为参数传递。

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

tpgth1q75#

在PHP函数中使用global关键字时不要犹豫,尤其是不要接受那些奇怪地鼓吹/叫喊global是如何"邪恶"的人。
首先,因为你使用什么完全取决于情况和问题,在编码中没有一个解决方案/方法可以做任何事情。完全抛开像"邪恶"这样无法定义的,主观的,宗教的形容词进入等式的谬论。
例证:
WordPress及其生态系统在其功能中使用全局关键字。是代码OOP或不OOP。
到目前为止,WordPress基本上占互联网的18.9%,它运行着从路透社到索尼,从纽约时报到CNN等无数巨头的大型网站/应用程序。
而且它做得很好。
在函数中使用global关键字可以让WordPress从巨大的膨胀中解放出来,这在它庞大的生态系统中是可能发生的。想象一下,每个函数都在请求/传递另一个插件、核心和返回所需的任何变量。加上插件的相互依赖性,这将最终导致变量的噩梦,或者作为变量传递的数组的噩梦。跟踪的地狱,调试的地狱,一个地狱般的开发。由于代码膨胀和变量膨胀导致的巨大内存占用。也更难写。
可能会有人站出来批评WordPress,它的生态系统,他们的做法以及这些地方发生的事情。
毫无意义,因为这个生态系统几乎占整个互联网的20%。显然,它确实起作用,它做它的工作和更多。这意味着它的全球关键字是一样的。
另一个很好的例子是"iframe是邪恶的"原教旨主义。十年前,使用iframe是一种异端邪说。在互联网上有成千上万的人反对使用iframe。然后是Facebook,然后是社交网站,现在iframe无处不在,从"喜欢"框到身份验证,瞧--所有人都闭嘴了。有些人仍然没有闭嘴--不管是对是错。但你知道吗,尽管有这样的观点,但生活还在继续,即使是十年前反对iframe的人,现在也不得不使用iframe将各种社交应用程序集成到他们组织自己的应用程序中,而不发一语。
......
编码器原教旨主义是非常非常糟糕的东西。我们中的一小部分人可能会在一个坚实的整体公司中享受舒适的工作,这个公司有足够的影响力来承受信息技术的不断变化以及它在竞争,时间,预算和其他考虑方面带来的压力,因此可以实行原教旨主义和严格遵守感知的"恶"或"善"。舒适的立场让人想起老年这些是,即使占领者很年轻。
然而,对于大多数人来说,信息技术世界是一个不断变化的世界,他们需要开放的思想和务实的态度。原教旨主义没有立足之地,抛开像"邪恶"这样令人发指的关键词在信息技术的前线战壕。
只要使用任何对手头的问题最有意义的东西,并适当考虑到近期、中期和长期的未来。不要回避使用任何特性或方法,因为它有一个猖獗的意识形态仇恨对它,在任何给定的编码器子集。
他们不会做你的工作。你会的。根据你的情况采取行动。

ipakzgxi

ipakzgxi6#

使用global关键字创建concat函数是没有意义的。
它用于访问全局变量,如数据库对象。
示例:

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

它可用作Singleton pattern的变体

e4yzc0pl

e4yzc0pl7#

我想每个人都已经对全局变量的消极方面进行了相当多的阐述。因此,我将添加积极方面以及正确使用全局变量的说明:
1.全局变量的主要用途是在函数之间共享信息。当类还不存在的时候,php代码由一堆函数组成。有时候你需要在函数之间共享信息。通常全局变量是用来做这件事的,把它们变成全局变量会有数据损坏的风险。
现在,在一些快乐的傻瓜开始评论依赖注入之前,我想问你,像get_post(1)这样的函数的用户是如何知道函数的所有依赖关系的。
版本到版本和服务器到服务器。依赖注入的主要问题是必须事先知道依赖关系。在不可能做到这一点或不需要的全局变量是实现这一目标的唯一途径的情况下。
由于类的创建,现在公共函数可以很容易地分组到一个类中并共享数据。通过像Mediators这样的实现,甚至不相关的对象也可以共享信息。这不再是必要的。
1.全局变量的另一个用途是用于配置目的,主要是在自动加载器加载之前,数据库连接建立之前,在脚本的开头使用。
在加载资源的过程中,全局变量可以用来配置数据(例如,使用哪个数据库、库文件的位置、服务器的url等)。最好的方法是使用define()函数,因为这些值不会经常更改,可以很容易地放在配置文件中。
1.全局变量的最后一个用途是保存公共数据(即CRLF、IMAGE_DIR、IMAGE_DIR_URL)、人类可读的状态标志(即ITERATOR_IS_RECURSIVE)。这里全局变量用于存储应用程序范围内使用的信息,允许更改这些信息并使这些更改在应用程序范围内显示。
1.单例模式在php 4期间流行起来,因为对象的每个示例都会占用内存,单例模式只允许创建一个对象示例,从而有助于保存内存,在引用出现之前,甚至依赖注入都是个坏主意。
PHP 5.4+中新的php对象实现解决了大部分问题,你可以安全地传递对象,几乎没有任何损失。这不再是必要的。
单例模式的另一个用途是一个对象的一个示例必须同时存在的特殊示例,该示例可能在脚本执行之前/之后存在,并且该对象在不同的脚本/服务器/语言之间共享,等等。
因此,如果你处于立场1、2或3,那么使用全局变量是合理的。但是在其他情况下,应该使用方法1。
您可以随时更新任何其他应该使用全局变量的示例。

相关问题