参考:什么是variables作用域,哪些variables可以从哪里访问,什么是“未定义的variables”错误?

注意:这是在PHP中处理variables作用域的一个参考问题。 请把适合这种模式的许多问题都作为这个模式的重复。

什么是PHP中的“可变范围”? 一个.php文件中的variables是否可以在另一个中访问? 为什么我有时会得到“未定义的variables”错误?

什么是“可变范围”?

variables有一个有限的“范围”,或“他们可以访问的地方”。 只是因为你写了$foo = 'bar'; 一旦在你的应用程序的某个地方 ,并不意味着你可以从应用程序的其他地方引用$foo 。 variables$foo具有一定范围,在该范围内它是有效的,并且只有在相同范围内的代码才能访问该variables。

在PHP中如何定义一个范围?

非常简单:PHP具有function范围 。 这是PHP中唯一的范围分隔符。 函数中的variables只能在该函数中使用。 函数之外的variables在函数之外的任何地方都可用,但不在任何函数之内。 这意味着在PHP中有一个特殊的范围: 全局范围。 在任何函数之外声明的任何variables都在这个全局范围内。

例:

 <?php $foo = 'bar'; function myFunc() { $baz = 42; } 

$foo全局范围内, $bazmyFunc中的本地范围内。 只有myFunc中的代码才能访问$baz 。 只有myFunc 之外的代码才能访问$foo 。 两者都没有访问另一个:

 <?php $foo = 'bar'; function myFunc() { $baz = 42; echo $foo; // doesn't work echo $baz; // works } echo $foo; // works echo $baz; // doesn't work 

范围和包括的文件

文件边界不分隔范围:

a.php只会

 <?php $foo = 'bar'; 

b.php

 <?php include 'a.php'; echo $foo; // works! 

include d代码的规则同样适用于任何其他代码:只有function是独立的作用域。 为了范围的目的,您可能会想到包含复制和粘贴代码的文件:

c.php

 <?php function myFunc() { include 'a.php'; echo $foo; // works } myFunc(); echo $foo; // doesn't work! 

在上面的例子中, a.php包含在myFunca.php所有variables只有本地函数的作用域。 仅仅因为他们似乎a.php中的全局范围并不一定意味着它们,它实际上取决于在哪个上下文中包含/执行代码。

那么函数和类中的函数呢?

每个新的function声明都引入了一个新的范围,就这么简单。

(匿名)函数

 function foo() { $foo = 'bar'; $bar = function () { // no access to $foo $baz = 'baz'; }; // no access to $baz } 

 $foo = 'foo'; class Bar { public function baz() { // no access to $foo $baz = 'baz'; } } // no access to $baz 

什么范围适合?

处理范围问题似乎很烦人,但是有限的variables范围对于编写复杂的应用程序是必不可less的! 如果你声明的每个variables都可以在你的应用程序的其他地方使用,那么你将会遍历你的variables,而没有真正的方法来追踪什么变化。 只有很多明智的名字可以给你的variables,你可能想在多个地方使用variables“ $name ”。 如果在应用程序中只能使用一次这个唯一的variables名称,则必须使用非常复杂的命名scheme来确保您的variables是唯一的,并且不会从错误的代码块中更改错误的variables。

注意:

 function foo() { echo $bar; } 

如果没有范围,上面的函数会做什么? $bar从哪里来? 它有什么状态? 它是否被初始化? 你每次都要检查吗? 这是不可维护的。 这给我们带来了…

穿越范围界限

正确的方法:将variables传入和传出

 function foo($bar) { echo $bar; return 42; } 

variables$bar作为函数参数显式进入这个范围。 只要看看这个函数,就可以很清楚地看到它的工作原理来自哪里。 然后显式返回一个值。 调用者有信心知道函数将使用哪些variables以及返回值的来源:

 $baz = 'baz'; $blarg = foo($baz); 

将variables的范围扩展为匿名函数

 $foo = 'bar'; $baz = function () use ($foo) { echo $foo; }; $baz(); 

匿名函数明确包含$foo的周围范围。 请注意,这与全局范围不一样。

错误的方式: global

如前所述,全局范围有些特殊,函数可以从中明确地导入variables:

 $foo = 'bar'; function baz() { global $foo; echo $foo; $foo = 'baz'; } 

这个函数使用并修改全局variables$foo不要这样做! (除非你真的真的真的知道你在做什么,即使如此:不要!)

这个函数的所有调用者都看到这个:

 baz(); // outputs "bar" unset($foo); baz(); // no output, WTF?! baz(); // outputs "baz", WTF?!?!! 

没有迹象表明这个function有任何副作用 ,但它确实。 随着一些function的不断变化,需要一些全局状态,这很容易变得混乱。 你希望函数是无状态的 ,仅对它们的input进行操作,并返回定义的输出,然而很多时候你都会调用它们。

您应该尽可能避免使用全球范围; 当然,你不应该把variables从全局范围“拉”到局部范围。

variables的范围是在其中定义的上下文。 大多数情况下,所有的PHPvariables只有一个范围。 这个单一的范围跨越包括和所需的文件以及。 例如:

 <?php $a = 1; include 'b.inc'; ?> 

这里$avariables将在包含的b.inc脚本中可用。 但是,在用户定义的函数中引入了本地函数作用域。 任何在函数内部使用的variables默认都是局限于本地函数的作用域。 例如:

 <?php $a = 1; /* global scope */ function test() { echo $a; /* reference to local scope variable */ } test(); ?> 

此脚本不会产生任何输出,因为echo语句引用$ avariables的本地版本,并且在此范围内没有为其分配值。 您可能会注意到,这与C语言有点不同,因为C中的全局variables可以自动用于函数,除非被本地定义明确覆盖。 这可能会导致一些问题,人们可能会无意中更改全局variables。 在PHP中,如果要在函数中使用全局variables,则必须在函数内声明全局variables。

虽然在函数作用域内定义的variables不能从外部访问,但并不意味着在该函数完成后不能使用它们的值。 PHP有一个众所周知的static关键字,广泛用于定义静态方法和属性的面向对象的PHP中,但是应该记住static也可以用在函数内部来定义静态variables。

什么是“静态variables”?

静态variables不同于函数作用域中定义的普通variables,当程序执行离开这个作用域时,它的值不会松动。 我们来考虑一下使用静态variables的例子:

 function countSheep($num) { static $counter = 0; $counter += $num; echo "$counter sheep jumped over fence"; } countSheep(1); countSheep(2); countSheep(3); 

结果:

 1 sheep jumped over fence 3 sheep jumped over fence 6 sheep jumped over fence 

如果我们没有定义$counter ,那么每次echo的值和传递给函数的$num参数是一样的。 使用static允许build立这个简单的计数器没有额外的解决方法。

静态variables的用例

  1. 在相应的函数之间存储值。
  2. 在没有办法(或没有目的)将它们作为parameter passing时,在recursion调用之间存储值。
  3. 要caching通常更好的值来检索一次。 例如,读取服务器上不可变文件的结果。

技巧

静态variables仅存在于本地函数作用域中。 它不能被定义的函数以外的地方访问。所以你可以确定它将保持其值不变,直到下一次调用该函数为止。

静态variables只能定义为标量或标量expression式(自PHP 5.6起)。 至less在写这篇文章的时候,指定其他的值不可避免地会导致失败。 不过,您可以在代码的下一行执行此操作:

 function countSheep($num) { static $counter = 0; $counter += sqrt($num);//imagine we need to take root of our sheep each time echo "$counter sheep jumped over fence"; } 

结果:

 2 sheep jumped over fence 5 sheep jumped over fence 9 sheep jumped over fence 

静态函数是在同一类的对象的方法之间“共享”的。 通过查看以下示例很容易理解:

 class SomeClass { public function foo() { static $x = 0; echo ++$x; } } $object1 = new SomeClass; $object2 = new SomeClass; $object1->foo(); // 1 $object2->foo(); // 2 oops, $object2 uses the same static $x as $object1 $object1->foo(); // 3 now $object1 increments $x $object2->foo(); // 4 and now his twin brother 

这只适用于同一类的对象。 如果对象来自不同的类(甚至相互延伸),静态variables的行为将如预期的那样。

静态variables是调用函数之间保持值的唯一方法吗?

在函数调用之间保持值的另一种方法是使用闭包。 在PHP 5.3中引入了闭包。 用两个字来说,它们允许你限制对一个函数范围内的一些variables的访问权限到另一个匿名函数,这将是访问它们的唯一方法。 在闭包variables中,可能会模仿(或多或less)成功的OOP概念,如结构化编程中的“类常量”(如果它们是通过值封闭传递的)或“私有属性”(如果通过引用传递)。

后者实际上允许使用闭包而不是静态variables。 使用什么总是由开发人员决定,但应该提到的是,静态variables在处理recursion时肯定是有用的,值得开发人员注意。