PHP全局函数

Pas*_*Qyy 98 php language-design

global关键字的效用是什么?

是否有任何理由更喜欢一种方法?

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

方法1:

function exempleConcat($str1, $str2)
{
  return $str1.$str2;
}
Run Code Online (Sandbox Code Playgroud)

方法2:

function exempleConcat()
{
  global $str1, $str2;
  return $str1.$str2;
}
Run Code Online (Sandbox Code Playgroud)

什么时候使用有意义global

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

提前致谢!


赏金

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

Gor*_*don 156

全球是邪恶的

对于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
}
Run Code Online (Sandbox Code Playgroud)

所有这些都将使您的代码依赖于外部.这意味着,在您可以可靠地调用其中任何一个之前,您必须知道应用程序所处的完整全局状态.没有该环境,该功能就不存在.

使用超全局可能不是一个明显的缺陷,但如果你从命令行调用你的代码,你没有$_GET$_POST.如果您的代码依赖于这些代码的输入,那么您将自己局限于Web环境.只需将请求抽象为对象并使用它.

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

重复使用受到以上所有因素的严重阻碍.单元测试也是如此.

此外,当您耦合到全局范围时,您的功能签名就在撒谎

function fn()
Run Code Online (Sandbox Code Playgroud)

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

如果您的函数需要运行参数,请将它们显式化并传递给它们:

function fn($arg1, $arg2)
{
    // do sth with $arguments
}
Run Code Online (Sandbox Code Playgroud)

清楚地从签名中传达了它需要被呼叫的东西.它不依赖于环境处于特定状态.你不必这样做

$arg1 = 'foo';
$arg2 = 'bar';
fn();
Run Code Online (Sandbox Code Playgroud)

这是一个拉入(全局关键字)与推入(参数)的问题.当您推入/注入依赖项时,该函数不再依赖于外部.当你这样做时,你不必fn(1)在外面某处拿一个变量.但是当你在$one函数内部引入全局时,你会耦合到全局范围并期望它具有某个定义的变量.该功能不再独立.

更糟糕的是,当你在函数中改变全局变量时,你的代码将很快变得难以理解,因为你的函数在整个地方都有副作用.

缺乏更好的例子,请考虑

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

然后你做

$foo = 'foo';
fn(); // prints foo
fn(); // prints bar <-- WTF!!
Run Code Online (Sandbox Code Playgroud)

没有办法看到$foo从这三行改变了.为什么用相同的参数调用相同的函数会突然改变它的输出或改变全局状态中的值?函数应为定义的输入Y执行X.始终.

在使用OOP时,这会变得更加严重,因为OOP是关于封装的,并且通过扩展到全局范围,您正在打破封装.您在框架中看到的所有这些单例和注册表都是代码味道,应该删除它们以支持依赖注入.解密你的代码.

更多资源:

  • 为什么PHP实现这样的东西?有没有实用工具?我总是对PHP中的危险实现感到惊讶,很多人每次都会使用...我很难相信没有逻辑原因! (10认同)
  • 我希望你能让*Globals变得邪恶*更大. (5认同)
  • 哇,最后有人解释为什么全局变量是邪恶的...我总是听到它们是,我看到了一些非常具体的例子,但是这对于一般原因来说真的是一个很好的全面解释.+1 (3认同)
  • 你是对的,除了常数.它们不代表应用程序的"状态",可以在函数内引用它们.如果从内部使用常量,该函数不会"撒谎".我同意这意味着程序员在某一点上已经了解了外部知识,但对于常数来说,这是一个非常可接受的权衡.而且,说真的,这不是太大的交易. (2认同)

dec*_*eze 35

反对的一个重要原因global是它意味着该功能依赖于另一个范围.这将很快变得混乱.

$str1 = 'foo';
$str2 = 'bar';
$str3 = exampleConcat();
Run Code Online (Sandbox Code Playgroud)

$str = exampleConcat('foo', 'bar');
Run Code Online (Sandbox Code Playgroud)

要求$str1$str2在函数调用范围内设置工作意味着您引入了不必要的依赖关系.您不能在此范围内重命名这些变量,也不能在函数中重命名它们,因此也可以在您使用此函数的所有其他范围中重命名这些变量.当你试图跟踪你的变量名时,很快就会陷入混乱.

global即使包括$db资源等全球性事物,也是一种糟糕的模式.有来的一天,当你想重命名$db,但不能,因为你的整个应用程序依赖于名字.

限制和分离变量范围对于编写任何中途复杂应用程序至关重要.

  • @kcd因为有一天你意识到依赖注入是多么伟大并希望重组你的应用程序?因为有一天你需要将你的东西与其他东西整合起来**使用全局`$ db`变量?因为有一天你会发现单元测试并且需要一次管理多个数据库连接吗?原因很多很多. (3认同)

Loe*_*man 34

全球不可避免.

这是一个古老的讨论,但我仍然想补充一些想法,因为我在上面提到的答案中想念它们.这些答案简化了全球化的过多,并提出了解决问题的解决方案.问题是:处理全局变量和使用关键字global的正确方法是什么?为此,我们首先要检查和描述全局是什么.

看看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',
   );
Run Code Online (Sandbox Code Playgroud)

这里有很多不可见的依赖.那些常量实际上是类.您还可以在此框架的某些页面中查看require_once.Require_once是全局依赖项,因此创建外部依赖项.这对框架来说是不可避免的.如何在没有很多外部代码的情况下创建类似DecoratorPluginManager的类?没有很多额外功能它无法运行.使用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();
Run Code Online (Sandbox Code Playgroud)

曾经写过重定向到登录页面?这正在改变全球价值.(然后你不是说'WTF',我认为这是对你的应用程序的错误文档的良好反应.)全局变量的问题不是它们是全局变量,你需要它们才能拥有一个有意义的应用程序.问题是整个应用程序的复杂性,这可能使它成为一个噩梦.会话是全局的,$ _POST是全局的,DRUPAL_ROOT是全局的,includes/install.core.inc'是不可修改的全局.为了让这个功能发挥作用,在任何功能之外都有大的世界.

戈登的答案是不正确的,因为他高估了一个功能的独立性,并且称一个骗子的功能过于简单化了.函数不是谎言,当你看一下他的例子时,函数的设计是不正确的 - 他的例子是一个bug.(顺便说一句,我同意这个结论,即应该解码代码.)deceze的答案并不是对情况的正确定义.函数总是在更广泛的范围内运行,他的例子过于简单化.我们都同意他的观点,即该函数完全没用,因为它返回一个常量.无论如何,这个功能设计不好.如果你想证明这种做法不好,请附上一个相关的例子.在整个应用程序中重命名变量并不是一个好的IDE(或工具).问题是变量的范围,而不是函数范围的差异.有一个适当的时间让函数在进程中执行它的角色(这就是为什么它首先被创建),并且在适当的时候它可能影响整个应用程序的功能,因此也在处理全局变量.xzyfer的答案是一个没有论证的陈述.如果您有程序功能或OOP设计,Globals就像在应用程序中一样.接下来两种改变全球价值的方式基本相同:

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

function setZ($var){
 $this->z = $var;
}
Run Code Online (Sandbox Code Playgroud)

在两个实例中,$ z的值在特定函数内发生了变化.在这两种编程方式中,您可以在代码中的许多其他位置进行更改.你可以说使用全局你可以在任何地方调用$ z并在那里进行更改.是的你可以.但是你呢?当在inapt地方完成时,它是否应该被称为bug?

Bob Fanger对xzyfer发表评论.

那么有人应该使用任何东西,尤其是关键字"全球"吗?不,但就像任何类型的设计一样,尝试分析它所依赖的内容以及依赖于它的内容.试着找出它何时发生变化以及变化的方式.只有那些随每个请求/响应而变化的变量才会发生变化的全局值.也就是说,仅对那些属于流程功能流的变量,而不是其技术实现.将URL重定向到登录页面属于进程的功能流,该实现类用于技术实现的接口.您可以在应用程序的不同版本中更改后者,但不应更改每个请求/响应.

为了进一步了解何时使用全局变量和关键字global是一个问题,何时不能解释下一句,来自Wim de Bie写博客的时候:'个人是的,私人没有'.当一个函数为了自己的功能而改变全局变量的值时,我会调用私有变量和bug的私有用法.但是当全局变量的更改是为了整个应用程序的正确处理时,比如用户重定向到登录页面,那么在我看来,这可能是好的设计,不是定义不好,当然也不是反模式.

回顾一下Gordon,deceze和xzyfer的答案:他们都有'私有'(和错误)作为例子.这就是为什么他们反对使用全局变量.我也会这样做.然而,他们并没有带来'个人是的,私人没有' - 就像我在这个答案中多次做过的那样.


xzy*_*fer 14

简单地说,global在现代PHP代码恕我直言中很少有理由而且从来都不是一个好理由.特别是如果你正在使用PHP 5.特别是如果你正在开发面向对象的代码.

Globals会对代码的可维护性,可读性和可测试性产生负面影响.globalcan的许多用法应该用Dependency Injection替换,或者简单地将全局对象作为参数传递.

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


uni*_*100 8

不要犹豫在PHP中使用函数内的全局关键字.特别是不要把那些异乎寻常地讲道/大吼大叫的全球人如何"邪恶"等等.

首先,因为你使用的完全取决于情况和问题,并且没有一种解决方案/方法可以在编码中做任何事情.完全抛弃了不可定义的,主观的,宗教的形容词如"邪恶"的谬误.

例证:

Wordpress及其生态系统在其功能中使用全局关键字.是代码OOP或不是OOP.

截至目前,Wordpress基本上占互联网总量的18.9%,并且运行着无数巨头的巨型megasites /应用程序,从路透社到索尼,再到纽约,再到CNN.

它做得很好.

在函数内部使用全局关键字可以将Wordpress从MASSIVE膨胀中解放出来,因为它具有庞大的生态系统.想象一下,每个函数都在询问/传递从另一个插件,核心和返回所需的任何变量.添加了插件相互依赖性,最终会成为变量的噩梦,或者是作为变量传递的数组的噩梦.一个地狱跟踪,一个地狱调试,一个地狱发展.由于代码膨胀和可变膨胀,内存占用空间巨大.更难写.

可能会有人出现并批评Wordpress,其生态系统,他们的实践以及这些部分中发生的事情.

毫无意义,因为这个生态系统几乎占整个互联网的20%.显然,它可以工作,它可以完成它的工作.这意味着它与全局关键字相同.

另一个很好的例子是"iframes是邪恶的"原教旨主义.十年前,使用iframe是异端邪说.互联网上有成千上万的人在讲道.然后来到Facebook,然后是社交,现在iframe到处都是从"喜欢"的盒子到身份验证,瞧 - 每个人都闭嘴.有些人仍然没有闭嘴 - 正当或错误.但是你知道什么,尽管有这样的意见,生活仍在继续,甚至那些十年前在iframe上宣传的人现在不得不用它们将各种社交应用程序整合到他们组织自己的应用程序中,而不说一句话.

......

编码器原教旨主义非常非常糟糕.我们中的一小部分人可以在一个坚实的单片公司中获得舒适的工作,该公司具有足够的影响力以承受信息技术的不断变化及其在竞争,时间,预算和其他考虑方面带来的压力,因此可以实践原教旨主义和严格遵守"邪恶"或"商品".即使占用者年轻,这些都是令人联想到旧时代的舒适位置.

然而,对于大多数人来说,世界是一个不断变化的世界,他们需要开放和实际.没有原教旨主义的地方,在信息技术的前线战壕中留下诸如"邪恶"等令人愤慨的关键词.

只需使用对AT HAND问题最有意义的东西,并考虑近期,中期和长期的未来.不要回避使用任何特征或方法,因为它在任何给定的编码器子集中具有猖獗的意识形态仇恨.

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

  • 反原教旨主义+1等等,但只是说"很多人使用它/它工作/等"只是一个"论证广告",一个基本的诡辩.大多数人认为或做一件事的事实并不能证明他们是对的!在人群中,如果出现危险,大多数人会做愚蠢的事情,有些人会被别人踩死.是不是因为他们认为必须绝对推开一扇门,只有当它被拉开以逃避火灾时,才能把脚放在这个五岁小女孩的脸上?我不这么认为...... (3认同)

Bob*_*ger 6

使用global关键字创建concat函数没有任何意义。

它用于访问全局变量,例如数据库对象。

例:

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

可以用作Singleton模式的变体


mAs*_*pEE 6

我想每个人都对全局变量的消极方面进行了很多阐述.所以我将添加正面以及正确使用全局变量的说明:

  1. 全局变量的主要目的是在函数之间共享信息.当没有类似于类时,PHP代码由一堆函数组成.有时您需要在功能之间共享信息.通常情况下,全局用于通过使数据全局化而导致数据损坏的风险.

    现在,在一些快乐之前,幸运的是,simpleton会开始关于依赖注入的评论,我想问你一个像example get_post(1)这样的函数的用户如何知道函数的所有依赖关系.还要考虑依赖关系可能因
    版本和服务器而异.依赖注入的主要问题是必须事先知道依赖性.在这种情况下,这是不可能的,或者不需要的全局变量是实现这一目标的唯一方法.

    由于类的创建,现在可以很容易地将常用函数分组到一个类中并共享数据.通过像Mediators这样的实现,甚至不相关的对象也可以共享信息.这不再是必要的.

  2. 全局变量的另一个用途是用于配置目的.主要是在加载任何自动加载器之前的脚本开头,进行数据库连接等.

    在加载资源期间,可以使用全局变量来配置数据(即,库文件所在的数据库,服务器的URL等).执行此操作的最佳方法是使用该define()函数,因为这些值不会经常更改,并且可以轻松放入配置文件中.

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

  4. 当php4中的每个实例占用内存时,单例模式在php中变得流行.单例通过仅允许创建一个对象实例来帮助保存ram.在参考之前,甚至依赖注射本来是一个坏主意.

    PHP 5.4+中对象的新PHP实现解决了大多数这些问题,你可以安全地传递对象,几乎没有惩罚.这不再是必要的.

    单例的另一个用途是特殊实例,其中一次只能存在一个对象实例,该实例可能存在于脚本执行之前/之后,并且该对象在不同脚本/服务器/语言之间共享等.这里单例模式解决了解决方案很好.

因此,如果您处于第1,2或3位,那么使用全局将是合理的.但是在其他情况下应该使用方法1.

随意更新应该使用全局变量的任何其他实例.