PHP中的15种魔术方法与8种魔术变量

PHP中的魔术方法是指在特定事件下自动触发的方法,这些方法都有一个共同特征:以“__”两个下划线开始,共15个魔术方法,整理如下

1、__construct

与C++里的构造函数类似,在创建对象的时候被调用,此方法可以初始化一些成员变量,php里可以相应的调用一些文件等。可以使构造函数有一个独一无二的名称,无论其所在的类名是什么,都统一用__construct来初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function getName()
{
return $this->name;
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT->getName();

2、__destruct

析构方法,在销毁对象前调用这个方法,具体的作用是清除占用内存空间并销毁对象相关资源,下面的示例新建对象时name有值,析构后name未定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
echo $this->name;
}
public function __destruct()
{
unset($this->name);
echo $this->name;
}
}

3、__get

当调用一个未定义的属性时访问此方法,下例访问school变量,但由于school未定义,会出现Notice级别的错误,随后调用__get方法,则会看到结果是属性name的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
class iat
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function __get($var)
{
return $this->name;
}
}
$IAT = new iat("iat");
echo @$IAT->$school;

4、__set

给一个不可访问的属性赋值时调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function getGrade()
{
return $this->grade;
}
public function __set($grade, $value)
{
$this->grade = $value;//赋值
}
}
$IAT = new iat("iat", "grade_1");
$IAT->grade = "grade_2";//给不可访问的变量赋值
echo $IAT->getGrade();//获得赋值后的结果

5、__call

当调用一个未定义的方法或权限不足时调用此方法,若未定义__call方法,调用未定义的方法时会给出不太友好的fata error级别的错误提示。用__call方法可以在调用未定义的方法时捕获异常,方便下一步更好的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __call($method, $args)
{
echo "unknow method " . $method;
}
}
$IAT = new iat("iat", "grade_1");
$IAT->getGrade_2();

6、__sleep

串行化的时候用,serialize() 检查类中是否有魔术名称__sleep的函数。如果这样,该函数将在任何序列化之前运行。它可以清除对象并应该返回一个包含有该对象中应被序列化的所有变量名的数组。使用 __sleep 的目的是关闭对象可能具有的任何数据库连接,提交等待中的数据或进行类似的清除任务。此外,如果有非常大的对象而并不需要完全储存下来时此函数也很有用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __sleep()
{
echo "sleep";
}
}
$IAT = new iat("iat", "grade_1");
$IAT_serialize = serialize($IAT);

不过上述代码会报Notice: serialize() [function.serialize]: __sleep should return an array only containing the names of instance-variables to serialize in的错误。原来__sleep()方法调用时一定要返回一个数组,一般都是返回一个关于对象成员变量的数组,上面我们有一个名为name的成员变量,所以我们就在__sleep()方法中返回array(’name’)

不过这样依旧会报错Notice: serialize() [function.serialize ]: “iat” returned as member variable from __sleep() but does not exist in…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __sleep()
{
return array($this->name);
}
}
$IAT = new iat("iat", "grade_1");
$IAT_serialize = serialize($IAT);

若直接return array();就不会报错了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __sleep()
{
return array();
}
}
$IAT = new iat("iat", "grade_1");
$IAT_serialize = serialize($IAT);

这又是什么原因呢?看完下面的__wakeup就会知道原因了

7、__wakeup

反串行化的时候调用,unserialize() 检查具有魔术名称 __wakeup 的函数的存在。如果存在,此函数可以重建对象可能具有的任何资源。

使用 __wakeup 的目的是重建在序列化中可能丢失的任何数据库连接以及处理其它重新初始化的任务。还是上面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __sleep()
{
return array();
}
public function __wakeup()
{
echo "wakeup";
}
public function getName()
{
return $this->name;
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT->getName();
$IAT_serialize = serialize($IAT);
$IAT_unserialize = unserialize($IAT_serialize);
echo $IAT_unserialize->getName();

这样的结果只输出一个iat,并不是想象中的两次getName。若将__sleep里return array($this->name)再看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __sleep()
{
return array(’name’);
}
public function __wakeup()
{
echo "wakeup";
}
public function getName()
{
return $this->name;
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT->getName();
$IAT_serialize = serialize($IAT);
$IAT_unserialize = unserialize($IAT_serialize);
echo $IAT_unserialize->getName();

到这边就显示两次iat了,说明返回这样形式的数组是为了在序列化时保存已经设了值得成员变量的。

8、__clone

PHP5中的对象赋值是使用的引用赋值,如果想复制一个对象则需要使用clone方法,在调用此方法是对象会自动调用__clone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function getName()
{
return $this->name;
}
public function __clone()
{
echo "clone";
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT->getName();
$IAT_copy = clone $IAT;

9、__toString

将一个对象转化成字符串时自动调用,比如使用echo打印对象时。如果类没有实现此方法,则无法通过echo打印对象,

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT;

否则会显示:Catchable fatal error: Object of class test could not be converted to string in此方法必须返回一个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __toString()
{
return "tostring";
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT;

在PHP 5.2.0之前,toString方法只有结合使用echo() 或 print()时 才能生效。PHP 5.2.0之后,则可以在任何字符串环境生效(例如通过printf(),使用%s修饰符),但 不能用于非字符串环境(如使用%d修饰符 )。从PHP 5.2.0,如果将一个未定义toString方法的对象 转换为字符串,会报出一个E_RECOVERABLE_ERROR错误。

10、__autoload

试图使用尚未被定义的类时自动调用。通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。

1
2
3
4
5
function __autoload($classname)
{
include_once($classname . ’.php’);
}
$IAT_class = new iat_class();

注意: 在 __autoload 函数中抛出的异常不能被 catch 语句块捕获并导致致命错误。

11、__isset

当在一个未定义的属性上调用isset()函数时调用此方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __isset($property)
{
echo "123";
}
}
$IAT = new iat("iat", "grade_1");
if(isset($IAT->school))
{
echo "isset school";
}

12、__unset

当在一个未定义的属性上调用unset()函数时调用此方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __unset($property)
{
echo "123";
}
}
$IAT = new iat("iat", "grade_1");
unset($IAT->school);

13、__invoke

当尝试以调用函数的方式调用一个对象时,__invoke 方法会被自动调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
public function __invoke()
{
echo "123";
}
}
$IAT = new iat("iat", "grade_1");
$IAT();

14、__set_state

当调用var_export()时,这个静态 方法会被调用(自PHP 5.1.0起有效)。var_export 作用为输出变量的结构信息,换句话说,当用它输出对象时,实际是查看对象的属性;其有个特点:返回的结果是合法的php代码,这为它与eval()语法结构函数结合创造了条件。

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class iat
{
private $name;
private $grade;
public function __construct($name, $grade)
{
$this->name = $name;
$this->grade = $grade;
}
}
$IAT = new iat("iat", "grade_1");
var_export($IAT);

本方法的唯一参数是一个数组,其中包含按array(’property’ => value, …)格式排列的类属性。可以看到结果iat::set_state(array( ’name’ => ’iat’, ’grade’ => ’grade_1’, ))对象的结构被当作一个大的静态魔术方法,内置为set_state,其属性用一个数组来表示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class iat
{
public $name;
public $grade;
public static function __set_state($arr)
{
$IAT_A = new iat;
$IAT_A->name = "iat";
$IAT_A->grade = "grade_1";
return $IAT_A;
}
}
$IAT = new iat("iat", "grade_1");
$IAT->name = "iat";
$IAT->grade = "grade_2";
eval(’$b = ’. var_export($IAT, true) .’;’);
var_dump($b);

set_state()魔术方法是一个与clone()相似,却更为强劲的类功能方法,因为它可以有选择地克隆,同时可以保留自己的属性且还可以进行其它一些改变。

15、__callStatic

它的工作方式类似于 call() 魔术方法,callStatic() 是为了处理静态方法调用,当调用过的静态方法不存在或权限不足时调用__callStatic方法

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class iat
{
public $name;
public $grade;
public static function __callStatic($name, $arg)
{
echo "callStatic";
}
}
$IAT = new iat("iat", "grade_1");
echo $IAT::getName();

以上就是PHP15种魔术方法的介绍,在PHP面向对象中选用恰当的魔术方法可以使编程更可靠。

PHP中的8中魔术变量再w3cschool里讲的比较详细了,引用参考http://www.w3cschool.cc/php/php-magic-constant.html


参考资料

Just a beginner.<br /><a href='https://github.com/yaoshanliang/about' target='_blank'>profile</a>