访问控制(可见性)
对属性或方法的访问控制(PHP 7.1后支持常量),是通过在前面添加关键字public (公有)、protected 、 private 来实现。公有的任意可见,受保护的可被自身及其子类和父类访问,私有的只能被其定义所在的类访问
属性的访问控制
类属性可以定义为public,private或者protected 。在没有任何访问控制关键字的情况下,属性声明为public
不对称属性可见性
从PHP 8.4开始,属性也可以设置不对称的可见性,读取(get)和写入(set)可以有不同的范围,准确的说可以单独指定set可见性,只要它不比默认可见性更宽。
当一个类继承另一个类时,子类可以重新定义任何不是final的属性。这样做时,可以扩大主要可见性或set可见性,只要新的可见性和父类相同或者更宽,需要注意的是,若一个private属性被重写,它实际上并没有改变父类的属性,而是创建了一个具有不同内部名称的新属性。
方法的访问控制
类中的方法可以被定义为public、private、protected。如果没设置默认为public
魔术方法与可见性
通过实现
__get()
、__set()
等魔术方法,可以动态访问private
或未定义的属性。魔术方法不会改变实际的属性可见性,但会提供访问入口,常用于封装和懒加载。
抽象方法与可见性
抽象方法只能声明为
public
或protected
,不能是private
。抽象方法必须在抽象类中定义,由子类实现具体功能。
常量的控制访问
自PHP 7.1后 , 类的常量可以定义为,public、private 或 protected ,没设置就默认public
其它对象的访问控制
同一个类的对象即使不是同一个实例也可以互相访问对方的private与protected成员。
接口中的方法可见性
接口中的方法默认都是
public
,不能声明为protected
或private
。实现接口时,方法必须保持为
public
,否则会报错。
对象继承
PHP的对象模型使用了继承,继承可以影响到类与类,对象与对象之间的关系。
比如,当扩展一个类,子类就会继承父类所有public和protected的方法,属性和常量。除非子类覆盖了父类的方法,被继承的方法都会保留其原有功能。
继承有助于功能的设计和抽象,在实现类似的对象、增加新功能时,无须重复编写这些公用的功能
子类无法访问父类的私有方法。因此,子类无需考虑正常的继承规则而重新实现私有方法。然而,在PHP 8.0前,final和static 的限制会应用于private方法。自PHP 8.0起,仅private final 的构造器是唯一受限的private方法;想要“禁用”构造器,通常使用静态工厂方法作为代替。
方法,属性和常量的可见性可以放宽,例如protected方法可以标记为public,但不能增加限制,例如标记public属性为private。
使用 final
限制继承或重写
使用
final
可以防止某个方法被子类覆盖,或防止整个类被继承。final
不改变可见性,但会影响继承层级的可扩展性。从 PHP 8.0 起,
private
方法(除构造器外)不会受final
约束。
Trait(特征)与可见性
Trait 可用于多个类共享属性或方法。
Trait 中可以使用
public
、protected
、private
来声明方法可见性。如果多个 Trait 有同名方法,需使用
insteadof
或as
解决冲突,且可改变方法可见性。
返回类型与内部类兼容
PHP 8.1前,大多数内部类或方法没有声明其返回类型,并且在继承它们是允许返回任何类型。
自PHP 8.1起 大多数内部方法开始“暂时”声明其返回类型,在这种情况下,方法的返回类型应该与继承的父级方法兼容,否则,将发出弃用通知。注意,没有指定返回声明也会视为签名不匹配,从而导致弃用通知。
反射 API 可突破可见性
PHP 的
ReflectionProperty
、ReflectionMethod
等允许在运行时读取和修改private
或protected
成员。通过
setAccessible(true)
可访问原本不可见的属性或方法,适用于单元测试或框架内部。
封装示例(Best Practice)
展示如何组合 private + getter/setter + readonly
class Product {
private float $price;
public function __construct(float $price) {
$this->price = $price;
}
public function getPrice(): float {
return $this->price;
}
protected function setPrice(float $price): void {
$this->price = $price;
}
}