PHP 常量

常量是在程序运行过程中值不会改变的标识符。与变量不同,常量一旦定义就不能被修改或重新定义(除非使用 runkit 等扩展)。常量在整个脚本中全局有效,不受变量作用域的限制,在函数、类、文件中都可以直接访问。

在实际开发中,常量通常用于存储配置信息(数据库地址、API 密钥)、状态码、版本号等不应该被修改的值。使用常量而非变量,可以防止这些关键值被意外修改,提高代码的安全性和可维护性。

定义常量:define() 函数

define() 是最传统的常量定义方式,在运行时执行,可以在条件语句中使用:

PHP 实例
define('MAX_SIZE', 100);
define('SITE_NAME', 'PHP 教程');
define('DEBUG', true);
define('BASE_URL', 'https://phpe.cn');

echo MAX_SIZE; // 100 echo SITE_NAME; // PHP 教程 ``

常量名通常使用全大写字母加下划线的命名规范,这是 PHP 社区的约定俗成,便于与变量区分。

PHP 7+ 支持定义数组常量:

`php define('ALLOWED_TYPES', ['jpg', 'png', 'gif', 'webp']); define('DB_CONFIG', [ 'host' => 'localhost', 'port' => 3306, 'charset' => 'utf8mb4', ]);

echo ALLOWED_TYPES[0]; // jpg echo DB_CONFIG['host']; // localhost `

定义常量:const 关键字

const 是在编译时定义常量,语法更简洁,但有一些限制:

`php const VERSION = '1.0.0'; const PI = 3.14159265358979; const APP_NAME = 'MyApp'; const IS_PRODUCTION = false;

echo VERSION; // 1.0.0 `

const 的主要优势是可以在类中定义类常量,这是 define() 无法做到的。

define() vs const 对比

两种方式各有适用场景,了解区别有助于做出正确选择:

特性define()const
定义时机运行时编译时
条件定义✅ 支持❌ 不支持
类中定义❌ 不支持✅ 支持
命名空间需要完整路径自动归属当前命名空间
数组值✅ PHP 7+✅ 支持
表达式值✅ 支持✅ PHP 8+
`php // define() 可以在条件中使用 if ($environment === 'production') { define('DEBUG', false); } else { define('DEBUG', true); }

// const 不能在条件、函数、循环中使用 // if (...) { const FOO = 1; } // 语法错误! `

魔术常量

PHP 提供了 9 个预定义的魔术常量,它们的值会随着代码位置的变化而变化,因此称为"魔术"常量:

常量说明示例值
__LINE__当前行号42
__FILE__当前文件完整绝对路径/var/www/html/index.php
__DIR__当前文件所在目录/var/www/html
__FUNCTION__当前函数名myFunction
__CLASS__当前类名(含命名空间)App\Models\User
__TRAIT__当前 Trait 名Timestampable
__METHOD__当前方法名(含类名)User::getName
__NAMESPACE__当前命名空间App\Controllers
__COMPILER_HALT_OFFSET____halt_compiler() 后的字节偏移-
`php echo __LINE__; // 输出当前行号 echo __FILE__; // /var/www/html/index.php echo __DIR__; // /var/www/html

function testMagic() { echo __FUNCTION__; // testMagic }

class MyClass { public function myMethod() { echo __CLASS__; // MyClass echo __METHOD__; // MyClass::myMethod } } `

魔术常量在实际开发中非常有用,例如用 __DIR__ 构建文件路径,避免相对路径问题:

`php // 不推荐:相对路径可能因调用位置不同而出错 require 'config/database.php';

// 推荐:使用 __DIR__ 确保路径正确 require __DIR__ . '/config/database.php'; `

类常量

在面向对象编程中,可以在类内部定义常量,通过 类名::常量名 访问:

`php class HttpStatus { const OK = 200; const NOT_FOUND = 404; const SERVER_ERROR = 500;

// PHP 8.3+ 支持类型化常量 // const int OK = 200; }

class Config { const VERSION = '2.0.0'; const DB_CHARSET = 'utf8mb4'; const MAX_RETRY = 3;

public function getVersion(): string { return self::VERSION; // 类内部用 self:: } }

// 类外部访问 echo HttpStatus::OK; // 200 echo HttpStatus::NOT_FOUND; // 404 echo Config::VERSION; // 2.0.0

// 子类可以继承父类常量 class ExtendedConfig extends Config { public function showVersion(): void { echo parent::VERSION; // 访问父类常量 echo self::VERSION; // 访问当前类常量 } } `

实际应用场景

常量在项目中有很多典型的使用场景:

`php // 1. 数据库配置 define('DB_HOST', 'localhost'); define('DB_PORT', 3306); define('DB_NAME', 'myapp'); define('DB_USER', 'root'); define('DB_CHARSET', 'utf8mb4');

// 2. 业务状态码 define('ORDER_PENDING', 1); // 待支付 define('ORDER_PAID', 2); // 已支付 define('ORDER_SHIPPED', 3); // 已发货 define('ORDER_COMPLETED', 4); // 已完成 define('ORDER_CANCELLED', 5); // 已取消

// 3. 文件路径 define('ROOT_PATH', __DIR__); define('UPLOAD_PATH', ROOT_PATH . '/uploads/'); define('CACHE_PATH', ROOT_PATH . '/cache/'); define('LOG_PATH', ROOT_PATH . '/logs/');

// 4. 使用常量 function getOrderStatus(int $status): string { return match($status) { ORDER_PENDING => '待支付', ORDER_PAID => '已支付', ORDER_SHIPPED => '已发货', ORDER_COMPLETED => '已完成', ORDER_CANCELLED => '已取消', default => '未知状态', }; }

常见问题

Q:常量和变量有什么本质区别? A:变量的值可以随时修改,常量一旦定义就不能改变。变量有作用域限制,常量全局有效。变量以 $ 开头,常量不需要。在存储不应被修改的配置值时,应该使用常量而非变量。

Q:常量名区分大小写吗? A:PHP 8.0 之前,define() 有一个第三个参数可以设置不区分大小写,但该特性已在 PHP 8.0 中移除。现在 PHP 常量默认区分大小写,MAX_SIZEmax_size 是不同的常量。

Q:如何检查一个常量是否已定义? A:使用 defined() 函数:if (defined('MY_CONST')) { ... }。这在防止重复定义常量时很有用,常见于框架的入口文件安全检查。

Q:接口中可以定义常量吗? A:可以。接口中定义的常量可以被实现该接口的类继承,通过 接口名::常量名类名::常量名` 访问。这是定义共享常量的好方式。