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

dec*_*eze 163 php scope

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

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

dec*_*eze 181

什么是"可变范围"?

变量具有有限的"范围"或"可以访问它们的位置".仅仅因为你写$foo = 'bar';一次的地方在你的应用程序并不意味着你可以参照$foo到处其他的应用程序中.变量$foo具有一定的范围,在该范围内,变量有效,并且只有同一范围内的代码才能访问变量.

如何在PHP中定义范围?

很简单:PHP具有功能范围.这是PHP中唯一存在的范围分隔符.函数内部的变量仅在该函数内可用.功能之外的变量可以在函数之外的任何地方使用,但不能在任何函数内部使用.这意味着PHP中有一个特殊范围:全局范围.在任何函数之外声明的任何变量都在此全局范围内.

例:

<?php

$foo = 'bar';

function myFunc() {
    $baz = 42;
}
Run Code Online (Sandbox Code Playgroud)

$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
Run Code Online (Sandbox Code Playgroud)

范围和包含的文件

文件边界不分隔范围:

a.php只会

<?php

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

b.php

<?php

include 'a.php';

echo $foo;  // works!
Run Code Online (Sandbox Code Playgroud)

include适用于任何其他代码的d代码适用相同的规则:只有function单独的范围.出于范围的目的,您可以考虑包括复制和粘贴代码等文件:

c.php

<?php

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

    echo $foo;  // works
}

myFunc();

echo $foo;  // doesn't work!
Run Code Online (Sandbox Code Playgroud)

在上面的例子中,a.php包含在里面myFunc,里面的任何变量a.php只有本地函数作用域.仅仅因为它们似乎在全局范围内a.php并不一定意味着它们,它实际上取决于包含/执行代码的上下文.

函数和类中的函数怎么样?

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

(匿名)函数内部函数

function foo() {
    $foo = 'bar';

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

    // no access to $baz
}
Run Code Online (Sandbox Code Playgroud)

$foo = 'foo';

class Bar {

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

}

// no access to $baz
Run Code Online (Sandbox Code Playgroud)

什么是范围有益?

处理范围问题可能看起来很烦人,但有限的变量范围对于编写复杂的应用程序至关重要!如果您声明的每个变量都可以从应用程序中的其他任何位置获得,那么您将无法跨越变量而没有真正的方法来跟踪更改内容的变化.你可以给你的变量只有很多明智的名字,你可能想$name在不止一个地方使用变量" ".如果你的应用程序只能拥有一个这个唯一的变量名,那么你必须采用非常复杂的命名方案,以确保你的变量是唯一的,并且你不会从错误的代码段中更改错误的变量.

注意:

function foo() {
    echo $bar;
}
Run Code Online (Sandbox Code Playgroud)

如果没有范围,上述功能会做什么?哪里$bar来的?它有什么状态?它甚至被初始化了吗?你每次都要检查一下吗?这是不可维护的.这让我们...

跨越范围边界

正确的方法:传入和传出变量

function foo($bar) {
    echo $bar;
    return 42;
}
Run Code Online (Sandbox Code Playgroud)

变量$bar显式作为函数参数进入此范围.只要看一下这个函数就可以清楚它所使用的值来源于何处.然后它显式返回一个值.调用者有信心知道函数将使用哪些变量以及返回值来自何处:

$baz   = 'baz';
$blarg = foo($baz);
Run Code Online (Sandbox Code Playgroud)

将变量范围扩展为匿名函数

$foo = 'bar';

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

$baz();
Run Code Online (Sandbox Code Playgroud)

匿名函数明确包含$foo其周围范围.请注意,这与全局范围不同.

错误的方法: global

如前所述,全局范围有点特殊,函数可以显式地从中导入变量:

$foo = 'bar';

function baz() {
    global $foo;
    echo $foo;
    $foo = 'baz';
}
Run Code Online (Sandbox Code Playgroud)

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

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

baz(); // outputs "bar"
unset($foo);
baz(); // no output, WTF?!
baz(); // outputs "baz", WTF?!?!!
Run Code Online (Sandbox Code Playgroud)

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

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


Ale*_*kov 10

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

什么是'静态变量'?

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

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

countSheep(1);
countSheep(2);
countSheep(3);
Run Code Online (Sandbox Code Playgroud)

结果:

1 sheep jumped over fence
3 sheep jumped over fence
6 sheep jumped over fence
Run Code Online (Sandbox Code Playgroud)

如果我们定义$counter没有static那么每次回显的值将与$num传递给函数的参数相同.使用static允许构建这个简单的计数器而无需额外的解决方法

静态变量用例

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

技巧

静态变量仅存在于本地函数范围内.它不能在已定义的函数之外访问.因此,您可以确保它将保持其值不变,直到下一次调用该函数.

静态变量只能定义为标量或标量表达式(自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";
}
Run Code Online (Sandbox Code Playgroud)

结果:

2 sheep jumped over fence
5 sheep jumped over fence
9 sheep jumped over fence
Run Code Online (Sandbox Code Playgroud)

静态函数在同一类的对象方法之间有点"共享".通过查看以下示例很容易理解:

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

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

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

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

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