php魔术方法解析

PHP把所有以__(两个下划线)开头的类方法当成魔术方法。

所以当自定义类方法时,除了魔术方法,建议不要以 __为前缀。 php魔术方法包括: __construct(), __destruct(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __clone(),__call()等。

  1. __construct() 是php中类的构造方法,用来初始化类中的属性。同一个类中只能声明一个构造方法,故不能重载。但可以使用默认参数。构造方法一般是public类型的,如果是private类型,就会构成单例模式,php单例设计模式

    <?php
        class Person
        {
            private $name;
            private $age;
            private $email;
            //声明类的构造函数
            public function __construct($name="yeetrack", $age=1, $email="yeetrack@123.com")
            {
                $this->name = $name;
                $this->age = $age;
                $this ->email = $email;
            }
            public function printInfo()
            {
                echo "name->" . $this->name . " age->" . $this->age . " email->" . $this->email . "<br/>";
            }
        }
                                                                                                                                                                                                              
        $per = new Person("youthflies",2);
        $per->printInfo();
    ?>

     

  2. __destruct()是php中类的析构方法,对象在销毁前自动调用此方法,常在析构方法中执行关闭文件,释放连接等操作。php自带垃圾回收机制,当对象不能被访问时就会自动启用垃圾回收的机制,释放对象占用的堆空间。而php的析构方法是在启动垃圾回收之前调用的方法。析构方法方法只有需要的时候才需要声明。

  3. 为了封装和数据保护,我们一般把类的数据属性声明为private,如果需要频繁的读取数据属性,就会很繁琐。__set()方法来解决这个问题,它能在程序运行时为private类型的数据赋值。承接上面代码:

    //声明魔术方法__set()
            public function __set($propertyName, $value)
            {
                if($propertyName == "name")
                    $this->name = $value; //为name赋值
                elseif ($propertyName == "age")
                {   
                    if($value>200 | $value<0) //判断age范围
                        return;
                    $this->age = $value;
                }
                elseif ($propertyName == "email")
                    $this->email = $value;
            }

    有了__set的声明我们就可以这样为成员属性赋值(虽然属性是private类型的)

    $per = new Person("youthflies",2);
    $per->name = "youthflies";
    $per ->age = 12;
    $per->email = "youthflies@123.com";
    $per->printInfo();

    这样赋值,php就会默认去调用__set()方法。

  4. 如果一个属性被声明为private类型,而在对象外面直接获取该属性,php就会默认去寻找__get()方法。该方法可以在程序运行中获取private类型的数据。承接上面代码:

    //声明魔术方法__get()
            public function __get($propertyName)
            {
                if($propertyName == "name")
                    return $this->name;
                elseif ($propertyName == "age") //不允许获取age属性
                    return "secret";
                elseif ($propertyName == "email")
                    return $this->email;
            }

    如上面代码,我们可以在__get()方法中加入一些权限控制,或者返回一些假数据等等。

  5. isset()方法用来判断变量是否存在。如果类中的属性为public,我们可以直接用isset()方法来判断对象中的属性,但如果为private类型的,就不能直接判断了,需要用到__isset()方法。只有在类内部声明了__isset()方法,我们才可以在类外部用isset()来间接判断类中的private数据。承接上面代码:

    //声明魔术方法__isset()
            public function __isset($propertyName)
            {
                if($propertyName == "name")
                    return isset($this->name);
                elseif ($propertyName == "age")//不允许判断age属性
                    return false;
                elseif ($propertyName == "email")
                    return isset($this->email);
            }

    经过上面的声明,我们就可以在类外这样:

    var_dump(isset($per->name));
    var_dump(isset($per->age));
    var_dump(isset($per->email));

     

  6. unset()方法用来删除变量,如果类中的属性是public,我们可以直接用unset()删除,如果为private,就要通过__unset()方法了,类似于__isset()方法。

    //声明魔法方法__unset()方法
            public function __unset($propertyName)
            {
                if($propertyName == "name")//不允许删除name属性
                    return;
                elseif($propertyName == "age")
                    unset($this->age);
                elseif ($propertyName == "email")
                    unset($this->email);
            }

    这样我们就可以在类外面直接unset()类中private类型的属性了。

  7. __toString()用来获取对象的字符串输出,当我们直接打印某对象引用 时,该方法被自动调用。 如下代码:

    //声明魔法方法__toString()
            public function __toString()
            {
                return "name: " . $this->name . "<br/>age: " . $this->age . "<br/>email: " . $this->email . "<br/>";
            }

    这样我们可以在外面直接 echo $per; php会默认去调用类中的__toString()方法。

  8. 当我们调用的方法,在类中不存在时,就会出现error,程序会shutdown。__call()方法用来解决这种情况,当调用了类中不存在的方法时,__call()方法就会被调用。该方法有两个参数,一是不存在的方法的名称,二是不存在的方法的参数(以数组的形式保存)。

    //声明魔法方法__call()
            public function __call($methodName, $args)
            {
                echo "方法:" . $methodName . "不存在<br/>";
                print_r($args);
            }

     

  9. __autoload()方法用于自动加载需要的类文件。  我们一把为每个类都单独建立一个php文件,当需要这个类时,就用include将php文件包含进来。 如果类文件非常多,就很麻烦。__autoload()可以自动加载,当然前提是我们用一定规则命名类文件。   

    //声明魔法方法__autoload()
            public function __autoload($className)
            {
                include(strtolower($className) . ".class.php";
            }
                                                                          
            $hello = new Hello();

       如上代码,我们新建Hello对象,如果Hello类不存在,就会默认调用autoload()方法,$className获得参数"Hello",然后它去include当前文件夹下的Hello.class.php文件,如果我们按照这种方式命名我们的php文件,就会include成功。          

  10. php5通过引用来调用对象,如果有建立一个独立副本的需求,可以通过clone方法,clone获得的时两个完全一样且互相独立的对象。如果想在clone过程中进行一些自定义的初始化操作,可以在类中声明__clone()方法,在调用clone()方法时,php会默认调用__clone()方法。  

    //声明魔法方法__clone()
            public function __clone()
            {
                $this->name = "youthflies__clone";
                $this->age = "12__clone";
                $this->email = "youthflies@124.com__clone";
            }

    声明后,  利用下面两行代码查看效果。  

        $per2 = clone $per;
        $per2->printInfo();

     

  11. __sleep()方法在将对象串行化时(即调用serialize()函数时)会被调用,关于串行化,参考php对象串行化 。__sleep()方法没有参数,返回值是一个数组,用来指定要被串行化的属性。  

    //声明魔法方法__sleep()
            public function __sleep()
            {
                return array("name","email");
            }

    这样在串行化$per时,就只会把name和email两个属性串行化。

  12. __wakeup()方法用来在反串行化对象时,给将要生成的对象初始化一些数据。

    //声明魔法方法__wakeup()
            public function __wakeup()
            {
                $this->age = 12;
            }

    由于我们在声明__sleep()时,没有串行化age属性,所以我们在__wakeup()方法中,手动为age属性赋值,这样当反串行化时,age就会被赋值为12.           

发表评论

您的电子邮箱地址不会被公开。

(Spamcheck Enabled)