解密PHP魔术方法:从__construct到__invoke的终极指南

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外,其他都是普通方法
  • 所有魔术方法名称必须严格区分大小写
  • 过度使用魔术方法可能降低代码可读性且魔术方法会增加一定的性能开销,因此需要合理的使用

有疑问或者补充和纠正的地方欢迎大家评论指教~
解密PHP魔术方法:从__construct到__invoke的终极指南_第1张图片

你可能感兴趣的:(php,java,linux,开发语言,运维,python,设计语言)