PHP强化之20 - 魔术方法

魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,你可以把它们理解为钩子,利用魔术方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。

魔术方法很多还是成对出现的,以下列出目前PHP中所有的魔术方法:

1、__construct()与__destruct

__construct(),类的构造函数。在使用 new关键字使用类实例化一个对象时自动执行。

__destruct(),类的析构函数。在对象被销毁(unset或PHP执行结束)时自动执行,通常用于释放对象占用的第三方资源(如:数据库)。

2、__call()与__callStatic()

__call(),在对象中调用一个不可访问方法时调用。

class Person{
  function __call($funName, $arguments)
  { 
     echo "你所调用的函数:" . $funName . "不存在。\n\r" ; // 输出调用不存在的方法名
     echo "参数为:\n\r";
     print_r($arguments); // 输出调用不存在的方法时的参数列表           
  }                     
}
$Person = new Person();      
$Person->eat("小明", "苹果");

以上例程会输出:

你所调用的函数:eat不存在。
参数为:
Array
(
    [0] => 小明
    [1] => 苹果
)

__callStatic(),用静态方式中调用一个不可访问方法时调用。

public static function __callStatic($funName, $arguments){
  //...
}

3、__get()与__set()

当对一个对象的未定义的属性,进行“取值”时,此时会自动调用类中预先定义好的魔术方法__get()。

class Person{
  private $name;
  private $age;
  function __construct($name="", $age=1){
    $this->name = $name;
    $this->age = $age;
  }
  public function __get($propertyName){  
      return $this->$propertyName;
  }
}

$Person = new Person("小明",  50);  
echo "姓名:" . $Person->name . "\n\r"; 
echo "年龄:" . $Person->age . "\n\r";  

以上例程会输出:

姓名:小明
年龄:50

__set(),当对一个对象的未定义的属性,进行“赋值”时,此时会自动调用类中预先定义好的魔术方法__set()。

class Person{
  private $name;
  private $age;
 
  public function __set($property, $value) {
    if ($property=="age"){
      if ($value > 150 || $value < 0) {
        return;
      }
    }
    $this->$property = $value;
  }
}
 
$Person=new Person(); 
$Person->name = "小红";   //赋值成功。如果没有__set(),则出错。
$Person->age = 16; //赋值成功
$Person->age = 160; //160是一个非法值,赋值失效

4、__isset()与__unset()

__isset(),当对不可访问属性调用isset()或empty()时调用。

__unset(),当对不可访问属性调用unset()时被调用。

5、__sleep() 和 __wakeup()

__sleep(),执行serialize()时,先会调用这个函数;__wakeup(),执行unserialize()时,先会调用这个函数。

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。同时,如果有一些很大的对象,但不需要全部保存,这个功能就很好用。
__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。

class Person{
  public $sex;
  public $name;
  public $age;

  public function __construct($name="", $age=25, $sex='男'){
    $this->name = $name;
    $this->age = $age;
    $this->sex = $sex;
  }
  
  public function __sleep() {
    echo "当在类外部使用serialize()时会调用这里的__sleep()方法<br>";
    $this->name = base64_encode($this->name);
    return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属
性名称
  }
  
  public function __wakeup() {
    echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法<br>";
    $this->age = 30;
    $this->sex = '女';
    // 这里不需要返回数组
  }
}
 
$person = new Person('小明'); // 初始赋值
$a = serialize($person);
var_dump($a);
$b = unserialize($a);
var_dump($b);

以上例程会输出:

当在类外部使用serialize()时会调用这里的__sleep()方法
/var/www/html/demo28.php:29:string 'O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}' (length=58)
当在类外部使用unserialize()时会调用这里的__wakeup()方法
/var/www/html/demo28.php:31:
object(Person)[2]
  public 'sex' => string '女' (length=3)
  public 'name' => string '5bCP5piO' (length=8)
  public 'age' => int 30

官方例子:

class Connection {
    protected $link;
    private $server, $username, $password, $db;
    
    public function __construct($server, $username, $password, $db) {
        $this->server = $server;
        $this->username = $username;
        $this->password = $password;
        $this->db = $db;
        $this->connect();
    }
    
    private function connect() {
        $this->link = mysql_connect($this->server, $this->username, $this->password);
        mysql_select_db($this->db, $this->link);
    }
    
    public function __sleep(){
        return array('server', 'username', 'password', 'db');
    }
    
    public function __wakeup(){
        $this->connect();
    }
}

6、__toString()

__toString() 方法用于一个类被当成字符串时应怎样回应。

例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

class TestClass{
    public $foo = __CLASS__;

    public function __toString() {
        return $this->foo;
    }
}

$class = new TestClass();
echo $class;

以上例程会输出:

TestClass

7、__invoke()

当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

class CallableClass {
    function __invoke($x) {
        var_dump($x);
    }
}
$obj = new CallableClass;
$obj(5);
var_dump(is_callable($obj));

以上例程会输出:

int(5)
bool(true)

8、__set_state()

__set_state(),调用var_export()导出类时,此静态方法会被调用。

class A{
    public $var1;
    public $var2;

    public static function __set_state($an_array){
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

eval('$b = ' . var_export($a, true) . ';'); // $b = A::__set_state(array(
                                            //    'var1' => 5,
                                            //    'var2' => 'foo',
                                            // ));
var_dump($b);

以上例程会输出:

object(A)#2 (2) {
  ["var1"]=>
  int(5)
  ["var2"]=>
  string(3) "foo"
}

9、__clone()

在克隆(clone)对象时自动执行。

class C {
    public function __clone() {
        echo '正在克隆。。。';
    }
}

$a = new C();
$b = clone $a;

以上例程会输出:

正在克隆。。。

10、__autoload()

__autoload(),尝试加载未定义的类。

function __autoload($className) { 
  $filePath = "project/class/{$className}.php"; 
  if (is_readable($filePath)) { 
    require($filePath); 
  } 
} 

随着PHP版本的更新,该函数已经不建议使用,取而代之的是spl_auto_register()函数。

11、__debugInfo()

__debugInfo(),打印所需调试信息。

class C {
    private $prop = 'test';
    
    public function __debugInfo() {
        return [
            'data1' => $this->prop,
        ];
    }
}

var_dump(new C());

以上例程会输出:

class C#1 (1) {
  public $data1 =>
  string(4) "test"
}

官方文档:http://php.net/manual/zh/language.oop5.magic.php

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容