
php 8.1 引入 readonly 属性后,开发者常困惑于它与已有 class constants 的分工边界;二者核心差异在于:常量是编译期静态绑定、全局唯一、不可实例化访问;而 readonly 属性支持运行时初始化、可实例差异化赋值、需对象上下文访问。
php 8.1 引入 readonly 属性后,开发者常困惑于它与已有 class constants 的分工边界;二者核心差异在于:常量是编译期静态绑定、全局唯一、不可实例化访问;而 readonly 属性支持运行时初始化、可实例差异化赋值、需对象上下文访问。
在 PHP 面向对象开发中,const(类常量)和 readonly 属性虽都体现“不可变性”,但设计目标与使用场景截然不同。理解二者本质差异,是写出清晰、可维护代码的关键。
✅ 本质差异一览表
| 维度 | 类常量(const) | readonly 属性 |
|---|---|---|
| 定义时机 | 编译期必须确定值(字面量或 PHP 8.2+ 支持的 const 表达式) | 运行期构造函数中动态赋值(支持函数调用、配置读取、依赖注入等) |
| 作用域 | 全局唯一,所有实例共享同一值 | 每个实例可拥有独立值(非 static) |
| 访问方式 | ClassName::CONST_NAME(无需实例)或 self::CONST_NAME | $obj->readonlyProp(必须通过实例);static readonly 需 ClassName::$prop |
| 可见性 | 支持 public/protected/private(PHP 7.1+) | 同样支持完整访问控制修饰符 |
| 类型系统 | 不参与属性类型声明(常量无类型) | 支持完整 PHP 类型声明(如 readonly public string $name;) |
| 序列化 | 不参与 serialize()(常量本身不被序列化) | readonly 属性正常参与序列化(PHP 8.2+ 明确支持) |
? 实际代码对比
class ServiceConfig
{
// ✅ 常量:适合绝对不变的元信息(如协议版本、固定标识)
public const API_VERSION = 'v3';
private const MAX_RETRY = 3;
// ✅ readonly 属性:适合依赖运行时上下文的不可变配置
public readonly string $baseUrl;
public readonly int $timeout;
public readonly bool $isProduction;
public function __construct(
string $env,
array $config = []
) {
$this->baseUrl = $config['base_url'] ?? 'https://api.example.com';
$this->timeout = $config['timeout'] ?? 5000;
$this->isProduction = $env === 'prod';
// ❌ 下面会触发 Fatal Error:Cannot modify readonly property
// $this->baseUrl = 'hacked';
}
}
// 使用示例:
$config1 = new ServiceConfig('dev', ['base_url' => 'https://dev.api.test']);
$config2 = new ServiceConfig('prod', ['base_url' => 'https://api.prod']);
var_dump($config1->baseUrl); // "https://dev.api.test"
var_dump($config2->baseUrl); // "https://api.prod" ← 各自独立!
var_dump(ServiceConfig::API_VERSION); // "v3" ← 所有实例共享
? 何时选择哪一种?
-
优先用 const 当:
- 值在编译期已完全确定(如 public const STATUS_PENDING = 'pending';);
- 表达领域内普适规则(如 HTTP 状态码、数学常量 PI);
- 需跨实例统一、且绝不允许差异化(如 public const ENCODING = 'UTF-8';)。
-
优先用 readonly 当:
- 值需在构造时根据参数、环境或依赖动态计算(如 $this->cacheKey = md5($prefix . $id););
- 每个对象应持有专属不可变状态(如实体 ID、创建时间戳、授权 token);
- 需类型安全 + IDE 支持 + 序列化兼容性;
- 替代过去滥用 private $prop; + __get() 或文档约定“只读”的脆弱模式。
⚠️ 注意事项
- readonly 不能用于静态属性(除非显式声明 public static readonly int $count; —— PHP 8.2+ 支持);而 const 天然是静态的。
- readonly 属性不可在 clone 后修改,但克隆对象会复制其值(PHP 8.2+ 支持 __clone 中重新赋值 readonly,需手动处理)。
- 常量不支持表达式(PHP 8.2 起部分支持 const X = 1 + 2;),但仍无法调用函数;readonly 则完全无此限制。
- 性能上无显著差异,二者均经引擎优化;选型应以语义清晰性为首要原则。
✅ 总结一句话
用 const 定义“世界规则”,用 readonly 封装“对象事实” —— 前者属于类契约,后者属于实例真相。合理分层,代码自会言之有物。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











