关于php魔术方法的一些总结

本文最后更新于:2024年12月14日 下午

__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()__sleep()__wakeup()__serialize(), __unserialize(), __toString()__invoke()__set_state()__clone()__debugInfo() 等方法在 PHP 中被称为魔术方法(Magic methods)

所有的魔术方法都要以public来声明.

__construct()__destruct()

__construct()是构造方法,即创建一个对象的时候,首先干的事情。

官方文档的说法是:PHP 允许开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

1
2
3
4
5
6
7
8
9
10
11
12
class test {
public $filename = '';
public $data = '';

function __construct($filename, $data) {
$this->filename = $filename;
$this->data = $data;
echo 'construct function in test class';
echo "<br>";
}
}
$a = new test('test.txt', 'data');

例如以上代码的执行结果就是construct function in test class

__destruct()即析构函数,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

官方文档给出了一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class MyDestructableClass
{
function __construct() {
print "In constructor\n";
}

function __destruct() {
print "Destroying " . __CLASS__ . "\n";
}
}

$obj = new MyDestructableClass();

这段代码的运行结果是这样的:

image-20211104215122208

我的理解就是这个对象所有内容执行完后所运行的代码。

__set(),__get(),__isset(),__unset()

以下是官方文档的说法:

PHP所提供的重载(overloading)是指动态地创建类属性和方法。我们是通过魔术方法(magic methods)来实现的。

当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。本节后面将使用不可访问属性(inaccessible properties)和不可访问方法(inaccessible methods)来称呼这些未定义或不可见的类属性或方法。

所有的重载方法都必须被声明为 public

image-20211104215625262

同样,也给出了这四个方法的用例

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?php
class PropertyTest {
/** 被重载的数据保存在此 */
private $data = array();



/** 重载不能被用在已经定义的属性 */
public $declared = 1;

/** 只有从类外部访问这个属性时,重载才会发生 */
private $hidden = 2;

public function __set($name, $value)
{
echo "Setting '$name' to '$value'\n";
$this->data[$name] = $value;
}
/*
这里的执行结果是:
Setting 'a' to '1'
*/
public function __get($name)
{
echo "Getting '$name'\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
/*
这里的结果是:
Getting 'a'
1
*/
$trace = debug_backtrace();
trigger_error(
'Undefined property via __get(): ' . $name .
' in ' . $trace[0]['file'] .
' on line ' . $trace[0]['line'],
E_USER_NOTICE);
return null;
}# 这一段暂且有点不明白

public function __isset($name)
{
echo "Is '$name' set?\n";
return isset($this->data[$name]);
}
/*
结果是:
Is 'a' set?
bool(true)
*/
public function __unset($name)
{
echo "Unsetting '$name'\n";
unset($this->data[$name]);
}
/*
这里是:
Unsetting 'a'
*/
/** 非魔术方法 */
public function getHidden()
{
return $this->hidden;
}
}


echo "<pre>\n";

$obj = new PropertyTest;
# 此时构建对象
$obj->a = 1;# 这里调用了__set()
echo $obj->a . "\n\n";# 这里调用了__get()去访问a的value

var_dump(isset($obj->a));# 这里调用了__isset()去判断a是否存在
unset($obj->a);# 这里调用__unset()删除a
var_dump(isset($obj->a));# 这里再次判断是否存在来检验unset()效果
echo "\n";

echo $obj->declared . "\n\n";

echo "Let's experiment with the private property named 'hidden':\n";
echo "Privates are visible inside the class, so __get() not used...\n";
echo $obj->getHidden() . "\n";
echo "Privates not visible outside of class, so __get() is used...\n";
echo $obj->hidden . "\n";
?>

我用自己的理解给他注释了一下

全部的运行结果如下:

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

到这里其实基本已经明晰了,这里的魔术方法其实有点类似于python中的装饰器,定义在访问这个方法的同时额外执行任务的代码(纯属个人理解哈)

__call()__callstatic()

这俩其实也属于重载一类,老规矩先看文档的介绍:

image-20211104223034247

同样的官网也给出了一个简单的例子,我们来复现一下:

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
<?php
class MethodTest
{
public function __call($name, $arguments)
{
// 注意: $name 的值区分大小写
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}

public static function __callStatic($name, $arguments)
{
// 注意: $name 的值区分大小写
echo "Calling static method '$name' "
. implode(', ', $arguments). "\n";
}
}

$obj = new MethodTest;
$obj->runTest('in object context');
# 不明白的人可能有点疑惑,这里的runtest()其实是对象中不存在的方法
# 访问不存在的方法时会调用__call()
# 也就是说这里会输出:Calling object method 'runTest' in object context

MethodTest::runTest('in static context');# 访问静态方法时会调用__callstatic()
?>

虽然代码很简短,我还是趁热打铁注释一下吧。(运行结果已经注释进去)

静态方法有点陌生:

image-20211104224010034

静态方法其实就是不需要对象即可调用的方法

__sleep(),__wakeup()

这2个方法在php反序列化中非常常见。

image-20211104224306820

文档的介绍如上

由于文档这里给出的演示代码个人感觉不是很清晰,所以这里借鉴了博主Magic_Zero的演示代码

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
class test {

private $flag = '';

# 用于保存重载的数据
private $data = array();

public $filename = '';

public $content = '';

function __construct($filename, $content) {
$this->filename = $filename;
$this->content = $content;
echo 'construct function in test class';
echo "<br>";
}

function __destruct() {
echo 'destruct function in test class';
echo "<br>";
}

# 反序列化时候触发
function __wakeup() {
// file_put_contents($this->filename, $this->data);
echo 'wakeup function in test class';
echo "<br>";
}

# 一般情况用在序列化操作时候,用于保留数据
function __sleep() {
echo 'sleep function in test class';
echo "<br>";
return array('flag', 'filename', 'data');
}

# 当需要输出得到对象名称时候会调用
function __toString() {
return $this->data;
}

public function set_flag($flag) {
$this->flag = $flag;
}

public function get_flag() {
return $this->flag;
}
}

$key = serialize(new test('test.txt', 'test'));

var_dump($key);

$b = unserialize($key);

由于这位大佬写的非常清晰了就不再注释了

img

var_dump() 函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。数组将递归展开值,通过缩进显示其结构。

image-20211104225623447

__tostring()

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

直接看文档上的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
// Declare a simple class
class TestClass
{
public $foo;

public function __construct($foo)
{
$this->foo = $foo;
}# 构造时传入一个变量

public function __toString() {
return $this->foo;
}# 当需要输出得到对象名称时会调用此方法
}

$class = new TestClass('Hello');
echo $class;# 结果是:Hello
?>

注释了一下

输出结果是Hello

至此本文结束

参考文献:

https://www.cnblogs.com/magic-zero/p/7360396.html

https://www.php.net/manual/zh/language.oop5.magic.php#object.tostring


关于php魔术方法的一些总结
http://gensokyo.cn/2021/11/04/关于php魔术方法的一些总结/
作者
s1ain
发布于
2021年11月4日
许可协议