首页  >  问答  >  正文

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


注意:这是一个处理 PHP 中变量作用域的参考问题。请将适合此模式的众多问题中的任何一个作为此问题的副本关闭。

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

P粉023650014P粉023650014340 天前514

全部回复(2)我来回复

  • P粉311563823

    P粉3115638232023-10-20 10:03:09

    尽管无法从外部访问在函数作用域内定义的变量,但这并不意味着您不能在该函数完成后使用它们的值。 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 可以构建这个简单的计数器,无需额外的解决方法。

    静态变量用例

    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";
    }

    结果:

    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 概念,例如结构化编程中的“类常量”(如果它们在闭包中按值传递)或“私有属性”(如果通过引用传递)。

    后者实际上允许使用闭包而不是静态变量。使用什么始终由开发人员决定,但应该提到的是,静态变量在使用递归时绝对有用,值得开发人员注意。

    回复
    0
  • P粉895187266

    P粉8951872662023-10-20 09:03:53

    什么是“变量范围”?

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

    PHP 中如何定义范围?

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

    示例:

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

    $foo 位于 global 范围内,$baz 位于 内的local 范围内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代码的规则与适用于任何其他代码的规则相同:仅函数的单独作用域。出于范围的目的,您可能会考虑包括复制和粘贴代码之类的文件:

    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”。如果您的应用程序中只能使用一次这个唯一的变量名称,那么您必须采用非常复杂的命名方案来确保您的变量是唯一的,并且您不会从错误的代码段中更改错误的变量。< /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。请注意,这与全局范围不同。

    错误的方式:全局

    如前所述,全局作用域有些特殊,函数可以显式地从中导入变量:

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

    该函数使用并修改全局变量$foo不要这样做! (除非你真的真的真的知道自己在做什么,即使这样:也不要这样做!)

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

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

    没有迹象表明此函数有任何副作用,但它确实有。这很容易变得混乱,因为某些函数不断修改并需要一些全局状态。您希望函数无状态,仅作用于它们的输入并返回定义的输出,无论您调用它们多少次。

    您应该尽可能避免以任何方式使用全局作用域;最肯定的是,您不应该将变量从全局范围“拉”到局部范围。

    回复
    0
  • 取消回复