private属性+类型声明可阻断非法赋值,PHP 7.4+中private string $name等声明使错误在运行时立即抛TypeError;须配合getter/setter中的业务校验(如年龄非负、邮箱格式)才能实现真正封装。

private 属性 + 类型声明,堵死非法赋值入口
PHP 7.4+ 支持在 private 属性上直接加类型,比如 private string $name;。这不只是语法糖——它让非法赋值在运行时立刻抛出 TypeError,而不是静默失败或靠文档提醒。没有类型声明的 private 属性,外部虽不能直接访问,但通过反射、__set() 魔术方法(如果开放了)、甚至序列化/反序列化仍可能绕过校验。
- 必须用
private,不用protected或public—— 后两者都允许子类或外部代码间接污染状态 - 标量类型(
string、int、bool)和类类型(如private DateTime $createdAt;)都受保护 - 避免用
mixed或不声明类型,那等于把封装的门虚掩着
getter/setter 里做逻辑校验,不止是“转发”
很多人写 getName() 和 setName($name) 只是简单读写,这没真正利用封装。真正的封装是在 setter 里加约束,在 getter 里做脱敏或计算。比如用户年龄不能为负、邮箱必须格式合法、密码字段返回 *** 而不是明文。
- setter 中不要直接赋值:
$this->age = $age;,而要先判断:if ($age - getter 不一定返回原始值:比如
getCreatedAt(): string可以返回格式化后的日期字符串,隐藏DateTime实例细节 - 避免在 getter 中触发副作用(如查询数据库),否则调用方会误以为是轻量操作
别让构造函数暴露内部结构
构造函数参数如果直接对应私有属性,等于把内部字段设计“泄漏”给了外部。例如 __construct(string $name, int $age, array $roles) 让调用方知道这个类内部存了 $roles 数组,还知道它是数组类型。一旦将来改成用 RoleCollection 对象管理角色,所有调用点都要改。
- 构造参数尽量抽象:用 DTO、Value Object 或接口类型,比如
__construct(UserName $name, UserAge $age, RoleSet $roles) - 或者只接受一个高阶对象:
__construct(UserData $data),把解析逻辑收进类内 - 禁止在构造函数里做重操作(如连接数据库、读文件),那会让依赖变得隐式且不可测
警惕魔术方法和序列化破环封装
__get()、__set()、__sleep()、__wakeup() 这些魔术方法如果没严格控制,会成为封装漏洞。尤其 serialize() / unserialize() 默认暴露所有属性,包括 private 的。
立即学习“PHP免费学习笔记(深入)”;
- 除非明确需要,否则不要实现
__get/__set;若必须实现,只允许访问白名单属性,并做类型检查 - 用
__sleep()显式返回需序列化的属性名数组,排除敏感字段(如passwordHash) - 在
__wakeup()中重置可能失效的资源(如关闭的数据库连接),而不是假设反序列化后状态完好
private,而是当别人想“临时绕过”校验调试时,你有没有守住边界——比如拒绝在测试中用反射修改私有属性,坚持走公共接口。封装的强度,取决于你每次妥协的代价。



















