参考:什么是变量范围,哪些变量可以从哪里访问,什么是“未定义变量”错误?

时间:2013-06-06 10:20:36

标签: php scope

  

注意:这是在PHP中处理变量范围的参考问题。请关闭适合此模式的许多问题中的任何一个。

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

3 个答案:

答案 0 :(得分:171)

什么是“变量范围”?

变量具有有限的“范围”或“可以访问它们的位置”。仅仅因为您在应用程序中的某处编写了$foo = 'bar';并不意味着您可以从应用程序内的其他引用$foo。变量$foo具有一定的有效范围,只有同一范围内的代码才能访问该变量。

如何在PHP中定义范围?

非常简单: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代码适用相同的规则:仅function个单独的范围。出于范围的考虑,您可以考虑包括复制和粘贴代码等文件:

c.php

<?php

function myFunc() {
    include 'a.php';

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!

在上面的例子中,a.php包含myFunca.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”。如果你的应用程序中只能有一个这个唯一的变量名,那么你必须采用非常复杂的命名方案来确保你的变量是唯一的,并且你不会从错误的代码段改变错误的变量。 / p>

观察:

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 范围不同。

错误的方式: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?!?!!

没有迹象表明此功能有副作用,但确实如此。这很容易变得混乱,因为一些函数不断修改并要求某些全局状态。您希望函数无状态,仅对其输入执行操作并返回已定义的输出,无论您多次调用它们。

您应该尽可能避免以任何方式使用全局范围;当然,你不应该将变量从全局范围“拉”到本地范围内。

答案 1 :(得分:10)

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

什么是'静态变量'?

静态变量与函数范围中定义的普通变量不同,只要在程序执行离开此范围时它没有松散值。让我们考虑以下使用静态变量的示例:

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可以构建这个简单的计数器而无需额外的解决方法。

静态变量用例

  1. 在后续的函数调用之间存储值。
  2. 当没有办法(或没有办法)时,在递归调用之间存储值 目的)将它们作为参数传递。
  3. 缓存通常最好检索一次的值。对于 例如,在服务器上读取不可变文件的结果。
  4. 技巧

    静态变量仅存在于本地函数范围内。它不可能是     在已经定义的函数之外访问。所以你可以     确保它在下次调用之前保持其值不变     那个功能。

    静态变量只能定义为标量或标量     表达式(自PHP 5.6起)。不可避免地为其分配其他值     至少在撰写本文时,导致失败。 不过,您只能在代码的下一行执行此操作:

    static

    结果:

    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
    

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

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

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

    后者实际上允许使用闭包而不是静态变量。使用什么总是由开发人员决定,但应该提到静态变量在处理递归时非常有用,并且值得开发人员注意。

答案 2 :(得分:1)

由于现有问题和PHP manual在解释大部分问题上都做得很好,因此我不会对问题作完整的回答。

但是缺少的一个主题是superglobals的主题,包括常用的$_POST$_GET$_SESSION等。这些变量是始终可用的数组,在任何范围内都没有global声明。

例如,此函数将打印出运行PHP脚本的用户名。该变量对函数可用,没有任何问题。

<?php
function test() {
    echo $_ENV["user"];
}

在PHP中,通常将“全局变量是坏的”的一般规则修改为“全局变量是坏的,但超级全局变量是好的”,只要不滥用它们即可。 (所有这些变量都是可写的,因此如果您确实很糟糕,可以将它们用于避免依赖项注入。)

这些变量不能保证存在;管理员可以使用php.ini中的variables_order directive禁用其中的一些或全部,但这不是常见的行为。


当前超全局变量列表:

  • $GLOBALS-当前脚本中的所有全局变量
  • $_SERVER-有关服务器和执行环境的信息
  • $_GET-在URL的查询字符串中传递的值,无论用于请求的HTTP方法如何
  • $_POST-在HTTP POST请求中使用application/x-www-form-urlencodedmultipart/form-data MIME类型传递的值
  • $_FILES-在HTTP POST请求中以multipart/form-data MIME类型传递的文件
  • $_COOKIE-当前请求传递的Cookie
  • $_SESSION-PHP内部存储的会话变量
  • $_REQUEST-通常是$_GET$_POST的组合,但有时是$_COOKIES。内容由php.ini中的request_order directive确定。
  • $_ENV-当前脚本的环境变量