zoukankan      html  css  js  c++  java
  • PHP基础知识系列:拦截器方法

    拦截器方法:它可以拦截发送到未定义方法和属性的消息。

     
    方法   描述
    __get($property) 访问未定义的属性时被调用
    __set($property) 给未定义的属性赋值时被调用
    __isset($property,$value) 对未定义的属性使用isset()时被调用
    __unset($property) 对未定义的属性调用unset()时被调用
    __call($method,$arg_array) 调用未定义的方法时被调用
    __autoload($classname) 自动载入类

     

    重载可以通过__get, __set, __call几个特殊方法来进行. 当Zend引擎试图访问一个成员并没有找到时,PHP将会调用这些方法.

    __get()和__set()方法用于处理类中为声明的属性。
    __call方法说明了你如何调用未经定义的方法. 你调用未定义方法时,方法名和方法接收的参数将会传给__call方法, PHP传递__call的值返回给未定义的方法.
    _get():当客户端代码试图访问未声明的属性时,__get()方法会被自动调用,并带一个包含要访问的属性名称的字符串参数。无论__get()返回什么都会发送给客户端代码,就好像带有该值一样。__get方法可以用来捕获一个对象中不存在的变量和方法。

    class Person{
      function __get($property){//访问属性不存在时调用指定方法的返回值
        $method = "get{$property}";
        if(method_exists($this,$method)){//判断函数是否存在
          return $this->method();
        }
      }
    
      function getName(){
        return "Bob";
      }
    
      function getAge(){
        return "44";
      }
    }


    如下:

    $p = new Person();
    print $p->name;

    如果访问未定义的属性,那么就会调用__get()方法,__get($name)方法内部获取到getname()方法的返回值,返回给结果,就如同内部有name属性一样。如果没有,则什么也不做。用户试图访问的属性被解析为NULL。

    __isset():与__get()方法相似,当客户在一个未定义的属性上调用isset()时,__isset()被调用。

    function __isset($property){
      $method = "get{$property}";
      return (method_exists($method));
    }

    现在,可以通过检查属性是否存在,来返回了:

    if(isset($p->name)){
      print($p->name);
    }

    当调用isset()方法时,属性不存在,那么会调用__isset()方法,判断获取属性的方法是否存在,如果存在的话,可以后续执行获取的方法:$p->name,这样方法就会调用__get()方法来获取值了。

    _set():在客户端代码试图给未定义的属性赋值时被调用,它会接受2个参数:属性名和客户要设置的值,然后我们在决定如何使用这些参数。__set方法可以用来捕获和按参数修改一个对象中不存在的变量和方法。

    class Person{
      private $_name;
      private $_age;
      //如果试图设置未定义的属性,那么调用此方法
      function __set($property,$value){
        $method = "set{$property}";
        if(method_exists($method)){
          return $this->$method($value);
        }
      }
    
      function setName($name){
        $this->_name = $name;
        if(!is_null($name)){
          $this->_name = strtoupper($this->_name);
        }
      }
    
      function setAge($age){
        $this->_age = strtoupper($age);
      }
    }

    当设置一个属性时,如果一个类没定义这个属性,那么__set()方法会被调用,参数为这个属性和你要设置的$value,这个值如何使用取决于你写的__set()方法的具体实现。在本例中我们调用set{$proterty}()方法来设置属性的值。

    $p = new Person();
    $p->name = "bob";
    echo $p->name;//返回bob

    __unset():与__set()相对应,当unset()在一个未定义的属性上被调用时,__unset()方法会被调用,并以该属性的名称作为参数,然后根据属性名进行必要的处理。

    class Person{
      //如果试图使用unset()处理未定义的属性,那么调用此方法
      function __unset($property){
        $method = "set{$property}";
        if(method_exists($method)){
          return $this->$method(null);
        }
      }
    }

    __call():当客户端代码要调用类中未定义的方法时,__call()方法会被调用。__call()接受2个参数,一个是方法的名称,一个是方法的参数数组。__call()对于实现委托很有用。委托是指一个对象转发或者委托一个请求给另一个对象,被委托的对象替原来的对象处理请求。类似于继承,但是继承的父子关系是确定的,而委托可以在代码运行时改变使用的对象,具有更好的灵活性。

    class PersonWriter{//被委托的类
      function writeName(Person $p){//注意这里的参数是Person对象
        print $p->getName()."\n";
      }
    
      function writeAge(Person $p){
        print $p->getAge()."\n";
      }
    }

    委托类:

    class Person{
      private $writer;
      function __construct(PersonWriter $writer){
        $this->witer = $writer;
      }
    
      function __call($method,$args){
        if(method_exists($this->writer,$methodname)){
          return $this->writer->$methodname($this);
        }
      }
      
      function getName(){return "Bob";}
      function getAge(){return 44;}
    }


    调用方式:

    $person = new Person(new PersonWriter());
    $person->wirteName();//Bob

     __autoload($classname):很多开发者写面向对象的应用程序时,对每个类的定义建立一个 PHP 源文件。一个很大的烦恼是不得不在每个脚本(每个类一个文件)开头写一个长长的包含文件的列表。 

    在软件开发的系统中,不可能把所有的类都写在一个PHP文件中,当在一个PHP文件中需要调用另一个文件中声明的类时,就需要通过include把 这个文件引入。不过有的时候,在文件众多的项目中,要一一将所需类的文件都include进来,是一个很让人头疼的事,所以我们能不能在用到什么类的时 候,再把这个类所在的php文件导入呢?这就是我们这里我们要讲的自动加载类

      在 PHP 5 中,可以定义一个 __autoload()函数,它会在试图使用尚未被定义的类时自动调 用,通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类, __autoload()函数接收的一个参数,就是你想加载的类的 类名,所以你做项目时,在组织定义类的文件名时,需要按照一定的规则,最好以类名为中心,也可以加上统一的前缀或后缀形成文件名,比如 xxx_classname.php、classname_xxx.php以及就是classname.php等等.

    本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类

      function __autoload($classname)  
        {
            require_once $classname . '.php';
        } 
    
        //MyClass1类不存在时,自动调用__autoload()函数,传入参数”MyClass1”
        $obj  = new MyClass1(); 
    
        //MyClass2类不存在时,自动调用__autoload()函数,传入参数”MyClass2”
        $obj2 = new MyClass2();

     

    __clone():复杂对象只是简单地将一个变量赋值给另一个变量。

    class CopyMe{}
    
    $first = new CopyMe();
    $second = $first;

    在PHP4中,$second和$first是两个完全不同的对象。我们无法检查两个变量是否指向相同的对象。

    在PHP5中,$second和$first指向同一个对象。运行PHP5的代码时,两个变量指向同一个引用,没有各自保留一份相同的副本,有时候是需要的。

    PHP5中提供了clone关键字:

    class CopyMe{}
    
    $first = new CopyMe();
    $second = clone $first;//现在的$second和$first是不同的两个对象了

    但是此时还有问题,每一个Person对象都有一个标示$id,如果这个对象$id对应的是数据库中的一条信息,可能出现的情况是,两个完全不同的 对象指向数据库中的一条记录,此时,对一个对象的操作就会印象到另一个对象了。如果我们可以控制对象复制的内容,那么久可以避免上面的问题了。这就是 __clone()的用途:

    浅复制:

    复制代码
    Person{
      private $name;
      private $age;
      private $id;
    
      function __construct($name,$age){
        $this->name = $name;
        $this->age = $age;
      }
    
      function setId($id){
        $this->id = $id;
      }
      //这个函数在赋值的对象上运行
      function __clone(){//将复制的对象id设置为0
        $this->id = 0;
      }
    }
    复制代码

    当在一个Person对象上调用clone时,产生一个新的副本,并且新副本的__clone()方法会被调用:

    $person = new Person("bob",44);
    $person->setId(343);
    $person2 = clone $person;//此时的person2:name:bob age:44 id:0

    深复制:以上的浅复制,可以保证所有基本数据类型的属性被完全赋值。在复制对象属性时只复制引用,并不复制引用 的对象。如果对象有一个$balance属性,可以将该属性也复制给新副本,那么此时就有两个Person对象同时引用一个$balance。对一个操 作,就以为着另一个也改变了。

    class Account{//账单
      public $balance;//余额
      function __construct($balance){
        $this->balance = $balance;
      }
    }

    下面是Person对象,且引用了Account对象

    复制代码
    Person{
      private $name;
      private $age;
      private $id;
    
      function __construct($name,$age,Account $account){
        $this->name = $name;
        $this->age = $age;
        $this->account = $account;
      }
    
      function setId($id){
        $this->id = $id;
      }
      //这个函数在赋值的对象上运行
      function __clone(){//将复制的对象id设置为0
        $this->id = 0;
      }
    }
    复制代码

    当复制这个对象是:

    复制代码
    $person = new Person("bob",44,new Accoun(200));
    $person->setId(343);
    $person2 = clone $person;
    //给person充一些钱
    $person->account->balance +=20;
    //结果是$person2也被充了钱
    print $person2->account->balance;//220
    复制代码

    解决方法:

      //这个函数在赋值的对象上运行
      function __clone(){//将复制的对象id设置为0
        $this->id = 0;
        $this->account = clone $account;//深度clone
      }



    __sleep():串行化的时候用。
    __wakeup(): 反串行化的时候用。

      串行化serialize可以把变量包括对象,转化成连续bytes数据. 你可以将串行化后的变量存在一个文件里或在网络上传输. 然后再反串行化还原为原来的数据. 你在反串行化类的对象之前定义的类,PHP可以成功地存储其对象的属性和方法. 有时你可能需要一个对象在反串行化后立即执行. 为了这样的目的,PHP会自动寻找__sleep和__wakeup方法.

      当一个对象被串行化,PHP会调用__sleep方法(如果存在的话). 在反串行化一个对象后,PHP 会调用__wakeup方法. 这两个方法都不接受参数. __sleep方法必须返回一个数组,包含需要串行化的属性. PHP会抛弃其它属性的值. 如果没有__sleep方法,PHP将保存所有属性.

      如何用__sleep和__wakeup方法来串行化一个对象. Id属性是一个不打算保留在对象中的临时属性. __sleep方法保证在串行化的对象中不包含id属性. 当反串行化一个User对象,__wakeup方法建立id属性的新值. 这个例子被设计成自我保持. 在实际开发中,你可能发现包含资源(如图像或数据流)的对象需要这些方法。

    class User{
        public $name;
        public $id;
        
        function __construct(){
        //give user a unique ID 赋予一个不同的ID
        $this->id = uniqid();
        }
        
        function __sleep(){
            //do not serialize this->id 不串行化id
            return(array("name"));
        }
        function __wakeup(){
        //give user a unique ID
        $this->id = uniqid();
        }
    }
    
    //create object 建立一个对象
    $u = new User;
    $u->name = "Leon";
    
    //serialize it 串行化 注意不串行化id属性,id的值被抛弃
    $s = serialize($u);
    
    //unserialize it 反串行化 id被重新赋值
    $u2 = unserialize($s);
    
    //$u and $u2 have different IDs $u和$u2有不同的ID
    print_r($u);
    print_r($u2);



     
  • 相关阅读:
    Git 将当前修改提交到指定分支
    Linux 安装中文字体
    枚举的处理,MybaitsPlus+JackSon
    SpringBoot JackSon全局配置
    SQL查询数据库中所有表名
    Feign url配置/注解
    如何让py生成pyd
    第二十九篇 -- PY程序返回值问题
    解决VS2017调试卡住的问题
    第二十八篇 -- 自定义窗口切换
  • 原文地址:https://www.cnblogs.com/colorstory/p/2676662.html
Copyright © 2011-2022 走看看