注意:这是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
文件边界不分隔作用域:
。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
包含在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
。 请注意,这与全局范围不同。
前面说过,全局范围有些特殊,函数可以显式地从中导入变量:
$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有一个众所周知的statice
关键字,在面向对象的PHP中广泛用于定义静态方法和属性,但是应该记住,statice
也可以在函数内部用于定义静态变量。
静态变量不同于一般定义在函数作用域中的变量,它在程序执行离开作用域时不会出现值松动。 让我们考虑以下使用静态变量的示例:
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
而没有statice
,那么每次回显的值将与传递给函数的$num
参数相同。 使用statice
可以生成这个简单的计数器,而无需额外的变通方法。
静态变量仅存在于局部函数作用域中。 不能在定义它的函数之外访问它。 因此您可以确信,在下次调用该函数之前,它将保持其值不变。
静态变量只能定义为标量或标量表达式(从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
这只适用于同一类的对象。 如果对象来自不同的类(甚至相互扩展),静态变量的行为将与预期的一样。
另一种在函数调用之间保持值的方法是使用闭包。 闭包是在PHP5.3中引入的。 简单地说,它们允许您将对某个函数范围内的某个变量集的访问限制在另一个匿名函数上,这是访问这些变量的唯一方法。 在闭包中,变量可能会模仿(或多或少成功地)OOP概念,如结构化编程中的“类常量”(如果它们在闭包中通过值传递)或“私有属性”(如果通过引用传递)。
后者实际上允许使用闭包而不是静态变量。 使用什么总是由开发人员来决定,但是应该提到的是,静态变量在使用递归时肯定是有用的,并且值得开发人员注意。
我不会给出这个问题的完整答案,因为现有的答案和PHP手册已经很好地解释了大部分问题。
但是缺少的一个主题是超全局变量,包括常用的$_post
,$_get
,$_session
等。这些变量是在任何作用域中始终可用的数组,没有global
声明。
例如,这个函数将打印出运行PHP脚本的用户的名称。 变量可用于函数而没有任何问题。
<?php
function test() {
echo $_ENV["user"];
}
“全局是坏的”的一般规则在PHP中通常被修改为“全局是坏的,但超全局是好的”,只要没有误用它们。 (所有这些变量都是可写的,因此如果您真的很糟糕,可以使用它们来避免依赖注入。)
这些变量不能保证存在; 管理员可以使用php.ini
中的variables_order
指令禁用部分或全部命令,但这不是常见的行为。
当前超全局的列表:
$globals
-当前脚本中的所有全局变量$_server
-有关服务器和执行环境的信息$_get
-在URL的查询字符串中传递的值,与请求使用的HTTP方法无关$_POST
-在HTTP POST请求中传递的值,具有application/x-www-form-urlencoded
或multipart/form-data
MIME类型$_Files
-在HTTP POST请求中传递的具有multipart/form-data
MIME类型$_cookie
-与当前请求一起传递的cookie$_Session
-PHP内部存储的会话变量$_request
-通常是$_get
和$_post
的组合,但有时是$_cookies
。 内容由php.ini
.request_order
指令确定
$_env
-当前脚本的环境变量