封装的核心是“不让人乱动”,即用private/protected限制直接赋值,强制通过getter/setter等公共方法操作,如withdraw()校验余额而非直接改balance;private仅本类可见,protected含子类,public无限制;适用于需校验、计算或资源管理的字段。

封装不是“不让人看”,而是“不让人乱动”
PHP里的封装,核心不是把代码藏起来不让读,而是用 private 和 protected 拦住直接赋值或调用,逼你走公开定义好的方法路径。比如账户余额不能 $account->balance = -1000,但可以调 $account->withdraw(1000)——后者的内部会校验余额是否充足。
常见错误现象:
- 直接读写
private属性,报错Cannot access private property - 子类误用
private方法,以为能继承,结果调不到 - 把所有属性都设成
public,后期加校验逻辑时发现到处都有直连赋值,改不动
private、protected、public 的实际边界在哪
三者不是“安全等级”,而是访问范围的硬性声明,PHP 解析器会在运行时强制拦截越界访问:
-
private:只在定义它的那个类内部可用(连子类都不行) -
protected:本类 + 所有子类可访问,但外部对象实例不可见 -
public:无限制,但暴露越多,后期重构风险越高
例如:BankAccount 类里 private $balance,子类 SavingsAccount 不能直接读 $this->balance,必须通过父类提供的 getBalance() ——这反而让子类行为更可控。
立即学习“PHP免费学习笔记(深入)”;
什么时候该用封装?别等出问题才补
不是所有类都需要严格封装,但以下场景不加就容易埋雷:
- 涉及状态校验的字段:如用户年龄不能为负、邮箱格式需验证,必须把赋值逻辑收进
setAge()或setEmail() - 计算型属性:如订单总价 = 单价 × 数量 × 折扣,不应暴露
$total可写,而应只提供getTotal() - 资源型操作:如数据库连接、文件句柄,应隐藏
private $pdo,只暴露query()等受控接口
性能影响几乎为零——访问修饰符是语法层控制,不引入额外运行时开销;但过度封装(比如每个 public 属性都配一对 getter/setter)会让代码膨胀,没实际约束意义。
封装后怎么调试和测试
封装不等于拒绝调试,只是换方式:
- 用
var_dump($obj)查看 public 属性,private/protected 字段默认不显示(PHP 8.2+ 可配合Xdebug展开) - 单元测试时,直接调用 public 方法验证行为,而不是绕过封装去测 private 属性值
- 需要临时观察内部状态?加一个
debugInfo()方法返回数组,仅开发环境启用,不破坏封装契约
最容易被忽略的一点:封装的真正价值不在“防人”,而在“防自己”——它迫使你在修改内部实现时,必须审视所有经过的公共接口,避免无意中破坏调用方的预期。一旦开始依赖 private,你就已经接受了“内部可随时重写,只要接口不变”的契约。



















