参考《PHP7内核刨析》,以及php官方文档
PHP魔术方法包括
__construct()
, __destruct()
, __call()
, __callStatic()
, __get()
, __set()
, __isset()
, __unset()
, __sleep()
, __wakeup()
, __toString()
, __invoke()
, __set_state()
, __clone()
和 __debugInfo()
PHP 将所有以
__
(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以__
为前缀。
重载
PHP所提供的重载(overloading)是指动态地创建类属性和方法。我们是通过魔术方法(magic methods)来实现的。
当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。本节后面将使用不可访问属性(inaccessible properties)和不可访问方法(inaccessible methods)来称呼这些未定义或不可见的类属性或方法。
所有的重载方法都必须被声明为 *public*。
Note:
这些魔术方法的参数都不能通过引用传递。
Note:
PHP中的重载与其它绝大多数面向对象语言不同。传统的重载是用于提供多个同名的类方法,但各方法的参数类型和个数不同。
1. __get()
,__set()
,__isset()
__unset()
方法(属性重载)
|
|
在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。
参数 $name
是指要操作的变量名称。__set()
方法的 $value
参数指定了 $name
变量的值。
属性重载只能在对象中进行。在静态方法中,这些魔术方法将不会被调用。所以这些方法都不能被 声明为 static。从 PHP 5.3.0 起, 将这些魔术方法定义为 static 会产生一个警告。
Note:
因为 PHP 处理赋值运算的方式,__set() 的返回值将被忽略。类似的, 在下面这样的链式赋值中,__get() 不会被调用:
1
$a = $obj->b = 8;在除 isset() 外的其它语言结构中无法使用重载的属性,这意味着当对一个重载的属性使用 empty() 时,重载魔术方法将不会被调用。
为避开此限制,必须将重载属性赋值到本地变量再使用 empty()。
Example #1 使用 get(),set(),__isset() 和 __unset() 进行属性重载
|
|
以上例程会输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Setting 'a' to '1' Getting 'a' 1 Is 'a' set? bool(true) Unsetting 'a' Is 'a' set? bool(false) 1 Let's experiment with the private property named 'hidden': Privates are visible inside the class, so __get() not used... 2 Privates not visible outside of class, so __get() is used... Getting 'hidden' Notice: Undefined property via __get(): hidden in <file> on line 70 in <file> on line 29 |
2. __call()
,__callStatic
(方法重载)
public __call ( string $name
, array $arguments
) : mixed
public static __callStatic ( string $name
, array $arguments
) : mixed
在对象中调用一个不可访问方法时,__call() 会被调用。
在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。
$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。
Example #2 使用 __call() 和 __callStatic() 对方法重载
|
|
以上例程会输出:
1 2 |
Calling object method 'runTest' in object context Calling static method 'runTest' in static context |
3. __sleep() 和 __wakeup() ¶
|
|
serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
Note:
__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。
__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
与之相反,unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。
Example #1 Sleep 和 wakeup
|
|
4. __toString() ¶
public __toString ( void ) : string
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
Warning
不能在 __toString() 方法中抛出异常。这么做会导致致命错误。
Example #2 简单示例
1
|
<?php// Declare a simple classclass TestClass{ public $foo; public function __construct($foo) { $this->foo = $foo; } public function __toString() { return $this->foo; }}$class = new TestClass('Hello');echo $class;?> |
以上例程会输出:
1
|
Hello |
需要指出的是在 PHP 5.2.0 之前,__toString() 方法只有在直接使用于 echo 或 print 时才能生效。PHP 5.2.0 之后,则可以在任何字符串环境生效(例如通过 printf(),使用 %s 修饰符),但不能用于非字符串环境(如使用 %d 修饰符)。自 PHP 5.2.0 起,如果将一个未定义 __toString() 方法的对象转换为字符串,会产生 E_RECOVERABLE_ERROR 级别的错误。
5. __invoke() ¶
__invoke ([ $... ] ) : mixed
当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。
Note:
本特性只在 PHP 5.3.0 及以上版本有效。
Example #3 使用 __invoke()
|
|
以上例程会输出:
1 2 |
int(5) bool(true) |
6. __set_state() ¶
static __set_state ( array $properties
) : object
自 PHP 5.1.0 起当调用 var_export() 导出类时,此静态 方法会被调用。
本方法的唯一参数是一个数组,其中包含按 array(‘property’ => value, …) 格式排列的类属性。
Example #4 使用 __set_state()>(PHP 5.1.0 起)
|
|
以上例程会输出:
1 2 3 4 5 6 |
object(A)#2 (2) { ["var1"]=> int(5) ["var2"]=> string(3) "foo" } |
7. __debugInfo() ¶
__debugInfo ( void ) : array
当 var_dump()
被使用时,如果此方法被定义了就会调用此方法。如果这个方法没有被定义var_dump()
会显示类的全部信息。
This feature was added in PHP 5.6.0.
Example #5 Using __debugInfo()
|
|
以上例程会输出:
1 2 3 4 |
object(C)#1 (1) { ["propSquared"]=> int(1764) } |