P粉7864325792023-07-24 11:19:34
雖然在函數作用域內定義的變數無法從外部訪問,但這並不意味著在函數完成後就無法使用它們的值。 PHP有一個廣為人知的靜態關鍵字,在物件導向的PHP中被廣泛用於定義靜態方法和屬性,但需要記住靜態關鍵字也可以在函數內部用來定義靜態變數。
靜態變數與在函數作用域內定義的普通變數不同,它在程式執行離開該作用域時不會遺失其值。讓我們考慮以下使用靜態變數的範例:
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變量,那麼每次輸出的值都會與傳遞給函數的$num參數相同。使用靜態關鍵字可以建立這個簡單的計數器,而無需額外的解決方案。
靜態變數僅存在於局部函數作用域中。它無法在定義它的函數之外存取。因此,您可以確保它的值在下一次呼叫函數之前保持不變。
靜態變數只能定義為標量或標量表達式(自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中引入。簡單來說,它們允許您將某個變數集合的存取權限制在函數作用域內的另一個匿名函數中,該匿名函數將是存取這些變數的唯一方式。在閉包中,變數可以模擬(或多或少成功)物件導向程式設計中的「類別常數」(如果它們透過值傳遞給閉包)或「私有屬性」(如果透過引用傳遞)的概念。
實際上,後者允許使用閉包來取代靜態變數。使用哪種方式完全取決於開發者的決定,但值得一提的是,靜態變數在處理遞歸時非常有用,並且值得開發者註意。
P粉5746952152023-07-24 09:30:10
變數有一個有限的"作用域",或說"可以存取它們的位置"。僅僅因為你在應用程式的某個地方寫了一次 $foo = 'bar'; 並不意味著你可以在應用程式的其他任何地方引用 $foo。變數 $foo 在特定的作用域內有效,只有在相同作用域內的程式碼才能存取該變數。
非常簡單:PHP具有函數作用域。這是PHP中唯一存在的作用域分隔符號。函數內部的變數只能在該函數內部使用。函數外部的變數可以在函數外的任何地方使用,但不能在任何函數內部使用。這意味著PHP中有一個特殊的作用域:全域作用域。在任何函數外部宣告的變數都在全域作用域內。
<?php $foo = 'bar'; function myFunc() { $baz = 42; }
$foo
is in the global scope, $baz
is in a local scope inside myFunc
. Only code inside myFunc
has access to $baz
. Only code outside myFunc
has access to $foo
. Neither 有 access to the other:
<?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!
適用於包含的程式碼的規則與適用於任何其他程式碼的規則相同:只有函數可以分隔作用域。就作用域而言,您可以將包含檔案視為複製和貼上程式碼。
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 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'; } } // 无法访问 $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變數。請注意,這與全域作用域不同。
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?!?!!
沒有任何跡象表明這個函數有任何副作用,但實際上它有。當一些函數不斷修改和依賴一些全域狀態時,這很容易變成一個混亂的局面。你希望函數是無狀態的,只對其輸入進行操作並傳回定義好的輸出,而不管你呼叫它多少次。
盡可能避免在任何情況下使用全域作用域;特別是不應該將變數從全域作用域「提取」到局部作用域。