Zend Framework 文件明確建議:「強烈不鼓勵使用 define 定義全域常量,類別常量請用 const」。本文從錯誤案例、效能分析、替代模式三個面向,說明為何應謹慎使用傳統常量,並提供現代最佳實踐。
<?php
define('C', 12345);
define('C', 123); // Notice: Constant C already defined
// 程式繼續執行,C 仍為 12345,但開發者可能誤以為已更新
?>
團隊協作、第三方套件載入順序不同時,極易產生「以為改了常數值,實際卻沒生效」的難除錯 Bug。
<?php
define('C', 123);
// ❌ 錯誤寫法 1:isset() 只能檢查變數
if (isset(C)) { ... }
// ❌ 錯誤寫法 2:defined() 參數需為字串
if (defined(C)) { ... } // 若 C 未定義,這裡會拋 Notice
// ✅ 正確寫法
if (defined('C')) { ... }
?>
PHP 內核處理常量需在全域符號表多次查找,且常量不可變特性阻礙 OpCache 某些優化。大量常量存取(如迴圈中)會有可測量的效能差距。
<?php
final class AppConfig {
// 編譯期常量,效能最佳、IDE 自動完成、可型別提示
public const WEBSITE = 'https://example.com';
public const MAX_UPLOAD_MB = 50;
// 執行期可變配置(如從資料庫/環境變數讀取)
private static ?string $forumTheme = null;
public static function getForumTheme(): string {
if (self::$forumTheme === null) {
self::$forumTheme = $_ENV['FORUM_THEME'] ?? 'default';
}
return self::$forumTheme;
}
public static function setForumTheme(string $theme): void {
self::$forumTheme = $theme;
}
}
// 使用方式
echo AppConfig::WEBSITE;
$theme = AppConfig::getForumTheme();
?>
| 特性 | define() | const (類別常量) | 靜態屬性 + Getter |
|---|---|---|---|
| 命名空間隔離 | ❌ 全域 | ✅ | ✅ |
| IDE 自動完成/重構 | ❌ | ✅ | ✅ |
| 型別提示 (PHP 8.1+) | ❌ | ✅ | ✅ |
| 執行期可修改 | ❌ | ❌ | ✅ |
| 效能 | 較低 | 最高 | 高 |
| 單元測試友善 | 困難 (全域狀態) | 容易 | 容易 (可 Mock) |
少數合理使用內建常數的場景:PHP_EOL 會依執行平台自動對應 \n (Linux/macOS)、\r\n (Windows)、\r (舊版 Mac),寫入文字檔、日誌、CLI 輸出時請直接使用,避免硬編碼換行符。
define(),改用 const 定義編譯期常量。private static 屬性 + 存取器,配合環境變數或設定檔載入。define(),並加上專案前綴避免衝突(如 MYAPP_DB_HOST)。