我偏好的檢查常數是否已設定,以及如果未設定則設定它的方法(可用於設定檔案中的預設值,使用者已經有機會在其他地方設定自己的值)。
<?php
defined('CONSTANT') or define('CONSTANT', 'SomeDefaultValue');
?>
Dan.
(PHP 4, PHP 5, PHP 7, PHP 8)
defined — 檢查指定的常數名稱是否存在
檢查給定的 constant_name
常數是否已定義。
注意:
如果您想查看變數是否存在,請使用 isset(),因為 defined() 只適用於 常數。如果您想查看函式是否存在,請使用 function_exists()。
constant_name
常數名稱。
範例 #1 檢查常數
<?php
/* 注意引號的使用,這很重要。此範例正在檢查
* 字串 'TEST' 是否為名為 TEST 的常數名稱 */
if (defined('TEST')) {
echo TEST;
}
interface bar {
const test = 'foobar!';
}
class foo {
const test = 'foobar!';
}
var_dump(defined('bar::test')); // bool(true)
var_dump(defined('foo::test')); // bool(true)
?>
範例 #2 檢查列舉成員 (PHP 8.1.0 起)
<?php
enum Suit
{
case Hearts;
case Diamonds;
case Clubs;
case Spades;
}
var_dump(defined('Suit::Hearts')); // bool(true)
?>
我偏好的檢查常數是否已設定,以及如果未設定則設定它的方法(可用於設定檔案中的預設值,使用者已經有機會在其他地方設定自己的值)。
<?php
defined('CONSTANT') or define('CONSTANT', 'SomeDefaultValue');
?>
Dan.
// 檢查類別常數是否存在,如果類別是由變數參考。
class Class_A
{
const CONST_A = 'value A';
}
// 當類別名稱已知時。
if ( defined( 'Class_A::CONST_A' ) )
echo 'Class_A::CONST_A defined';
// 使用類別名稱變數。注意雙引號。
$class_name = Class_A::class;
if ( defined( "$class_name::CONST_A" ) )
echo '$class_name::CONST_A defined';
// 使用實例化物件作為變數類別。
$object_A = new $class_name();
if ( defined( get_class($object_A).'::CONST_A' ) )
echo '$object_A::CONST_A defined';
在使用 defined() 之前,請先查看以下的基準測試
true 0.65 毫秒
$true 0.69 毫秒 (1)
$config['true'] 0.87 毫秒
TRUE_CONST 1.28 毫秒 (2)
true 0.65 毫秒
defined('TRUE_CONST') 2.06 毫秒 (3)
defined('UNDEF_CONST') 12.34 毫秒 (4)
isset($config['def_key']) 0.91 毫秒 (5)
isset($config['undef_key']) 0.79 毫秒
isset($empty_hash[$good_key]) 0.78 毫秒
isset($small_hash[$good_key]) 0.86 毫秒
isset($big_hash[$good_key]) 0.89 毫秒
isset($small_hash[$bad_key]) 0.78 毫秒
isset($big_hash[$bad_key]) 0.80 毫秒
PHP 版本 5.2.6, Apache 2.0, Windows XP
每個陳述式都執行 1000 次,雖然 1000 次呼叫的 12 毫秒的額外負荷不會讓終端使用者抓狂,但與 if(true) 相比,確實會產生一些有趣的結果
1) if($true) 幾乎完全相同
2) if(TRUE_CONST) 速度幾乎慢了兩倍 - 我猜想替換不是在編譯時完成的(我必須再次確認這一點!)
3) 如果常數存在,defined() 的速度會慢 3 倍
4) 如果常數不存在,defined() 的速度會慢 19 倍!
5) 無論您輸入什麼,isset() 的效率都非常高(對於任何實作陣列驅動事件系統的人來說都是好消息 - 我!)
可能要避免 if(defined('DEBUG'))...
您也可以在 defined 中使用後期靜態命令 "static::"。這個範例輸出 - 正如預期 - "int (2)"
<?php
abstract class class1
{
public function getConst()
{
return defined('static::SOME_CONST') ? static::SOME_CONST : false;
}
}
final class class2 extends class1
{
const SOME_CONST = 2;
}
$class2 = new class2;
var_dump($class2->getConst());
?>
別忘了將常數名稱放在單引號中。 否則你不會收到錯誤或警告。
<?php
define("AMOUNT_OF_APPLES", 12);
if(defined(AMOUNT_OF_APPLES)){
//這裡不會有任何輸出
echo AMOUNT_OF_APPLES;
}
?>
所以應該這樣做
<?php
define("AMOUNT_OF_APPLES", 12);
if(defined("AMOUNT_OF_APPLES")){
//這樣就可以了
echo AMOUNT_OF_APPLES;
}
//輸出: 12
?>
我花了半天時間才發現...
我發現 PHP 沒有 enum 函式,所以我建立了自己的。這並非必要,但偶爾會派上用場。
<?php
function enum()
{
$args = func_get_args();
foreach($args as $key=>$arg)
{
if(defined($arg))
{
die('重複定義常數 ' . $arg);
}
define($arg, $key);
}
}
enum('ONE','TWO','THREE');
echo ONE, ' ', TWO, ' ', THREE;
?>
這個函式,以及 constant(),都對命名空間敏感。可以想像它們總是在「根命名空間」下運行,這可能會有幫助。
<?php
命名空間 FOO\BAR
{
const WMP="wmp";
函式 test()
{
if(defined("WMP")) echo "直接使用: ".constant("WMP"); //無效
elseif(defined("FOO\\BAR\\WMP")) echo "使用命名空間: ".constant("FOO\\BAR\\WMP"); //有效
echo WMP; //有效
}
}
命名空間
{
\FOO\BAR\test();
}
在 PHP5 中,您可以使用 defined() 來查看物件常數是否已定義,如下所示:
<?php
類別 Generic
{
const WhatAmI = 'Generic';
}
if (defined('Generic::WhatAmI'))
{
echo Generic::WhatAmI;
}
?>
認為這點可能值得注意。
-Nick
我發現了一些東西:如果參考遺失,defined() 可能會回傳 false。
<?php
session_start(); // $_SESSION 已建立
define('SESSION_BACKUP', $_SESSION);
if (defined('SESSION_BACKUP')) echo 'A';
session_unset(); // $_SESSION 已銷毀
if (defined('SESSION_BACKUP')) echo 'B';
?>
您會看到 "A",但不會看到 "B"。
如果常數名稱開頭有反斜線 (\),則無法使用 defined() 函式偵測其是否存在,也無法使用 constant() 函式取得其值。
您可以使用 get_defined_constants() 函式檢查其是否存在並取得其值,或者在常數名稱前面加上兩個反斜線 (\\)。
<?php
define('\DOMAIN', 'wuxiancheng.cn');
$isDefined = defined('\DOMAIN'); // false
$domain = constant('\DOMAIN'); // NULL,在 PHP 8+ 版本中,你會得到一個致命錯誤。
var_dump($isDefined, $domain);
?>
<?php
define('\DOMAIN', 'wuxiancheng.cn');
$constants = get_defined_constants();
$isDefined = isset($constants['\DOMAIN']);
$domain = $isDefined ? $constants['\DOMAIN'] : NULL;
var_dump($isDefined, $domain);
?>
<?php
define('\DOMAIN', 'wuxiancheng.cn');
$isDefined = defined('\\\DOMAIN');
$domain = constant('\\\DOMAIN');
var_dump($isDefined, $domain);
?>
使用布林值定義常數時要小心,並假設 defined 檢查了特定值,例如:
<?php
define('DEBUG', false);
if(defined('DEBUG')){
echo '並非真正的除錯模式';
}
?>
你也應該檢查常數的值,例如:
<?php
define('DEBUG', true);
if(defined('DEBUG') && DEBUG){
echo '這才是真正的除錯模式';
}
?>
defined 做的只是驗證常數是否存在,而不是它的值。
如果想要保護檔案不被直接存取,我通常會這樣做
index.php
<?php
// 主要程式碼放在這裡
define('START',microtime());
include "x.php";
?>
x.php
<?php
defined('START') || (header("HTTP/1.1 403 Forbidden") & die('403.14 - 目錄列表被拒絕。'));
?>
要在使用 trait 的類別中檢查常數是否已定義,請在常數名稱前加上 `self::class`
<?php
trait MyTrait {
public function checkConstant() {
assert(defined(self::class . "::MY_CONSTANT"));
print self::MY_CONSTANT;
}
}
class MyClass {
use MyTrait;
protected const MY_CONSTANT = 'my value';
}
$class = new MyClass();
$class->checkConstant();
?>
你可能會發現,如果你使用 <?= ?> 來輸出你的常數,而它們沒有被定義,根據你的錯誤報告級別,你可能不會顯示錯誤,而是只顯示常數的名稱。例如:
<?= TEST ?>
...可能會顯示 TEST,而不是你預期的空字串。 解決方法是像這樣的函式:
<?php
function C(&$constant) {
$nPrev1 = error_reporting(E_ALL);
$sPrev2 = ini_set('display_errors', '0');
$sTest = defined($constant) ? '已定義' : '未定義';
$oTest = (object) error_get_last();
error_reporting($nPrev1);
ini_set('display_errors', $sPrev2);
if ($oTest->message) {
return '';
} else {
return $constant;
}
}
?>
所以現在你可以這樣做:
<?= C(TEST) ?>
如果 TEST 已使用 define() 賦值,則你將收到該值。如果沒有,則你將收到一個空字串。
如果您能用更少的程式碼完成這項工作,或者能使用比切換錯誤處理器更佳的解決方案,請發文。