想在 PHP 類別裡放一個固定值,或讓多個物件共用一份狀態時,你可能會遇到 const 和 static。兩者都用 :: 存取,看起來像失散多年的雙胞胎;但一個是「不能改的規則」,另一個是「可以改的共用狀態」。這篇會用範例帶你分清楚它們的用途、限制,以及該怎麼選,避免讓 AI 幫你寫出一鍋靜態義大利麵。
目錄
先用一句話分清楚
const 用來宣告類別常數:值建立後不能重新指定,適合表達固定規則或設定。
static 則是 static 關鍵字的總稱,可用於:
- 靜態屬性:屬於類別、由同一個類別的程式碼共用,而且可以修改。
- 靜態方法:不需要物件實例就能呼叫的方法。
- 函式或方法內的靜態區域變數:在多次呼叫之間保留值。
因此,const 和 static 不是同一個層級的東西。const 是一種常數宣告方式;static 是一種「不綁定到單一物件」的語意。
static:共用狀態與不需要物件的方法
靜態屬性
靜態屬性使用 :: 和 $ 存取,例如 Counter::$count。它不屬於某個特定物件,而是屬於類別本身:
<?php
class Counter
{
public static int $count = 0;
public static function increment(): void
{
self::$count++;
}
}
Counter::increment();
Counter::increment();
echo Counter::$count; // 2
這裡不需要先 new Counter()。每次呼叫 increment(),都會修改同一份 Counter::$count。
要注意,靜態屬性是「共用」,不是「自動安全」。如果不同流程都能任意修改它,程式的狀態就可能變得難以追蹤。共用狀態很方便,方便到有時候會偷偷變成全域變數,然後在除錯時躲在角落喝咖啡。
靜態方法
靜態方法也不需要物件實例,通常適合不依賴物件狀態的工具型操作:
<?php
class TextFormatter
{
public static function normalize(string $text): string
{
return trim(mb_strtolower($text));
}
}
echo TextFormatter::normalize(' HELLO '); // hello
靜態方法裡沒有 $this,因為根本沒有「目前這個物件」。如果方法需要讀取或修改某個物件的屬性,就應該改成一般的實例方法。
函式內的靜態變數
static 也可以用在函式或方法內,讓區域變數在同一個 PHP 執行期間的多次呼叫之間保留值:
<?php
function nextNumber(): int
{
static $number = 0;
return ++$number;
}
echo nextNumber(); // 1
echo nextNumber(); // 2
這裡的 $number 不是類別屬性,也不是常數;它只是這個函式自己的持久區域狀態。需要注意的是,這種狀態會讓函式不再完全純粹,測試與重設狀態時要多留意。
const:不會改變的類別規則
類別常數在類別主體中使用 const 宣告,透過 ClassName::CONSTANT 或類別內的 self::CONSTANT 存取:
<?php
class HttpStatus
{
public const OK = 200;
public const NOT_FOUND = 404;
}
function isSuccess(int $status): bool
{
return $status === HttpStatus::OK;
}
echo isSuccess(HttpStatus::OK) ? '成功' : '失敗'; // 成功
類別常數的值不能像變數一樣重新指定。下面這種寫法不是更新值,而是錯誤的 PHP 程式碼:
<?php
class Config
{
public const VERSION = '1.0';
}
// 錯誤:類別常數不能重新指定
Config::VERSION = '2.0';
如果版本需要在執行期間變更,就不該用 const;請改用設定物件、一般屬性,或其他適合管理可變狀態的方式。常數的工作是守規則,不是每天改版號。
常數不能放在方法裡
類別常數要直接宣告在類別主體,不能在方法內使用 const 宣告:
<?php
class Example
{
// 正確:常數宣告在類別主體
public const MESSAGE = 'Hello';
public function show(): string
{
return self::MESSAGE;
}
}
若你需要在函式內建立一次、之後重複使用的可變狀態,那是函式內的 static;若值從設計上就不應改變,才考慮常數。
self::、static:: 和 $this-> 怎麼選?
這三種寫法常常一起出現,但角色不同:
$this->property:存取目前物件的實例屬性。self::$property:存取目前宣告這段程式碼的類別之靜態屬性。static::$property:使用晚期靜態繫結,依照實際呼叫的子類別解析。ClassName::CONSTANT:從類別外存取類別常數。
例如:
<?php
class ParentConfig
{
public const NAME = 'parent';
public static function withSelf(): string
{
return self::NAME;
}
public static function withStatic(): string
{
return static::NAME;
}
}
class ChildConfig extends ParentConfig
{
public const NAME = 'child';
}
echo ChildConfig::withSelf(); // parent
echo ChildConfig::withStatic(); // child
self:: 指向方法宣告所在的類別;static:: 則會考慮最後實際呼叫的類別。只有在需要支援繼承與晚期靜態繫結時,才使用 static::,不要看到 static 就全部套上去——PHP 不是調味料,什麼都加一匙不會比較香。
static 與 const 的差異
| 比較項目 | static | const |
|---|---|---|
| 主要用途 | 共用狀態、靜態方法、函式內保留狀態 | 宣告不能重新指定的類別常數 |
| 是否能重新指定 | 靜態屬性可以;靜態方法不是值 | 不可以 |
| 是否需要物件 | 靜態屬性與方法不需要 | 不需要 |
| 存取方式 | ClassName::$property、ClassName::method() | ClassName::CONSTANT |
是否有 $this | 靜態方法沒有 $this | 常數本身沒有方法內容 |
| 常見風險 | 共用狀態造成隱藏的相依性 | 把其實會變動的設定錯當成固定值 |
這裡不列「誰比較快」的排行榜。除非你有實際的效能測量,否則直接宣稱某一種語法一定比較快,通常只是把猜測穿上工程師外套。選擇時先看語意與可維護性;真的遇到效能問題,再用符合實際情境的基準測試驗證。
實際上該怎麼選?
可以用下面幾個問題快速判斷:
- 這個值從設計上就不應改變嗎?選類別常數
const。 - 多個物件需要共用,而且狀態會更新嗎?才考慮靜態屬性
static。 - 方法完全不需要物件資料嗎?可以考慮靜態方法,但也要衡量測試與相依性是否會變複雜。
- 只是想在函式多次呼叫時記住上一次結果嗎?可以使用函式內的
static,但要清楚記錄這份隱藏狀態。 - 這其實是某個物件的資料嗎?使用一般屬性與實例方法,通常更容易理解與測試。
一般來說,版本號、固定的狀態碼、固定的限制值,很適合用 const。需要累計的計數器、共用快取或類別層級狀態,才可能使用 static。至於 Singleton,雖然可以用靜態屬性實作,但它會引入全域存取與隱藏相依性,不是看到「只要一個實例」就必須立刻拿出的萬用扳手。
總結
記住這個簡化版:
const:這個值是規則,不能重新指定。static:這個成員不綁定單一物件,可以在類別層級使用;若是屬性,值仍然可能改變。$this->:這個資料屬於目前這個物件。
語法記熟只是起點,真正重要的是讓程式碼把「固定規則」和「可變狀態」分清楚。分清楚之後,未來閱讀 PHP 類別時,就不會看到兩個冒號便開始冒冷汗。
參考資料
- PHP 官方文件:Static Keyword:說明靜態屬性、靜態方法,以及函式內的靜態變數。
- PHP 官方文件:Class Constants:說明類別常數的宣告、存取與繼承行為。
- PHP 官方文件:Scope Resolution Operator (
::):說明::、self、parent與static的用途。 - PHP 官方文件:Late Static Bindings:說明
static::如何依照實際呼叫的類別解析。

