Skip to content

常量语法

常量是程序中固定不变的值的标识符。一旦定义,常量的值不能被修改或取消定义。PHP 提供了两种定义常量的方式:const 关键字和 define() 函数。理解它们的区别和适用场景是编写健壮 PHP 代码的基础。

前置知识

基础概念

常量与变量类似,但有几个关键区别:

  • 常量前面没有 $ 符号
  • 常量一旦定义就不能被重新定义或取消定义
  • 常量不受作用域限制,可在任何地方访问
  • 常量默认为全局可见

PHP 提供两种定义常量的方式:

方式语法定义时机作用域
const 关键字const NAME = value;编译时当前作用域(类内为类常量)
define() 函数define("NAME", value);运行时全局

const 关键字

基本用法

const 在编译时定义常量,必须在最外层作用域(或在类/接口声明内)使用:

php
<?php
declare(strict_types=1);

// 使用 const 定义常量
const APP_NAME = "My Application";
const APP_VERSION = "2.0.0";
const MAX_UPLOAD_SIZE = 5242880;
const IS_DEBUG = false;

// 访问常量(不需要 $ 前缀)
echo APP_NAME . "\n";      // My Application
echo APP_VERSION . "\n";   // 2.0.0
echo MAX_UPLOAD_SIZE . "\n"; // 5242880

const 的限制

const 只能接受标量表达式和数组作为值,且必须在编译时可确定:

php
<?php
declare(strict_types=1);

// 标量值
const PI = 3.14159;
const SITE_NAME = "My Site";

// 标量表达式
const SITE_URL = SITE_NAME . ".com";
const DOUBLE_PI = PI * 2;

// 数组常量
const ALLOWED_TYPES = ["jpg", "png", "gif", "webp"];
const DB_CONFIG = [
    "host" => "localhost",
    "port" => 3306,
];

echo ALLOWED_TYPES[0] . "\n";     // jpg
echo DB_CONFIG["host"] . "\n";    // localhost

const 不能在条件块中使用

const 在编译时定义,因此不能在函数、循环、if 语句或 try/catch 块中使用:

php
<?php
declare(strict_types=1);

// 错误:const 不能在 if 内部使用
// if (true) {
//     const INSIDE_IF = "value";  // Parse error
// }

// 正确:define() 可以在运行时条件定义
if (true) {
    define("INSIDE_IF", "value");  // 合法
}

echo INSIDE_IF . "\n"; // value

define() 函数

基本用法

define() 在运行时定义常量,可以接受任何表达式作为值,并且可以在任何位置调用:

php
<?php
declare(strict_types=1);

// 使用 define() 定义常量
define("DATABASE_HOST", "localhost");
define("DATABASE_PORT", 3306);
define("DATABASE_NAME", "myapp");

echo DATABASE_HOST . "\n"; // localhost

// define() 支持表达式
define("DB_DSN", "mysql:host=" . DATABASE_HOST . ";port=" . DATABASE_PORT . ";dbname=" . DATABASE_NAME);
echo DB_DSN . "\n";

define() 的可选参数

define() 支持第三个参数 case_insensitive,控制常量名是否区分大小写:

php
<?php
declare(strict_types=1);

// 默认区分大小写
define("MY_CONSTANT", "value");
echo MY_CONSTANT . "\n";   // value
// echo my_constant;        // PHP 8.0+ Error: 未定义的常量

// PHP 8.0 起不再支持大小写不敏感的常量定义
// define("OLD_CONSTANT", "value", true); // 废弃

PHP 8.0 变更

PHP 8.0.0 起已移除大小写不敏感的常量定义。所有常量名现在都是区分大小写的。在 PHP 8.0 之前,define("CONSTANT", "value", true) 会创建一个大小写不敏感的常量。

define() 的高级特性

define() 支持将常量定义为表达式,这是它与 const 的一个重要区别:

php
<?php
declare(strict_types=1);

// define() 允许使用运行时表达式
$prefix = "app_";
$suffix = "_version";
define("DYNAMIC_NAME", $prefix . "current" . $suffix);
echo DYNAMIC_NAME . "\n"; // app_current_version

// define() 支持多维数组(PHP 7.0+)
define("MULTI_DIM_ARRAY", [
    "1st" => ["jan", "feb", "mar"],
    "2nd" => ["apr", "may", "jun"],
]);
echo MULTI_DIM_ARRAY["1st"][0] . "\n"; // jan

详细说明

const 与 define() 的对比

特性constdefine()
定义时机编译时运行时
作用域当前作用域(全局或类)始终全局
条件定义不支持支持
表达式值仅编译时常量表达式任意表达式
数组支持PHP 5.6+PHP 7.0+
大小写敏感始终敏感PHP 8.0+ 始终敏感
类常量支持不支持
命名空间遵循命名空间默认全局

使用未定义常量的行为

PHP 8.0+ 严格处理

从 PHP 8.0.0 起,使用未定义的常量将抛出 Error 异常。在 PHP 8.0 之前,未定义的常量会被解释为字符串本身(并产生 E_NOTICE 警告),此行为在 PHP 7.2.0 中被废弃。

php
<?php
declare(strict_types=1);

// PHP 8.0+:抛出 Error
// echo UNDEFINED_CONSTANT;

// 使用 defined() 检查常量是否存在
if (defined("APP_DEBUG")) {
    echo APP_DEBUG ? "Debug ON" : "Debug OFF";
} else {
    echo "APP_DEBUG is not defined\n";
}

// 使用 constant() 动态获取常量值
$constName = "APP_VERSION";
if (defined($constName)) {
    echo constant($constName) . "\n";
}

命名空间中的常量

php
<?php
declare(strict_types=1);

namespace App\Config;

// const 遵循命名空间
const DB_HOST = "localhost";

// define() 默认定义全局常量
define("GLOBAL_CONST", "global value");

// 访问方式不同
echo \App\Config\DB_HOST . "\n";   // localhost(带命名空间)
echo GLOBAL_CONST . "\n";           // global value(全局)
echo \GLOBAL_CONST . "\n";          // global value

类常量

const 可以在类中定义常量,这是 define() 无法做到的:

php
<?php
declare(strict_types=1);

class UserRole
{
    const ADMIN = "admin";
    const EDITOR = "editor";
    const VIEWER = "viewer";

    const ROLE_LEVELS = [
        self::ADMIN => 3,
        self::EDITOR => 2,
        self::VIEWER => 1,
    ];
}

// 访问类常量
echo UserRole::ADMIN . "\n";         // admin
echo UserRole::ROLE_LEVELS[1] . "\n"; // 1

// PHP 8.3+ 支持类常量类型声明(PHP 8.3 新增 const expressions in class constants)
class StatusCode
{
    const int OK = 200;
    const int NOT_FOUND = 404;
    const int SERVER_ERROR = 500;
}

final 常量(PHP 8.1+)

PHP 8.1.0 起支持 final 类常量,防止子类覆盖:

php
<?php
declare(strict_types=1);

class BaseConfig
{
    final public const VERSION = "1.0.0";
    public const MAX_RETRIES = 3;
}

class AppConfig extends BaseConfig
{
    // const VERSION = "2.0.0";  // Error: 不能覆盖 final 常量
    public const MAX_RETRIES = 5; // OK:可以覆盖非 final 常量
}

实战示例

配置常量文件

php
<?php
declare(strict_types=1);

// config/constants.php
const APP_ENV = "production";
const APP_DEBUG = false;
const APP_URL = "https://myapp.example.com";
const APP_TIMEZONE = "Asia/Shanghai";

const DB_CONNECTION = "mysql";
const DB_HOST = "localhost";
const DB_PORT = 3306;
const DB_DATABASE = "myapp";
const DB_USERNAME = "root";
const DB_PASSWORD = "";

const CACHE_DRIVER = "redis";
const CACHE_PREFIX = "myapp_";
const CACHE_TTL = 3600;

const LOG_CHANNEL = "daily";
const LOG_LEVEL = "warning";

// 使用常量构建 DSN
const DB_DSN = DB_CONNECTION . ":host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_DATABASE;

动态常量管理

php
<?php
declare(strict_types=1);

class ConstantManager
{
    private static array $runtimeConstants = [];

    /**
     * 安全定义运行时常量
     */
    public static function set(string $name, mixed $value, bool $allowOverride = false): void
    {
        if (defined($name) && !$allowOverride) {
            throw new RuntimeException("Constant '{$name}' is already defined");
        }

        if (!define($name, $value)) {
            throw new RuntimeException("Failed to define constant '{$name}'");
        }

        self::$runtimeConstants[$name] = $value;
    }

    /**
     * 批量设置常量
     */
    public static function batchSet(array $constants, bool $allowOverride = false): void
    {
        foreach ($constants as $name => $value) {
            self::set($name, $value, $allowOverride);
        }
    }

    /**
     * 获取所有运行时常量
     */
    public static function getAll(): array
    {
        return self::$runtimeConstants;
    }
}

// 使用
ConstantManager::batchSet([
    "SMTP_HOST" => "smtp.example.com",
    "SMTP_PORT" => 587,
    "SMTP_USER" => "noreply@example.com",
]);

echo SMTP_HOST . "\n"; // smtp.example.com

使用 get_defined_constants() 检查常量

php
<?php
declare(strict_types=1);

// 获取所有已定义的常量
$allConstants = get_defined_constants(true);

// 按类别分类输出
$categories = ["user" => [], "internal" => [], "core" => []];
foreach ($allConstants as $category => $constants) {
    $count = count($constants);
    echo "Category: {$category} ({$count} constants)\n";
}

// 检查特定常量
$userConstants = get_defined_constants(false);
echo "User defined constants: " . count($userConstants) . "\n";

注意事项

1. 常量命名规范

推荐使用全大写字母和下划线的命名方式:

php
<?php
declare(strict_types=1);

// 推荐
const MAX_FILE_SIZE = 5242880;
const DEFAULT_LANGUAGE = "zh-CN";

// 类常量推荐使用驼峰(PSR-12)
class HttpStatus
{
    const OK = 200;
    const NOT_FOUND = 404;
}

2. 常量与变量的命名空间

常量和变量在不同的命名空间中,true$TRUE 是不同的:

php
<?php
declare(strict_types=1);

define("MY_CONST", 100);
$myConst = 200;
echo MY_CONST . "\n";  // 100
echo $myConst . "\n";  // 200

3. 优先使用 const

在可以使用 const 的场景下(如全局常量、类常量),优先使用 const,因为它在编译时定义,性能更好。

最佳实践

  1. 全局常量使用 const:编译时定义,性能更优
  2. 需要条件定义时使用 define():运行时动态场景
  3. 类常量使用 const:封装在类中,语义更清晰
  4. 使用 defined() 检查常量:避免直接使用未定义常量
  5. 全大写命名:全局常量用 UPPER_SNAKE_CASE
  6. 避免魔法数字:将固定值定义为有意义的常量
  7. 分组管理常量:使用类封装相关常量(如枚举类)

下一节

了解了常量的语法和定义方式之后,接下来将学习 PHP 提供的预定义常量——那些由 PHP 核心和扩展库内置的、可直接使用的常量。请阅读 预定义常量

参考链接