它的本质是:**类成员(Properties 和 Methods)是封装在 类作用域 (Class Scope) 内的 数据 (State)行为 (Behavior)

  • 属性 (Properties):是对象的 静态状态容器,存储在堆内存中,伴随对象实例存在。
  • 方法 (Methods):是对象的 动态行为指令,存储在代码段中,通过 this 指针绑定到具体实例。
  • 可见性 (Visibility):是 封装的防火墙,决定了谁有权读写状态或调用行为。
  • 静态成员 (Static):是 类级别的共享资源,不属于任何实例,存在于全局符号表中。

如果把类比作一家公司

  • 类 (Class):是 公司章程 + 办公室蓝图
  • 实例 (Object):是 实际运营的公司实体
  • 属性 (Properties):是 公司的资产和档案(现金、员工名单、客户数据)。
    • public:前台展示柜,任何人都能看。
    • private:CEO 保险箱,只有内部高层能开。
    • protected:部门经理文件夹,子公司(子类)也能看。
  • 方法 (Methods):是 员工的岗位职责(销售、研发、财务)。
    • $this:是 当前正在执行任务的员工 ID
  • 静态成员 (Static):是 公司品牌 Logo 和注册号码
    • 不管开多少家分公司(实例),Logo 只有一个,存在总部(类级别)。
  • 核心逻辑别把家底(属性)随便露在外面,也别让外人(外部代码)直接指挥员工(方法)。通过接口(Public Methods)进行受控交互。

一、属性 (Properties):状态的持久化

1. 定义与初始化
  • 声明:必须使用关键字 var (PHP4遗留), public, protected, private
  • 默认值:可以是标量、数组、null不能是表达式或资源(如 public $db = new PDO(...) ❌)。
  • 初始化时机
    • 声明时:编译期确定默认值。
    • 构造函数中:运行时动态赋值。
    • PHP 8.0+ 构造函数提升public function __construct(private string $name) {}
2. 动态属性 (Dynamic Properties) - PHP 8.2+ 已废弃
  • 现象$obj->newProp = 'value';
  • 本质:在实例的哈希表中动态添加键值对。
  • 风险
    • 拼写错误隐蔽$user->nmae 不会报错,而是创建新属性,导致 Bug。
    • 性能差:无法优化内存布局。
    • 对策:使用 #[AllowDynamicProperties] (仅用于兼容) 或改用 数组/DTO
3. 类型声明 (Typed Properties) - PHP 7.4+
  • 语法public int $id;
  • 优势
    • 早期错误检测:赋值类型不符立即报错。
    • OPcache 优化:引擎知道类型,减少 ZVal 转换开销。
    • 未初始化状态:声明了但未赋值的 typed property 处于 “Uninitialized” 状态,访问它会报错。这是防止使用 null 默认值的安全机制。

💡 核心洞察属性是对象的记忆。类型声明是给记忆加锁,防止存错东西。


二、方法 (Methods):行为的执行

1. $this 指针
  • 本质:指向 当前调用该方法的对象实例
  • 作用:在方法内部访问其他属性和方法。
  • 静态方法中没有 $this。因为静态方法不属于任何实例。
2. 魔术方法 (Magic Methods)
  • __construct / __destruct:生与死。
  • __get / __set:拦截不可访问属性的读写(实现重载 Overloading)。
  • __call / __callStatic:拦截不可访问方法的调用。
  • __invoke:让对象可像函数一样调用。
  • 价值:提供 钩子 (Hooks),允许框架(如 Laravel/Hyperf)实现 ORM、代理、AOP 等高级功能。
3. 引用传递 vs. 值传递
  • 对象参数:默认通过 对象标识符 (Object Identifier) 传递。
    • 修改对象内部状态会影响原对象。
    • 但重新赋值变量 ($obj = new Other()) 不会影响原变量。
  • 标量参数:默认 值传递。需 &$param才能引用传递。

三、可见性 (Visibility):封装的防火墙

关键字 访问权限 隐喻 适用场景
public ** everywhere** 公司官网 API 接口、对外服务方法。
protected 自身 + 子类 内部内网 模板方法模式、供子类扩展的逻辑。
private 仅自身 CEO 日记 内部辅助函数、敏感数据、实现细节。
1. 封装原则
  • 最小暴露原则:默认 private,除非必要才 protected,最后才 public
  • Getter/Setter:通过方法控制访问,而非直接暴露属性。
    • 优势:可以在 Setter 中加入验证逻辑(如年龄不能为负)。
2. 继承中的可见性
  • 规则:子类可以访问父类的 publicprotected 成员,但不能访问 private
  • 重写 (Override):子类可以重写父类的 public/protected 方法,但不能降低可见性(如不能把 public 改为 private)。

四、静态成员 (Static Members):类级别的共享

1. 静态属性 (static $count)
  • 存储:存在于 类结构 (zend_class_entry) 中,而非对象实例中。
  • 生命周期:脚本启动时初始化,脚本结束时销毁。
  • 用途:计数器、单例实例、配置缓存。
  • 陷阱:在 Swoole/Hyperf 常驻内存环境中,静态属性会 永久保留,导致内存泄漏或数据污染(不同请求共享同一静态变量)。
    • 对策:在协程环境中慎用静态属性,或使用 Context 隔离。
2. 静态方法 (static function)
  • 特点:不依赖 $this,不能访问非静态属性。
  • 用途:工具函数、工厂方法 (create())。
  • 后期静态绑定 (static::)
    • self:::指向定义该方法的类。
    • static:::指向 调用该方法的类(运行时决定)。
    • 价值:解决继承中的静态方法多态问题。

五、认知牢笼:常见误区

1. 误区:“私有属性不能被外部读取,所以绝对安全。”
  • 真相
    • 反射 (Reflection) 可以强制访问私有成员。
    • 序列化/反序列化 可能绕过构造函数。
    • 对策:不要依赖可见性做安全隔离,它只是代码规范。
2. 误区:“静态方法比实例方法快。”
  • 真相
    • 差异微乎其微。
    • 静态方法难以 Mock,不利于单元测试。
    • 对策:优先使用实例方法,除非确实是纯工具函数。
3. 误区:“属性越多,对象越强大。”
  • 真相
    • 属性多意味着 状态复杂,难以维护。
    • 对策:遵循 单一职责原则 (SRP)。如果一个类有超过 5-7 个属性,考虑拆分类或引入 Value Object。
4. 误区:“$this 可以在静态方法中使用。”
  • 真相
    • Fatal Error。静态方法没有上下文实例。
    • 对策:如果需要操作实例,请传入实例作为参数,或使用非静态方法。
5. 误区:“PHP 8.2 废弃动态属性是因为它没用。”
  • 真相
    • 是因为它 有害(隐藏 Bug、性能差)。
    • 对策:使用 关联数组stdClass 代替动态属性需求。

🚀 总结:原子化“PHP 类成员”全景图

维度 关键点
本质 封装在类作用域内的状态与行为
属性 堆内存存储、类型声明、避免动态属性
方法 代码段存储、$this 绑定、魔术方法钩子
可见性 Public(公开)/Protected(继承)/Private(内部)
静态 类级别共享、常驻内存风险、后期静态绑定
PHP 隐喻 Company Assets (Props) & Job Roles (Methods)
公式 Object = State (Props) + Behavior (Methods) ^ Encapsulation

终极心法

类成员的本质,是“秩序与边界”。
属性是私密的记忆,方法是公开的承诺。
守住边界,才能构建复杂的系统。
于封装中见安全,于静态见共享;以可见性为尺,解混乱之牛,于对象设计中,求严谨之真。

行动指令

  1. 审计属性:检查项目中是否有 public 属性,尝试改为 private 并提供 Getter。
  2. 启用严格类型:为所有属性添加类型声明 (string, int, ?User)。
  3. 清理动态属性:搜索 $obj->xyz = ...,替换为正式属性或数组。
  4. 慎用静态:在 Hyperf/Swoole 项目中,审查所有 static 变量,确保不会造成请求间污染。
  5. 思维升级:记住,好的类设计,是让使用者只需关心 Public 接口,而无需窥探内部实现。

更多推荐