魔术方法
魔术方法是特殊的方法,当对对象执行某些操作时,它们会覆盖PHP的默认操作。
注意
所有以__开头的函数名都被PHP保留。因此,除非要覆盖PHP的行为,否则不建议使用此类函数名。
以下函数名被认为是魔术方法:__construct(),__destruct(),__call(),__callStatic(),__get(),__set(),__isset(),__unset(),__sleep(),__wakeup(),__serialize(),__unserialize(),__toString(),__invoke(),__set_state(),__clone() 和 __debugInfo()。
警告
除__construct(),__destruct() 和 __clone()之外的所有魔术方法 *必须* 声明为 public,否则会发出 E_WARNING 警告。在PHP 8.0.0之前,对于魔术方法 __sleep(),__wakeup(),__serialize(),__unserialize() 和 __set_state() 不会发出任何诊断信息。
警告
如果在魔术方法的定义中使用了类型声明,则它们必须与本文档中描述的签名相同。否则,会发出致命错误。在PHP 8.0.0之前,不会发出任何诊断信息。但是,__construct() 和 __destruct() 不能声明返回类型;否则会发出致命错误。
__sleep() 和 __wakeup()
public __sleep(): array
public __wakeup(): void
serialize() 检查类是否具有名为 __sleep() 的魔术方法。如果存在,则在任何序列化之前都会执行该函数。它可以清理对象,并应返回一个数组,其中包含应序列化的该对象所有变量的名称。如果该方法没有返回任何内容,则会序列化 null 并发出 E_NOTICE 通知。
注意:
__sleep() 无法返回父类中私有属性的名称。这样做会导致 E_NOTICE 级别的错误。请改用 __serialize()。
注意:
从PHP 8.0.0开始,从__sleep()返回非数组的值会生成警告。以前,它会生成通知。
__sleep() 的预期用途是提交挂起的数据或执行类似的清理任务。此外,如果非常大的对象不需要完全保存,则该函数非常有用。
相反,unserialize() 检查是否存在名为 __wakeup() 的魔术方法。如果存在,此函数可以重建对象可能拥有的任何资源。
__wakeup() 的预期用途是重新建立在序列化期间可能丢失的任何数据库连接并执行其他重新初始化任务。
示例 #1 休眠和唤醒
dsn = $dsn; $this->username = $username; $this->password = $password; $this->connect(); } private function connect() { $this->link = new PDO($this->dsn, $this->username, $this->password); } public function __sleep() { return array('dsn', 'username', 'password'); } public function __wakeup() { $this->connect(); }}?>
__serialize() 和 __unserialize()
public __serialize(): array
public __unserialize(array $data): void
serialize() 检查类是否具有名为 __serialize() 的魔术方法。如果存在,则在任何序列化之前都会执行该函数。它必须构造并返回一个键值对的关联数组,这些键值对表示对象的序列化形式。如果未返回数组,则会抛出 TypeError 异常。
注意:
如果 __serialize() 和 __sleep() 都在同一个对象中定义,则只调用 __serialize()。 __sleep() 将被忽略。如果对象实现了 Serializable 接口,则接口的 serialize() 方法将被忽略,并改用 __serialize()。
__serialize() 的预期用途是定义对象的序列化友好的任意表示形式。数组的元素可能对应于对象的属性,但这并不是必需的。
相反,unserialize() 检查是否存在名为 __unserialize() 的魔术方法。如果存在,此函数将接收从 __serialize() 返回的已恢复数组。然后,它可以根据需要从该数组中恢复对象的属性。
注意:
如果 __unserialize() 和 __wakeup() 都在同一个对象中定义,则只调用 __unserialize()。 __wakeup() 将被忽略。
注意:
此功能从 PHP 7.4.0 开始可用。
示例 #2 序列化和反序列化
dsn = $dsn; $this->username = $username; $this->password = $password; $this->connect(); } private function connect() { $this->link = new PDO($this->dsn, $this->username, $this->password); } public function __serialize(): array { return [ 'dsn' => $this->dsn, 'user' => $this->username, 'pass' => $this->password, ]; } public function __unserialize(array $data): void { $this->dsn = $data['dsn']; $this->username = $data['user']; $this->password = $data['pass']; $this->connect(); }}?>
__toString()
public __toString(): string
__toString() 方法允许类决定当它被当作字符串处理时如何反应。例如,echo $obj; 将打印什么。
警告
从 PHP 8.0.0 开始,返回值遵循标准的 PHP 类型语义,这意味着如果可能并且禁用了严格类型,它将被强制转换为string。
如果启用了严格类型,Stringable 对象将*不会*被string 类型声明接受。如果需要这种行为,类型声明必须通过联合类型接受Stringable 和string。
从 PHP 8.0.0 开始,任何包含 __toString() 方法的类也将隐式实现 Stringable 接口,因此将通过该接口的类型检查。尽管如此,建议显式实现该接口。
在 PHP 7.4 中,返回值*必须*是 string,否则将抛出 Error。
在 PHP 7.4.0 之前,返回值*必须*是 string,否则将发出致命的 E_RECOVERABLE_ERROR。
警告
在 PHP 7.4.0 之前,无法从 __toString() 方法中抛出异常。这样做会导致致命错误。
示例 #3 简单示例
foo = $foo; } public function __toString() { return $this->foo; }}$class = new TestClass('Hello');echo $class;?>
以上示例将输出
Hello
__invoke()
__invoke( ...$values): mixed
当脚本尝试将对象作为函数调用时,将调用 __invoke() 方法。
示例 #4 使用 __invoke()
以上示例将输出
int(5)
bool(true)
示例 #5 使用 __invoke()
key = $key; } public function __invoke(array $a, array $b): int { return $a[$this->key] <=> $b[$this->key]; }}$customers = [ ['id' => 1, 'first_name' => 'John', 'last_name' => 'Do'], ['id' => 3, 'first_name' => 'Alice', 'last_name' => 'Gustav'], ['id' => 2, 'first_name' => 'Bob', 'last_name' => 'Filipe']];//按名字排序客户usort($customers, new Sort('first_name'));print_r($customers);//按姓氏排序客户usort($customers, new Sort('last_name'));print_r($customers);?>
以上示例将输出
Array
(
[0] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
)
Array
(
[0] => Array
(
[id] => 1
[first_name] => John
[last_name] => Do
)
[1] => Array
(
[id] => 2
[first_name] => Bob
[last_name] => Filipe
)
[2] => Array
(
[id] => 3
[first_name] => Alice
[last_name] => Gustav
)
)
__set_state()
static __set_state(array $properties): object
这个静态方法被 var_export()导出的类调用。
此方法的唯一参数是一个数组,其中包含以 ['property' => value, ...] 形式导出的属性。
示例 #6 使用 __set_state()
var1 = $an_array['var1']; $obj->var2 = $an_array['var2']; return $obj; }}$a = new A;$a->var1 = 5;$a->var2 = 'foo';$b = var_export($a, true);var_dump($b);eval('$c = ' . $b . ';');var_dump($c);?>
以上示例将输出
string(60) "A::__set_state(array(
'var1' => 5,
'var2' => 'foo',
))"
object(A)#2 (2) {
["var1"]=>
int(5)
["var2"]=>
string(3) "foo"
}
注意:导出对象时,var_export() 不会检查对象的类是否实现了 __set_state(),因此如果未实现 __set_state(),则重新导入对象将导致 Error 异常。尤其是在某些内部类中,这种情况会受到影响。 程序员有责任验证只有那些类实现了 __set_state() 的对象才会被重新导入。
__debugInfo()
__debugInfo(): 数组
此方法由 var_dump() 在转储对象时调用,以获取应显示的属性。如果对象上未定义此方法,则将显示所有公共、受保护和私有属性。
示例 #7 使用 __debugInfo()
prop = $val; } public function __debugInfo() { return [ 'propSquared' => $this->prop ** 2, ]; }}var_dump(new C(42));?>
以上示例将输出
object(C)#1 (1) {
["propSquared"]=>
int(1764)
}