PHP 常量
常量是在程序运行过程中值不会改变的标识符。与变量不同,常量一旦定义就不能被修改或重新定义(除非使用 runkit 等扩展)。常量在整个脚本中全局有效,不受变量作用域的限制,在函数、类、文件中都可以直接访问。
在实际开发中,常量通常用于存储配置信息(数据库地址、API 密钥)、状态码、版本号等不应该被修改的值。使用常量而非变量,可以防止这些关键值被意外修改,提高代码的安全性和可维护性。
定义常量:define() 函数
define() 是最传统的常量定义方式,在运行时执行,可以在条件语句中使用:
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_SIZE 和 max_size 是不同的常量。
Q:如何检查一个常量是否已定义? A:使用 defined() 函数:if (defined('MY_CONST')) { ... }。这在防止重复定义常量时很有用,常见于框架的入口文件安全检查。
Q:接口中可以定义常量吗? A:可以。接口中定义的常量可以被实现该接口的类继承,通过 接口名::常量名 或 类名::常量名` 访问。这是定义共享常量的好方式。