魔术方法
魔术方法总结
魔术方法 | 触发时机 |
---|---|
__construct() |
类的构建函数 |
__destruct() |
类的析构函数 |
__sleep() |
执行 serialize() 时,先会调用这个函数 |
__wakeup() |
执行 unserialize() 时,先会调用这个函数 |
__toString() |
类被当成字符串调用时会触发 |
__invoke() |
以调用函数的方式调用一个对象时会触发 |
__call() |
在对象中调用一个不存在或不可访问方法时会触发 |
__callStatic() |
用静态方式中调用一个不存在或不可访问方法会触发 |
__get() |
调用的成员属性不存在时会触发 |
__set() |
给不存在的成员属性赋值时会触发 |
__isset() |
当对不存在或不可访问属性调用 isset() 或 empty() 时会触发 |
__unset() |
当对不存在或不可访问属性调用 unset() 时会触发 |
__clone() |
当使用clone 函数对象复制完成时会触发 |
__set_state() |
调用var_export() 导出类时会触发此静态方法 |
__autoload() |
尝试加载未定义的类会触发 |
__debugInfo() |
打印所需调试信息 |
__construct()
构造函数,在创建、实例化一个对象时,首先会去自动执行的一个魔术方法
- **触发时机:**实例化对象
- **功能:**提前清理不必要内容
- 参数:非必要
1 |
|
- 在实例化
User
类时,会触发__construct()
魔术方法 __construct()
魔术方法会将传入的benben
参数赋值给类的username
属性- 并且会
echo
输出触发了构造函数一次
__destruct()
析构函数,在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法
- **触发时机:**对象引用完成,或对象被销毁(实例化或反序列化之后)
1 |
|
- 实例化对象结束后,这个对象最终会被销毁,就会触发一次
__destruct()
魔术方法,第一次执行echo
语句输出触发了析构函数1次
- 因为反序列化得到的是对象,用完之后会被销毁,所以反序列化结束后也会触发一次
__destruct()
魔术方法,第二次执行echo
语句输出触发了析构函数1次
析构函数例题
1 |
|
进行序列化
1 |
|
1 |
|
最后会发现只会触发一次__destruct()
魔术方法,是在unserialize($ser);
这时触发的
__sleep()
序列化serialize()
函数会检查类中是否存在一个魔术方法__sleep()
如果存在,该方法会被先调用,然后才执行序列化操作
此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组
如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误
- **触发时机:**序列化
serialize()
之前 - **功能:**对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性
- 参数:成员属性
- **返回值:**需要被序列化存储的成员属性
1 |
|
- 在整个过程中,先实例化
User
,会首先触发__construct()
魔术方法,会给$username, $nickname, $password
分别赋值'a', 'b', 'c'
- 然后在序列化
serialize()
对象时就会触发__sleep()
,返回只含有username
和nickname
的数组
如果没有sleep()
魔术方法,序列化后的结果为
1 |
|
这题中加入了sleep()
魔术方法,序列化结果为
1 |
|
只有username
和nickname
__wakeup()
反序列化unserialize()
会检查是否存在一个__wakeup()
方法
如果存在,则会先调用__wakeup()
方法,预先准备对象所需的资源
预先准备对象资源,返回void
,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作
- **触发时机:**反序列化
serialize()
之前
1 |
|
- 这里对已经构造好的一个序列化数据进行反序列化,在序列化数据中已经定义好了
username
为a
,nickname
为b
- 在反序列化之前,会触发
__wakeup()
魔术方法,使得password
的值等于username
的值
要绕过这个魔术方法只需要让代表属性数量的值加一即可
__toString
表达方式错误导致魔术方法触发
调用对象可以使用print_r
或var_dump
使用echo
或print
是调用字符串的方式
- **触发时机:**把对象当作字符串调用
1 |
|
- 这里将
User
类实例化为一个对象test
,然后使用了print_r
和echo
调用了这个对象 - 可以使用
print_r
调用对象 - 但不能使用
echo
,这会导致将对象以字符串的形式调用,从而触发__toString
魔术方法
__invoke()
格式表达错误导致魔术方法触发
- **触发时机:**把对象当作函数调用
1 |
|
echo $test ->benben;
是正常的调用对象中的benben
属性,输出benebn
的值echo $test() ->benben;
是在以调用函数的方法调用test
这个对象,会触发__invoke()
魔术方法,会输出它不是个函数!
__call()
- **触发时机:**调用一个不存在的方法
- **参数:**2个参数传参
$arg1
(不存在的方法名),$arg2
(给不存在的方法传入的参数) - **返回值:**调用的不存在的方法的名称和参数
1 |
|
$test -> callxxx('a');
这里调用了不存在的方法callxxx()
,并传入参数a
,会触发魔术方法__call()
,传入的两个参数分别是不存在的方法名callxxx()
和传入的参数a
__callStatic()
- **触发时机:**静态调用或调用成员常量时使用的方法不存在
- **参数:**2个参数传参
$arg1
(不存在的方法名),$arg2
(给不存在的方法传入的参数) - **返回值:**调用的不存在的方法名称和参数
1 |
|
$test::callxxx('a');
这里使用了静态调用的方式调用test
对象中的callxxx()
方法,但这个方法不存在,所以会触发__callStatic()
魔术方法,传入的两个参数分别是不存在的方法名callxxx
和传入的参数a
__get()
- **触发时机:**调用的成员属性不存在
- **参数:**传参
$arg1
(不存在的成员属性) - **返回值:**不存在的成员属性名称
1 |
|
$test ->var2;
这里调用了test
对象中的var2
这个成员属性,但是在对象中这个成员属性是不存在的,这就会触发__get()
魔术方法,传入的参数$arg1
是不存在的成员属性var2
__set()
- **触发时机:**给不存在的成员属性赋值
- **参数:**传参
$arg1
(不存在的成员属性名),arg2
(给不存在的成员属性赋的值) - **返回值:**不存在的成员属性的名称和赋的值
1 |
|
$test ->var2=1;
这里对test
对象中的var2
属性赋值为1
,但是var2
这个属性是不存在的,就会触发__set()
魔术方法,传入的参数分别是不存在的属性名var2
和赋的值1
__isset()
- **触发时机:**对不可访问或不存在的属性使用
isset()
或empty()
时会触发 - **参数:**传参
$arg1
(不可访问或不存在的属性) - **返回值:**不可访问或不存在的成员属性的名称
1 |
|
isset($test->var);
这里使用isset
函数调用了不可访问的私有属性var
,就会触发__isset()
魔术方法empty($test->var1);
这里使用了empty
函数调用了不存在的属性var1
,就会触发__isset()
魔术方法
__unset()
- **触发时机:**对不可访问或不存在的属性使用
unset()
时会触发 - **参数:**传参
$arg1
(不可访问或不存在的属性) - **返回值:**不存在的成员属性名
1 |
|
unset($test->var);
这里使用unset
函数调用不可访问的私有属性var
,会触发__unset()
魔术方法,传入的参数是不可访问的私有属性var
unset($test->var2);
这里使用unset
函数调用不存在的属性var2
,会触发__unset()
魔术方法,传入的参数是不存在的属性var2
__clone()
- **触发时机:**当使用
clone
关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法__clone()
1 |
|
$test = new User() ;
将User
类实例化成对象test
,这个类中有__clone
魔术方法$newclass = clone($test)
这里使用clone
函数将对象test
拷贝给新对象newclass
,新对象就会触发拷贝过来的__clone
魔术方法
魔术方法
https://yschen20.github.io/2025/08/03/PHP序列化魔术方法/