PHP的魔术方法是一类特殊的方法,标志是以下划线__
开头,在特定情况下会被自动调用。开发者可以合理利用此特性重载类的默认行为。实现自己想要实现的目的。根据魔术方法的作用可划分为以下几类。
__construct
构造函数,在创建对象时自动调用
class ClassName
{
function __construct()
{
echo "我被创建了\n";
}
}
$obj = new ClassName();
// 我被创建了
__destruct
析构函数,在对象被销毁时自动调用
class ClassName
{
function __destruct()
{
echo "我被销毁了\n";
}
}
$obj = new ClassName();
unset($obj);
// 我被销毁了
__toString
当对象被当作字符串使用时调用
class ClassName
{
function __toString()
{
return "调用了toString方法\n";
}
}
$obj = new ClassName();
echo $obj;
// 调用了toString方法
__get
读取不可访问属性时调用
class ClassName {
public function __get($name) {
return "读取了不可访问属性:{$name}\n";
}
}
$obj = new ClassName();
echo $obj->unknownPrototype;
// 读取了不可访问属性:unknownPrototype
__set
给不可访问属性赋值时调用
class ClassName
{
function __set($name,$value)
{
echo "对象给不可访问属性:{$name},设置了值:{$value}\n";
}
}
$obj = new ClassName();
$obj->unknownPrototype = 'ok';
// 对象给不可访问属性:unknownPrototype,设置了值:ok
__isset
对不可访问属性调用
isset()
或empty()
时调用
class ClassName {
public function __isset($name) {
echo "对象对不可访问属性:{$name},调用了isset/empty方法\n";
}
}
$obj = new ClassName();
isset($obj->unknownPrototype);
// 对象对不可访问属性:unknownPrototype,调用了isset/empty方法
__unset
对不可访问属性调用
unset()
时调用
class ClassName {
public function __unset($name) {
echo "对象对不可访问属性:{$name},调用了unset方法\n";
}
}
$obj = new ClassName();
unset($obj->unknownPrototype);
// 对象对不可访问属性:unknownPrototype,调用了unset方法
__call
调用不可访问的普通方法时调用
class ClassName {
public function __call($method,$args) {
echo "对象调用不可访问方法:{$method},并且传参:".var_export($args,true)."\n";
}
}
$obj = new ClassName();
$obj->unknownMethod('param1','param2');
/*
对象调用不可访问方法:unknownMethod,并且传参:array (
0 => 'param1',
1 => 'param2',
)
*/
__callStatic
调用不可访问的静态方法时调用
class ClassName {
public static function __callStatic($method,$args) {
echo "对象调用不可访问的静态方法:{$method},并且传参:".var_export($args,true)."\n";
}
}
ClassName::unknownMethod('param1','param2');
/*
对象调用不可访问的静态方法:unknownMethod,并且传参:array (
0 => 'param1',
1 => 'param2',
)
*/
__sleep
在对象被序列化前调用,返回需要被序列化的属性名数组
class ClassName
{
private $a;
private $b;
function __sleep()
{
return ['a']; // 只序列化a属性
}
}
$obj = new ClassName();
echo serialize($obj);
// O:9:"ClassName":1:{s:12:"ClassNamea";N;}
__wakeup
在对象被反序列化后调用
class ClassName
{
private $a;
private $b;
function __wakeup()
{
echo "对象已被反序列化\n";
}
}
$obj = new ClassName();
print_r(unserialize(serialize($obj)));
/*
对象已被反序列化
ClassName Object
(
[a:ClassName:private] =>
[b:ClassName:private] =>
)
*/
__serialize
替代
__sleep()
,返回包含所有需要序列化数据的数组(PHP7.4+)
class ClassName
{
private $a = 1;
private $b = 2;
function __serialize()
{
return ['a' => $this->a]; // 自定义控制
}
}
$obj = new ClassName();
echo serialize($obj);
// O:9:"ClassName":1:{s:1:"a";i:1;}
__unserialize
替代
__wakeup()
,接收__serialize()
返回的数组(PHP7.4+)
class ClassName
{
private $a = 1;
private $b = 2;
function __serialize()
{
return ['a' => $this->a]; // 自定义控制
}
function __unserialize($data)
{
// print_r($data);
/*
Array
(
[a] => 1
)
*/
$this->a = $data['a'];
}
}
$obj = new ClassName();
print_r(unserialize(serialize($obj)));
/*
ClassName Object
(
[a:ClassName:private] => 1
[b:ClassName:private] => 2
)
*/
__clone
当对象被克隆时调用
class ClassName
{
function __clone()
{
echo "对象被复制";
}
}
$obj = new ClassName();
$cloneObj = clone $obj;
// 对象被复制
__invoke
当尝试以调用函数的方式调用对象时调用
class ClassName
{
function __invoke($a,$b)
{
echo "对象被当函数调用,并传参:{$a},{$b}";
}
}
$obj = new ClassName();
$obj(1,2);
// 对象被当函数调用,并传参:1,2
__debugInfo
当使用
var_dump()
打印对象时调用,返回要显示的属性数组
class ClassName
{
private $x;
function __debugInfo()
{
return ['message' => 'I am __debugInfo']; //自定义显示
}
}
$obj = new ClassName();
var_dump($obj);
/*
未加__debugInfo时:
object(ClassName)#1 (1) {
["x":"ClassName":private] => NULL
}
加__debugInfo时:
object(ClassName)#1 (1) {
["message"]=>
string(16) "I am __debugInfo"
}
*/
public
,除了__callStatic
是静态方法static
外,其他都是普通方法