先前同事詢問有關 PHP static 關鍵字的用法,這裡我簡單整理一下。
static 主要用途在於定義一個變數空間,讓函式或類別可以保留住該變數的值,直到下次的存取。
以下就各別來探討 static 在函式與類別中的用法。
函式裡的 static 關鍵字
先來看看以下的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | |
首先我們在 getCount() 這個方法中定義了一個 static 變數 $count ,然後每次呼叫 getCount() 時,就會對 $count 作累加的動作。
接著我們透過迴圈執行十次 getCount() 方法,便可得到了 1 ~ 10 的輸出結果。
這是因為將變數宣告為 static 後,第一次呼叫 getCount() 這個方法時,會為 $count 保留一塊記憶體空間;而當脫離了 getCount() 的變數作用域後,這個記憶體空間並不會被消滅掉,而會在下一次呼叫 getCount() 方法時,再次被配置進來,並還原先前的變數值。
因此,除了第一次呼叫 getCount() 方法外,接下來的每次呼叫都會讓 $count 值累加,而得到 1 ~ 10 的輸出結果;如果把 static 關鍵字拿掉,就會輸出十次的 1 。
應用在遞迴上的 static 關鍵字
瞭解函式中的 static 關鍵字用法後,我們來看一個應用的例子:
1 2 3 4 5 6 7 8 | |
這是 PHP 版的 Fibonacci Sequence 遞迴函式,原理我就不多說明了。先來看看它在參數為 20 時所需要的執行時間:
1 2 3 4 5 6 7 8 | |
這裡代入的數字越大,執行時間會越長。
現在我們用 static 關鍵字來改寫:
1 2 3 4 5 6 7 8 9 10 11 12 | |
然後再來看執行的時間:
1 2 3 4 5 6 7 8 | |
速度竟然差了快三百倍!為什麼?
其實是因為這裡的 static 關鍵字扮演了 cache 的角色,讓程式不用重新計算已經算好的結果。而使用了 static 關鍵字後,也使得執行時間不會再隨著代入數字變大而變長。
註:不是任何遞迴函式都可以用 static 變數來做 cache ,在使用上要特別注意這一點。
類別裡的 static 關鍵字
在類別裡的 static 關鍵字,也扮演了類似的角色。我們利用 static 在類別裡定義的屬性,會佔用類別的記憶體空間。而透過同一類別所生成的物件,會彼此共享這個 static 屬性;所以不論我們產生多少同類別的物件,它們都會存取到同一個 static 類別屬性。
來看看以下的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
範例即為經典的 Singleton 模式,原理這裡先不多做說明。
這裡我們定義了兩個類別 static 屬性,分別是 $_instance 及 $_instanceCount ;而透過 static 定義的類別屬性,在類別裡存取時要用 self 關鍵字加上雙冒號 (::) ,例如 self::$_instance 。
接著我們可以看到在 getInstance() 方法中,如果 self::$_instance 是 null 的話,表示是第一次呼叫,那麼程式就會透過私有的建構式產生一個 DB 物件指定給 self::$_instance 變數,最後再將它回傳出去。這時雖然 getInstance() 裡的變數作用域已經結束,但 self::$_instance 卻會保留下來。
下一次 getInstance() 呼叫時,因為 self::$_instance 已經不再是 null 值,所以就會直接將其內容回傳給呼叫的程式了。
也因為這個原因,所以整個程式執行下來, DB 的 __construct() 方法也只被執行過一次,使得 self::$_instanceCount 也只累加一次,其結果永遠為 1 。
結論
一般 PHP 開發者很少會去使用 static 關鍵字,因為平常會用到 static 的場合其實也不多。這裡我再做一次 static 使用時機的重點整理:
- 需要記住上一次函式執行的結果。
- 某些可以保留執行結果的遞迴函式。
- 不希望因為物件個體不同,進而被影響的類別屬性。
- 類別的 Singleton 模式。
希望透過上面的說明,能讓大家對 static 關鍵字有進一步的瞭解。