您现在的位置是:门户> 编程语言> PHP

PHP魔术方法和魔术变量
x0ffer 2018-12-13 233人围观 0条评论
简介有些东西如果不是经常使用,很容易忘记,比如魔术方法和魔术常量

    本章介绍PHP魔术方法和魔术常量。

    魔术方法(Magic methods)

    PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。 魔术方法包括:

    • __construct(),类的构造函数

    • __destruct(),类的析构函数

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

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

    • __get(),获得一个类的成员变量时调用

    • __set(),设置一个类的成员变量时调用

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

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

    • __sleep(),执行serialize()时,先会调用这个函数

    • __wakeup(),执行unserialize()时,先会调用这个函数

    • __toString(),类被当成字符串时的回应方法

    • __invoke(),调用函数的方式调用一个对象时的回应方法

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

    • __clone(),当对象复制完成时调用

    __construct()__destruct()

    构造函数和析构函数应该不陌生,他们在对象创建和消亡时被调用。例如我们需要打开一个文件,在对象创建时打开,对象消亡时关闭

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <?php 
    class FileRead
    {
       protected $handle = NULL;

       function __construct(){
           $this->handle = fopen(...);
       }

       function __destruct(){
           fclose($this->handle);
       }
    }
    ?>

    这两个方法在继承时可以扩展,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php 
    class TmpFileRead extends FileRead
    {
       function __construct(){
           parent::__construct();
       }

       function __destruct(){
           parent::__destruct();
       }
    }
    ?>

    __call()__callStatic()

    在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。这两个方法我们在可变方法(Variable functions)调用中可能会用到。
    这个方法和is_callable经常配合调用,注意是callable,不是callback, 不是回调。
    callback 回调更多的可以理解成一种使用方式,而匿名函数则是用来满足这种使用方式的一种途径,还有别的方式能满足callback,比如直接定义一个函数,传入函数的名称(字符串,如果是匿名函数的话,要把这个匿名函数对应的变量传进去,二者还是有些不同的),只是这种方式使用起来比较麻烦,比较少而已

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <?php
    class MethodTest
    {
       public function __call ($name, $arguments) {
           echo "Calling object method '$name' ". implode(', ', $arguments). "\n";
       }

       public static function __callStatic ($name, $arguments) {
           echo "Calling static method '$name' ". implode(', ', $arguments). "\n";
       }
    }

    $obj = new MethodTest;
    $obj->runTest('in object context');
    MethodTest::runTest('in static context');
    ?>

    __get()__set()__isset()__unset()

    当get/set一个类的成员变量时调用这两个函数。例如我们将对象变量保存在另外一个数组中,而不是对象本身的成员变量
    var_dump($this->aa)
    $this->aa = ‘pp’
    isset($this->aa)
    empty($this->aa)
    值的注意的是 isset 和 empty 触发的魔术方法好像只能返回true和false

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <?php 
    class MethodTest
    {
       private $data = array();

       public function __set($name, $value){
           $this->data[$name] = $value;
       }

       public function __get($name){
           if(array_key_exists($name, $this->data))
               return $this->data[$name];
           return NULL;
       }

       public function __isset($name){
           return isset($this->data[$name])
       }

       public function unset($name){
           unset($this->data[$name]);
       }
    }
    ?>


    __sleep()__wakeup()

    当我们在执行serialize()unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。(还没有用到过)例子如下:

    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
    30
    31
    32
    <?php
    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();
       }
    }
    ?>

    __toString()

    对象当成字符串时的回应方法。例如使用echo $obj;来输出一个对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php
    // Declare a simple class
    class TestClass
    {
       public function __toString() {
           return 'this is a object';
       }
    }

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

    这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。

    __invoke()

    调用函数的方式调用一个对象时的回应方法。如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <?php
    class CallableClass
    {
       function __invoke() {
           echo 'this is a object';
       }
    }
    $obj = new CallableClass;
    var_dump(is_callable($obj));
    ?>

    __set_state()

    调用var_export()导出类时,此静态方法会被调用。注意var_dump很强大,直接输出了,并不会触发。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?php
    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';
    var_dump(var_export($a));
    ?>

    __clone()

    当对象复制完成时调用。例如在设计模式详解及PHP实现:单例模式一文中提到的单例模式实现方式,利用这个函数来防止对象被克隆。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?php 
    public class Singleton {
       private static $_instance = NULL;

       // 私有构造方法
       private function __construct() {}

       public static function getInstance() {
           if (is_null(self::$_instance)) {
               self::$_instance = new Singleton();
           }
           return self::$_instance;
       }

       // 防止克隆实例
       public function __clone(){
           die('Clone is not allowed.' . E_USER_ERROR);
       }
    }
    ?>

    魔术常量(Magic constants)

    PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量。

    • __LINE__,文件中的当前行号

    • __FILE__,文件的完整路径和文件名

    • __DIR__,文件所在的目录

    • __FUNCTION__,函数名称

    • __CLASS__,类的名称

    • __TRAIT__,Trait的名字

    • __METHOD__,类的方法名

    • __NAMESPACE__,当前命名空间的名称

    这些魔术常量常常被用于获得当前环境信息或者记录日志。


分享:

文章评论