PHP 类用 class Name { ... } 声明,里面包含属性(变量)和方法(函数)。new Name() 用它创建一个对象,-> 用来读取属性或调用方法:$user->name、$user->greet()。
类是蓝图,对象是按蓝图造出来的一个具体东西。同一个类可以创建任意多个对象,每个对象都有自己的属性值。
属性和方法
属性用一个访问控制关键字声明,类型和默认值可写可不写。方法就是类里面的函数。在方法内部,$this 是调用该方法的对象,所以 $this->balance 读取的是这个对象的余额。
每个对象都有自己的 $balance:往 $a 里存钱不会影响 $b。在 new 之后逐个设置属性很繁琐,构造函数可以直接把初始值作为参数接收。
public、private 和 protected
访问控制决定了谁可以访问属性或调用方法:
| 关键字 | 可以访问的位置 |
|---|---|
public | 任何地方 |
protected | 类本身以及继承它的类 |
private | 只有声明它的类 |
把属性设为 private,再对外提供方法,类就能保护自己的规则。下面的例子中,余额只能通过 deposit() 和 withdraw() 修改,而这两个方法会拒绝不合法的金额:
在类外部读取私有属性不是警告,而是 Error:
类型化属性和未初始化的值
没有默认值的类型化属性一开始处于“未初始化”状态,这和 null 不一样。在赋值之前读取它会抛出 Error。解决办法是给它一个默认值、在构造函数中赋值,或者用 ?Type 和 = null 把它变成可为 null 的类型。
类型在赋值时检查。在默认的非严格模式下,$p->stock = '7' 会把数字字符串转换成 7,而 $p->stock = 'many' 会抛出 TypeError。
readonly 只读属性
readonly 属性(PHP 8.1)只能在类内部写入一次,之后就再也不能修改。它是构建金额、ID 这类值对象的常用方式。PHP 8.2 新增了 readonly class,让类的所有属性都变成只读。
只读属性必须声明类型,并且不能有默认值。如果需要一个修改过的副本,就创建一个新对象(new Order('B-2002')),而不是去改旧的对象。
用 __toString 输出对象
只有类定义了 __toString(),echo $object 才能正常工作。每当对象被当作文本使用时,比如在 echo 中、字符串拼接中或 "{$object}" 中,PHP 就会使用这个方法返回的字符串。
如果没有 __toString,echo $object 会抛出 Error(“Object of class Money could not be converted to string”)。调试时,print_r() 和 var_dump() 适用于任何对象,还会显示私有属性,参见 var_dump 和 print_r。
赋值对象并不会复制它
这是从数组转过来的人最容易踩的坑。赋值数组会复制整个数组,赋值对象只复制句柄:之后两个变量指向同一个对象。
clone 做的是浅拷贝:如果某个属性保存着另一个对象,两个副本仍然共享这个内部对象。=== 只有在是同一个对象时才为 true,而 == 比较的是同一个类的两个对象的属性值。把对象传给函数和赋值的效果一样,所以函数可以修改它收到的对象。
常见问题
PHP 中怎么用类创建对象?
用 new 加上类名:$user = new User();。如果类有构造函数,就把参数写在括号里,例如 new User('Ada')。之后用箭头运算符调用方法和读取属性:$user->name、$user->greet()。
PHP 中的 $this 是什么意思?
$this 是调用当前方法的那个对象。在 $ada->greet() 内部,$this 就是 $ada,所以 $this->name 读取的是 Ada 的名字。它只存在于非静态方法中。
PHP 中 public、private 和 protected 有什么区别?
public 成员在任何地方都能使用,private 成员只能在声明它的类内部使用,protected 成员可以在该类及其子类中使用。从外部读取私有属性会抛出 Error:Cannot access private property。
PHP 中对象是按引用传递的吗?
变量保存的是指向对象的句柄,而不是对象本身。$b = $a; 复制的是句柄,所以两个变量指向同一个对象,通过 $b 做的修改会在 $a 中体现。要得到一个独立的副本,请使用 $b = clone $a;。
PHP 中的 readonly 属性是什么?
声明为 public readonly string $id; 的属性(PHP 8.1+)只能在类内部赋值一次,之后再也不能修改。第二次写入会抛出 Error:Cannot modify readonly property。