搜尋

首頁  >  問答  >  主體

參考:什麼是變數範圍,哪些變數可以從哪裡存取以及什麼是「未定義變數」錯誤?


注意:這是一個處理 PHP 中變數作用域的參考問題。請將適合此模式的眾多問題中的任何一個作為此問題的副本關閉。

什麼是 PHP 中的「變數範圍」?一個 .php 檔案中的變數是否可以在另一個 .php 檔案中存取?為什麼我有時會收到「未定義的變數」錯誤?

P粉023650014P粉023650014405 天前624

全部回覆(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
  • 取消回覆