注意:这是在PHP中处理变量范围的参考问题。请关闭符合此模式的许多问题中的任何一个。
什么是PHP中的“变量范围”?一个.php文件中的变量是否可以在另一个中访问?为什么我有时会得到“未定义的变量”错误?
变量具有有限的“范围”或“可以访问它们的位置”。仅仅因为你在应用程序的某个地方写过$foo = 'bar';
并不意味着你可以从应用程序内的其他地方引用$foo
。变量$foo
具有一定的范围,在该范围内它是有效的,并且只有相同范围内的代码才能访问该变量。
很简单:PHP具有功能范围。这是PHP中唯一存在的范围分隔符。函数内部的变量仅在该函数内可用。功能之外的变量可以在函数之外的任何地方使用,但不能在任何函数内部使用。这意味着PHP中有一个特殊范围:全局范围。在任何函数之外声明的任何变量都在此全局范围内。
<?php
$foo = 'bar';
function myFunc() {
$baz = 42;
}
$foo
在全球范围内,$baz
位于myFunc
内的本地范围内。只有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
s单独的范围。出于范围的目的,您可以考虑包括复制和粘贴代码等文件:
c.php
<?php
function myFunc() {
include 'a.php';
echo $foo; // works
}
myFunc();
echo $foo; // doesn't work!
在上面的例子中,a.php
包含在myFunc
中,a.php
中的任何变量只有本地函数范围。仅仅因为它们似乎在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
处理范围问题可能看起来很烦人,但有限的变量范围对于编写复杂的应用程序至关重要!如果您声明的每个变量都可以从应用程序中的其他任何位置获得,那么您将无法跨越变量而没有真正的方法来跟踪更改内容的变化。你可以给你的变量只有很多合理的名字,你可能想在不止一个地方使用变量“$name
”。如果你的应用程序只能拥有一个这个唯一的变量名,那么你必须采用非常复杂的命名方案,以确保你的变量是唯一的,并且你不会从错误的代码段中更改错误的变量。
注意:
function foo() {
echo $bar;
}
如果没有范围,上述功能会做什么? $bar
来自哪里?它有什么状态?它甚至被初始化了吗?你每次都要检查一下吗?这是不可维护的。这让我们...
function foo($bar) {
echo $bar;
return 42;
}
变量$bar
明确地作为函数参数进入此范围。只要看一下这个函数就可以清楚它所使用的值来源于何处。然后它显式返回一个值。调用者有信心知道函数将使用哪些变量以及返回值来自何处:
$baz = 'baz';
$blarg = foo($baz);
$foo = 'bar';
$baz = function () use ($foo) {
echo $foo;
};
$baz();
匿名函数显式包含来自其周围范围的$foo
。请注意,这与全局范围不同。
global
如前所述,全局范围有点特殊,函数可以显式地从中导入变量:
$foo = 'bar';
function baz() {
global $foo;
echo $foo;
$foo = 'baz';
}
此函数使用并修改全局变量$foo
。不要这样做! (除非你真的真的真的知道你在做什么,即便如此:不要!)
这个函数的所有调用者看到的是:
baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!
没有迹象表明这个功能有任何副作用,但确实如此。由于一些功能不断修改并需要一些全局状态,因此很容易变得混乱。您希望函数是无状态的,仅对其输入执行操作并返回已定义的输出,但是您需要多次调用它们。
您应该尽可能避免以任何方式使用全局范围;当然,您不应该将变量从全局范围“拉”到本地范围。
虽然无法从外部访问函数范围内定义的变量,但并不意味着在该函数完成后无法使用它们的值。 PHP有一个众所周知的static
关键字,它广泛用于面向对象的PHP中,用于定义静态方法和属性,但是应该记住,static
也可以在函数内部用于定义静态变量。
静态变量与函数范围中定义的普通变量不同,以防在程序执行离开此范围时它没有松散值。让我们考虑以下使用静态变量的示例:
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
的情况下定义了static
,那么每次回显的值都会与传递给函数的$num
参数相同。使用static
可以构建这个简单的计数器而无需额外的解决方法。
静态变量仅存在于本地函数范围内。它不能在已定义的函数之外访问。因此,您可以确保它将保持其值不变,直到下一次调用该函数。
静态变量只能定义为标量或标量表达式(自PHP 5.6起)。至少在撰写本文时,为其分配其他值不可避免地会导致失败。不过,您只能在代码的下一行执行此操作:
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
这仅适用于同一类的对象。如果对象来自不同的类(甚至相互扩展),静态变量的行为将如预期的那样。
在函数调用之间保持值的另一种方法是使用闭包。闭包在PHP 5.3中引入。用两个词来说,它们允许您将对函数范围内某些变量集的访问限制为另一个匿名函数,这是访问它们的唯一方法。在闭包变量中可能模仿(或多或少成功)OOP概念,如“类常量”(如果它们是按值关闭传递)或“私有属性”(如果通过引用传递)在结构化编程中。
后者实际上允许使用闭包而不是静态变量。使用什么总是由开发人员决定,但应该提到静态变量在使用递归时非常有用,并且值得开发人员注意。
变量的范围是定义它的上下文。在大多数情况下,所有PHP变量只有一个范围。此单一范围也涵盖包含和所需文件。例如:
<?php
$a = 1;
include 'b.inc';
?>
这里的$a
变量将在包含的b.inc
脚本中提供。但是,在用户定义的函数中,引入了本地函数范围。函数内使用的任何变量默认限制为本地函数范围。例如:
<?php
$a = 1; /* global scope */
function test()
{
echo $a; /* reference to local scope variable */
}
test();
?>
此脚本不会生成任何输出,因为echo语句引用$ a变量的本地版本,并且尚未在此范围内分配值。您可能会注意到这与C语言略有不同,因为C中的全局变量可自动用于函数,除非被本地定义特别覆盖。这可能会导致一些问题,因为人们可能会无意中更改全局变量。在PHP中,如果要在该函数中使用全局变量,则必须在函数内将其声明为全局变量。