# 常量
## 1. 特征
| 序號 | 特征 |
| ---- | ---------------------------- |
| 1 | 常量前面沒有美元符號`$` |
| 2 | 常量創建時必須初始化 |
| 3 | 常量禁止更新和刪除 |
| 4 | 常量不受作用域限制 |
| 5 | 推薦使用大寫字母加下劃線命名 |
---
## 2. 函數/關鍵字
| 序號 | 定義方式 | 描述 |
| ---- | ------------------------- | ---------------- |
| 1 | `get_defined_constants()` | 查看系統所有常量 |
| 2 | `defined()` | 檢測常量是否存在 |
| 3 | `define()` | 創建常量 |
| 4 | `const` 關鍵字 | 創建常量 |
| 5 | `constant()` | 獲取常量值 |
- `get_defined_constants(true)`: 常量分組打印,自定義常量在`user`分組
- `defined()`: 返回布爾值
```php
// 獲取系統定義的所有常量
print_r(get_defined_constants(true));
// 僅查看用戶自定義的常量
print_r(get_defined_constants(true)['user']);
```
---
## 3. 創建常量
| 序號 | 定義方式 | 區別 |
| ---- | -------------- | ---------------------------------------------------- |
| 1 | `define()` | 除了不能創建類常量, 可以在程序的任何地方定義 |
| 2 | `const` 關鍵字 | 必須全局定義,不能用在函數和流程控制中,允許創建類常量 |
- 常量值: `int`, `float`, `string`, `boolean`,`null`, `array`(php7+)
- `define($name, true)`: 允許忽略常量名大小寫
- `const`在編譯階段處理, `define()`在運行階段處理,所以`const`必須定義在全局才有效
- `class`類聲明在編譯時處理,且并不會創建作用域,當然也是全局中, 所以`const`可以用
---
## 4. 獲取常量
| 序號 | 函數/關鍵字 | 區別 |
| ---- | ------------ | ---------------------- |
| 1 | `echo` | 通常使用已知的常量名 |
| 2 | `constant()` | 常量名不確定或在變量中 |
示例代碼: `demo12.php`
```php
# 常量
// define():函數定義常量
define('LECTURE', '朱老師');
// const關鍵字: 定義常量
const COURSE = 'PHP';
// 常量不受作用域限制
function test1()
{
echo LECTURE . '教: ' . COURSE . '<br>';
// define()可以用在函數中,流程控制中,const不可以
define('SEX', '男');
echo SEX, '<br>';
// const不能用在函數中
// const AGE = 30;
// echo AGE;
}
test1();
echo '<hr>';
if (true) {
// define()允許用在流程控制中
define('EMAIL', 'admin@php.cn');
echo EMAIL;
// const 不能用在流程控制中
// const B = 'hello world';
// echo B;
}
echo '<hr>';
// const可以創建類常量,define()不行
class Demo
{
const HELLO = 'php.cn';
// define('HI', 'Peter Zhu');
}
echo Demo::HELLO, '<br>';
// 檢測常量是否存在?
var_dump(defined('EMAIL'));
echo '<hr>';
// 獲取系統定義的所有常量
// echo '<pre>' . print_r(get_defined_constants(), true) . '</pre>';
// 根據鍵名分組查看
// echo '<pre>' . print_r(get_defined_constants(true), true) . '</pre>';
// 僅查看用戶自定義的常量
echo '<pre>' . print_r(get_defined_constants(true)['user'], true) . '</pre>';
// 常量的值允許是標量(單值)和數組
// 標量: 整數,浮點數, 字符串, 布爾,前面已有介紹
// 從php7.0+開始, 常量也支持數組類型了
const DB_LINKS = [
'host' => 'localhost',
'username' => 'root',
'password' => 'root',
'charset' => 'utf8',
];
echo '<pre>' . print_r(DB_LINKS, true) . '</pre>';
echo '<hr>';
// 除了用echo 打印常量外, 還可以用constant()函數
// constant參數是字符串,字符串的內容是變量名,并且只返回的值,必須用echo / print_r等打印
echo constant('LECTURE') . '<br>';
// 你可能會認為這樣做多此一舉,因為完全可以直接用echo, 事實也的確如此
echo LECTURE . '<br>';
// 但是,當我們不知道常量名, 或者常量名在變量中時, 這個函數就非常有用了
// 常量名在變量中
$constName = 'EMAIL';
// 只能輸出常量名,不能獲取到常量值
echo $constName, '<br>';
// constant(): 可以將常量名從變量中解析來正確讀到
echo constant($constName), '<br>';
// 空字符串也能做常量名, 盡管沒啥意義,但卻是合法的(我想應該沒人會這樣做)
define('', "其實我也是常量");
// 空字符串不是一個有效的php標識符,所以不能使用const來定義這個常量
// 下面指令肯定獲取不到常量值
echo '', '<br>';
// 而constant()函數仍然可以讀到常量, 不知道這是不是一個Bug?
echo constant('');
```
---
## 5. 預定義常量
預定義常量非常多,有許多與具體擴展相關,如 `PDO`, 這里僅列出系統級常用的:
| 序號 | 預定義常量 | 描述 |
| ---- | ---------------------- | ----------------------------------------- |
| 1 | `PHP_VERSION` | PHP 版本 |
| 2 | `PHP_MAXPATHLEN` | PHP 路徑最大長度:1024 |
| 3 | `PHP_OS_FAMILY` | 操作系統:Windows/Darwin/Linux |
| 4 | `PHP_SAPI` | web 服務器與 php 之間接口: apache2handler |
| 5 | `PHP_EOL` | 行尾結束符 |
| 6 | `PHP_INT_MAX` | 最大整數: `9223372036854775807` |
| 7 | `PHP_INT_MIN` | 最小整數: `-9223372036854775808` |
| 8 | `PHP_INT_SIZE` | 整數寬度: `8` |
| 9 | `PHP_FLOAT_MAX` | 最大浮點數:`1.7976931348623E+308` |
| 10 | `PHP_FLOAT_MIN` | 整小浮點數: `2.2250738585072E-308` |
| 11 | `DEFAULT_INCLUDE_PATH` | 默認 PHP 命令路徑 |
| 12 | `PHP_EXTENSION_DIR` | 默認 PHP 擴展路徑 |
| 13 | `E_ERROR` | 運行時錯誤: 致命中斷 |
| 14 | `E_PARSE` | 語法解析錯誤: 致命中斷 |
| 15 | `E_NOTICE` | 運行時提示: 不中斷 |
| 16 | `E_WARNING` | 運行時警告: 不中斷 |
| 17 | `E_ALL` | 所有級別錯誤(除`E_STRICT`) |
| 18 | `E_STRICT` | 更加嚴格的錯誤處理機制,高于`E_ALL` |
| 19 | `TRUE` | 布爾真 |
| 20 | `FALSE` | 布爾假 |
| 21 | `NULL` | 空 |
| 22 | `DIRECTORY_SEPARATOR` | 目錄分隔符 |
更多預定義常量:<https://www.php.net/manual/zh/reserved.constants.php>
---
## 6. 魔術常量
- 魔術常量也屬于"預定義常量", 比較特殊所有單獨列出
- 所謂"魔術", 是指常量的值, 會隨它們在代碼中的位置改變而改變
- 魔術常量不區分大小寫, 但是推薦全部大寫
| 序號 | 魔術常量 | 描述 |
| ---- | --------------- | ---------------------- |
| 1 | `__LINE__` | 文件中的當前行號 |
| 2 | `__FILE__` | 文件的完整路徑和文件名 |
| 3 | `__DIR__` | 文件所在目錄 |
| 4 | `__FUNCTION__` | 當前的函數名稱 |
| 5 | `__CLASS__` | 當前類名稱 |
| 6 | `__TRAIT__` | 當前`Trait`名稱 |
| 7 | `__METHOD__` | 當前類方法名稱 |
| 8 | `__NAMESPACE__` | 當前命名空間名稱 |
示例代碼: `demo13.php`
```php
# 預定義常量
echo '版本號: ' . PHP_VERSION . '<br>';
echo '操作系統: ' . PHP_OS_FAMILY . '<br>';
echo '最大整數: ' . PHP_INT_MAX . '<br>';
echo '最大浮點數: ' . PHP_FLOAT_MAX . '<br>';
echo '目錄分隔符: ' . DIRECTORY_SEPARATOR . '<hr>';
// 魔術常量
echo '當前行號: ' . __LINE__ . '<br>';
echo '當前文件: ' . __FILE__ . '<br>';
echo '當前目錄: ' . __DIR__ . '<br>';
// 更多魔術常量在類與對象中再詳細介紹
```
---
## 7. 常量命名空間
- 當使用的第三方組件(類庫)中存在也當前腳本命名沖突的常量名時,可以用命名空間解決
- 命名空間允許將同名的標識符,定義在不同的空間中,類似同名文件可存放在不同目錄下
- 命名空間使用關鍵字`namespace`聲明, 必須放在腳本的首行,且前面不允許有任何輸出
```php
# 常量的命名空間
// 報重復聲明錯誤
// const APP_PATH = __DIR__ . '/public/';
// const APP_PATH = __DIR__ . '/home/';
// 將同名常量定義在不同的空間中, 則不會報錯
namespace ns1 {
const APP_PATH = __DIR__ . '/public/';
}
namespace ns2 {
const APP_PATH = __DIR__ . '/home/';
}
// 全局空間,不需要空間名
namespace {
// 訪問命名空間中的常量, 必須帶上空間名稱
echo \ns1\APP_PATH . '<br>';
echo \ns2\APP_PATH . '<br>';
}
```