补个档,重新复习下php的几种魔术方法,同时魔术方法也是必须掌握的,面试什么的也是必考的.附上php文档的相关警告:PHP 将所有以两个下划线开头的类方法保留为魔术方法。所以在定义类方法时,除了讲到的魔术方法,建议不要以两个下划线为前缀. 切记切记~~

常用魔术方法

__construct()

  • 描述:类构造方法,在实例化类时自动调用
1
2
3
4
5
6
7
8
class A {
function __construct() //参数列表可选
{
//.....
}
}
$instance = new A(); //实例化时自动调用

__destruct()

  • 描述:析构函数,在对象的所有引用被删除或者脚本执行结束时调用
1
2
3
4
5
6
7
8
9
10
class A {
function __destruct() //参数列表为空
{
//...
}
}
$instance = new A();
unset($instance); //此处调用析构函数 如果此行注释掉,脚本结束亦会调用

__toString()

  • 描述:当类被当作字符串时,此方法返回字符串。返回值为string,无参数。
1
2
3
4
5
6
7
8
9
10
11
12
class A{
function __toString()
{
return "I AM A";
}
}
$instance = new A();
echo $instance;
print $instance;

__get()

  • 描述:当读取不可访问属性的调用,参数string值,返回值mixed
1
2
3
4
5
6
7
8
9
10
11
12
13
class A{
private $a = 1;
function __get($name) //$name为要访问的属性名
{
if(isset($this->$name)){
return $this->$name;
}
}
}
$instance = new A();
echo $instance->a; //1

__set()

  • 描述:当给不可访问的属性赋值时调用,参数1(属性名) string 参数2(赋值) mixed ,返回值为void
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A{
private $a = 1;
function __set($name,$value)
{
if(isset($this->$name)){
$this->$name=$value;
}
}
function __get($name){
if(isset($this->$name)){
return $this->$name;
}
}
}
$instance = new A();
$instance->a = 3; //自动调用__set()
echo $instance->a; //自动调用__get()

__isset()

  • 描述:对当不可访问的属性调用isset()或empty()时,__isset()会被自动调用,接受一个string 参数(被访问的属性名)
1
2
3
4
5
6
7
8
9
10
11
class A{
private $a;
function __isset($name) //被访问的属性名
{
echo $name."__isset is invoked";
}
}
$instance = new A();
isset($instance->a); // a__isset is invoked

__unset()

  • 描述:对当不可访问的属性调用unset()时,__unset()会被自动调用,接受一个string 参数(被访问的属性名)
1
2
3
4
5
6
7
8
9
10
11
class A{
private $a;
function __unset($name) //被访问的属性名
{
echo $name."__unset is invoked";
}
}
$instance = new A();
unset($instance->a); //a__unset is invoked

__call()

  • 描述:当对象调用一个不可访问的方法是,__call()会被自动调用,接受两个参数,第一个参数string(方法名),第二个参数array(参数列表)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A{
private function inside($a,$b)
{
echo $a.'_'.$b;
}
function __call($name,$args) //方法名&参数列表
{
echo 'funciton_name:'.$name.' parameter:'.implode(' , ',$args);
}
}
$instance = new A();
$instance->inside(1,2,3); //funciton_name:inside parameter:1 , 2 , 3

__callStatic()

  • 描述:当对象调用一个不可访问的静态方法是,__callStatic()会被自动调用,接受两个参数,第一个参数string(方法名),第二个参数array(参数列表),修饰必须是可见的以及静态的
1
2
3
4
5
6
7
8
9
10
11
12
13
class A{
private static function inside($a,$b)
{
echo $a.'_'.$b;
}
public static function __callStatic($name,$args) //修饰必须是可见的以及静态的
{
echo 'static_funciton_name:'.$name.' parameter:'.implode(' , ',$args);
}
}
A::inside(1,2,3); //static_funciton_name:inside parameter:1 , 2 , 3

__clone()

  • 描述:当使用关键字clone对象是,__clone()方法会被自动调用.
  • 作用:因为clone是浅克隆,当要复制的对象内部有一个引用属性时,并不会进行复制,而是照搬引用,所以此时需要在__clone方法中对某些引用属性进行深克隆
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
class A{
public $obj ; //引用对象
public function __construct(B $ojb)
{
$this->obj = $ojb;
}
function __clone(){
$this->obj = clone $this->obj; //对应引用对象进行深克隆
}
}
class B{
public $content="abc";
}
$a = new A(new B());
$b = clone $a;
$a->obj->content = "efg"; //即使$a内部的引用对象改变了
echo $b->obj->content; //$b内部的引用对象仍不变 为abc

__sleep()

  • 描述:serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。
  • 备注:__sleep() 不能返回父类的私有成员的名字。这样做会产生一个 E_NOTICE 级别的错误。可以用 Serializable 接口来替代。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A{
public $var1 = 1;
public $var2 = 2;
public $var3 = 3;
public function __sleep() //返回应被序列化的变量名称的数组
{
return array('var1','var2'); //序列化时忽略序列化$var3
}
}
$instance = new A();
echo serialize($instance); //O:1:"A":2:{s:4:"var1";i:1;s:4:"var2";i:2;}

Notice:serialize()和unserialize(),是将php的值与字符串相互转化的方法(序列化与反序列化),主要便于存储,同时也不丢失类型结构。

__wakeup()

  • 描述:unserialize(反序列化)对象时,__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
class A{
public $var1 = 1;
public $var2 = 2;
public $var3 = 3;
public function __sleep()
{
return array('var1','var2');
}
function __wakeup()
{
$this->var1 = 233;
}
}
$instance = new A();
$a=serialize($instance);
echo $a; // O:1:"A":2:{s:4:"var1";i:1;s:4:"var2";i:2;} 可见var1的值为1
$a=unserialize($a);
echo $a->var1; //输出233 而不是 1

魔术方法实现重载

属性重载

PHP所提供的”属性重载”是指动态地”创建”类属性

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
class A{
// 可重载的属性存放在data数组中
private $data = array();
// 此属性不能被重载
public $declared = 1;
// 从类外部访问此属性时才发生重载
private $hidden = 2;
// 利用__set动态创建
public function __set($name , $value)
{
$this->data[$name] = $value;
}
// 返回重载属性数组data
public function getData()
{
return $this->data;
}
}
$instance = new A();
$instance->a = 3; //a属性被动态添加
print_r($instance->getData()); //Array ( [a] => 3 )

方法重载

传统的”重载”是用于提供多个同名的类方法,但各方法的参数类型和个数不同。不过要实现相同的功能可以使用__call()魔术方法.

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
class A{
function __call($name,$args)
{
if($name == 'func')
{
$args_num = count($args);
$invoke_name=$name.$args_num;
if(method_exists($this,$invoke_name)){
call_user_func_array(array($this,$invoke_name),$args); //参数1:回调方法 参数2:数组形式的参数列表
}
}
}
function func1($arg1)
{
echo "invoke func1:".$arg1;
}
function func2($arg1,$arg2)
{
echo "invoke func2:".$arg1.','.$arg2;
}
}
$instance = new A();
$instance->func(1); //执行func1
$instance->func(1,2); //执行func2

notice:call_user_func_array方法的第一个参数是callable类型,一个已实例化的 object 的方法被作为 array 传递,下标 0 包含该 object,下标 1 包含方法名。 在同一个类里可以访问 protected 和 private 方法。

参考资料

PHP官方文档