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()方法
"; $this->name = base64_encode($this->name); return array('name', 'age'); // 这里必须返回一个数值,里边的元素表示返回的属 性名称 } public function __wakeup() { echo "当在类外部使用unserialize()时会调用这里的__wakeup()方法
"; $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

你可能感兴趣的:(PHP强化之20 - 魔术方法)