应将业务敏感属性设为private并配setter校验,protected仅限子类复用而非封装,PHP8.2 readonly class可彻底禁止修改,常量需按访问范围合理设为private/protected/public。

直接改 $obj->property 会绕过所有校验逻辑
当你把属性设为 public,外部代码就能跳过构造函数、setter 方法和任何业务规则,直接赋值。比如一个 User 类要求邮箱必须合法,但 $user->email = "xxx"; 会完全绕过 filter_var($email, FILTER_VALIDATE_EMAIL) 校验。
常见错误现象:
-
PHP Warning: Cannot assign to private property User::$email—— 这其实是好事,说明封装起作用了 - 没报错但数据已脏:比如
$user->age = -5;或$user->status = "deleted";却没触发状态机约束
实操建议:
- 所有业务敏感属性一律用
private,哪怕只是临时字段 - 用
public function setEmail(string $email): void替代裸赋值,校验写在方法里 - PHP 7.4+ 可配合类型声明:
private string $email;,让非法类型在运行时直接抛TypeError
protected 不是“半公开”,它只对继承链有效
很多人误以为 protected 是“内部可用、外部不可用”的折中方案,其实它只解决子类复用问题,不解决封装问题。子类仍可随意读写该属性,一旦子类逻辑出错,父类状态就可能被污染。
使用场景:
- 基类定义通用字段(如
protected int $id;),供多个子类统一管理生命周期 - 需要被子类覆盖的默认行为(如
protected function validate(): bool)
容易踩的坑:
- 把本该
private的配置项写成protected,结果被子类意外重置 - 子类中直接修改
$this->cache而非调用$this->clearCache(),导致缓存一致性丢失
PHP 8.2 的 readonly class 是终极状态防护
如果你的需求是“初始化后彻底禁止修改”,readonly class 比手写一堆 private + getter 更可靠。它在语言层强制拦截所有写操作,连反射都绕不过去。
性能与兼容性影响:
- PHP 8.2+ 才支持,低于此版本会解析失败
- 类内所有属性自动变为只读,无需逐个加
readonly修饰符 - 不能有
__set()或动态属性,否则报Fatal error: Cannot modify readonly property
示例:
readonly class Point {
public function __construct(
public int $x,
public int $y,
) {}
}
$point = new Point(1, 2);
// $point->x = 3; // Fatal error
常量也得管住访问范围
PHP 7.1+ 支持给类常量加 private 和 protected,但很多人还在用全 public const。这会导致内部实现细节(如重试次数 const MAX_RETRY = 3)被外部硬编码依赖,一改就崩。
实操建议:
- 仅将真正跨模块共享的值设为
public const(如 HTTP 状态码STATUS_OK = 200) - 算法参数、超时阈值、调试开关等一律
private const - 避免在常量名里暴露实现,比如不用
DB_DRIVER_PDO,改用语义化名称STORAGE_LOCAL
最易被忽略的一点:private 常量无法被 IDE 自动补全到外部调用处——这反而是好事,说明你没把它当公共契约用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











