zoukankan      html  css  js  c++  java
  • PHP 16 个编程法则

    HP是最好的编程语言。对于PHP开发者来说,掌握一些编程法则是十分重要的。而在PHP中,以双下划线(__)开头的方法称为魔术方法,它们扮演着非常重要的角色。

    常用的魔术方法包括:

    -__construct():类的构造方法;

    -__destruct():类的析构方法;

    -__call($funName, $arguments):当访问未定义或没有访问权限的方法时,__call()会被调用;

    -__callStatic($funName, $arguments):当访问未定义或没有访问权限的静态方法时,__call()会被调用;

    -__get($propertyName):读取类的成员变量时__get()会被调用;

    -__set($property, $value):写入类的成员变量时__set()会被调用;

    -__isset($content):当针对未定义或没有访问权限的成员使用isset()或empty()时__isset()会被调用;

    -__unset($content):在未定义或没有访问权限的成员上使用reset()时__unset()会被调用;

    -__sleep():在执行serialize()时__sleep()会被调用;

    -__wakeup():在执行deserialization()时__wakeup()会被调用;

    -__toString():使用echo方法直接输出对象时,__toString()会被调用;

    -__invoke():像调用函数一样调用对象时,对象的__invoke()会被调用;

    -__set_state($an_array):调用var_export()时__set_state()会被调用;

    -__clone():复制对象时__clone()会被调用;

    -__autoload($className):试图加载未定义的类;

    -__debuginfo():输出调试信息。

    本文将通过具体点的例子说明,这些PHP魔术方法的用法。

    1. __construct()

    PHP构造方法是对象创建之后自动调用的第一个方法。任何类都有构造方法。如果没有显式定义,那么类会有个默认的构造方法,该方法没有参数,方法体为空。

    1) 构造方法的用法

    构造函数通常用来执行初始化工作,如在创建对象时设置成员变量的初始值。

    2) 声明类的构造方法的格式

    function __constrct([参数列表]){
       方法体 // 通常用于设置成员变量的初始值
    }

    注意:同一个类只能有一个构造方法,因为PHP不支持构造方法重载。

    完整的示例如下:

    <?php
       class Person
       {                                                                    
               public $name;      
               public $age;      
               public $sex;      
           /**
            * 显示定义带有参数的构造方法
            */                                                                                      
           public function __construct($name="", $sex="Male", $age=22)
           {    
               $this->name = $name;
               $this->sex = $sex;
               $this->age = $age;
           }
           /**
            * say方法
            */
           public function say()
           {
               echo "Name:" . $this->name . ",Sex:" . $this->sex . ",Age:" . $this->age;
           }  
       }

    不使用任何参数创建对象$Person1。

    $Person1 = new Person();
    echo $Person1->say(); //输出: Name:,Sex:Male,Age:22

    使用参数"James"创建对象$Person2。

    $Person2 = new Person("Jams");
    echo $Person2->say(); // 输出: Name: Jams, Sex: Male, Age: 22

    使用三个参数创建$Person3。

    $Person3 = new Person ("Jack", "Male", 25);
    echo $Person3->say(); // 输出: Name: Jack, Sex: Male, Age: 25

    2. __destruct()

    现在我们知道了构造方法,那么相对的就是析构方法。

    析构方法可以在对象销毁之前执行一些操作,如关闭文件、清空结果集,等等。

    析构方法是PHP5引入的新特性。

    析构方法的声明格式与构造方法 __construct() 类似,就是说__destruct()也以双下划线开头,其名称也是固定的。

    1) 析构方法的声明格式

    function __destruct()
    {
       // 方法体
    }

    注意:析构方法不能带任何参数。

    2) 析构方法的用法

    一般来说,PHP中析构方法并不是太常用。在类中它是可选的,通常用于在对象销毁之前执行某些清理工作。

    下面的例子演示了如何使用析构方法:

    <?php
    class Person{    
       public $name;        
       public $age;        
       public $sex;        
       public function __construct($name="", $sex="Male", $age=22)
       {  
           $this->name = $name;
           $this->sex  = $sex;
           $this->age  = $age;
       }
       /**
        * say方法
        */
       public function say()
       {
           echo "Name:".$this->name.",Sex:".$this->sex.",Age:".$this->age;
       }  
       /**
        * 定义析构方法
        */
       public function __destruct()
       {
               echo "Well, my name is ".$this->name;
       }
    }
    $Person = new Person("John");
    unset($Person); // 销毁上面创建的$Person对象

    以上程序的输出结果为:

    Well, my name is John

    3. __call()

    该方法有两个参数。第一个参数$function_name自动接收未定义方法的名称,第二个参数$arguments以数组的方式接收该方法调用的多个参数。

    1) __call()方法的用法

    function __call(string $function_name, array $arguments)
    {
       // 方法体
    }

    程序中调用未定义的方法时,__call()方法会自动被调用。

    示例如下:

    <?php
    class Person
    {                            
       function say()
       {
              echo "Hello, world!<br>";
       }    
       function __call($funName, $arguments)
       {
             echo "The function you called:" . $funName . "(parameter:" ;  // 输出不存在的方法的名称
             print_r($arguments); // 输出不存在的方法的参数列表
             echo ")does not exist!!<br> ";                  
       }                                        
    }
    $Person = new Person();          
    $Person->run("teacher"); // 如果对象内不存在的方法被调用,则 __call() 方法会被自动调用
    $Person->eat("John", "apple");            
    $Person->say();

    输出结果如下:

    The function you called: run (parameter: Array([0] => teacher)) does not exist!
    The function you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
    Hello world!

    4. __callStatic()

    当程序中调用未定义的静态方法时,__callStatic()方法会被调用。

    __callStatic()的用法与__call()类似。示例如下:

    <?php
    class Person
    {
       function say()
       {
           echo "Hello, world!<br>";
       }
       public static function __callStatic($funName, $arguments)
       {
           echo "The static method you called:" . $funName . "(parameter:" ;  // 输出不存在的方法的名称
           print_r($arguments); // 输出不存在的方法的参数列表
           echo ")does not exist!<br> ";
       }
    }
    $Person = new Person();
    $Person::run("teacher"); // 如果对象内不存在的方法被调用,则 __callStatic() 方法会被自动调用
    $Person::eat("John", "apple");
    $Person->say();

    输出结果如下:

    The static method you called: run (parameter: Array([0] => teacher)) does not exist!
    The static method you called: eat (parameter: Array([0] => John[1] => apple)) does not exist!
    Hello world!

    5. __get()

    当试图访问外部对象的私有属性时,程序会抛出异常并结束执行。但我们可以使用__get()方法来解决这个问题。它能在对象外部取得对象的私有方法。示例如下:

    <?php
    class Person
    {
       private $name;
       private $age;
       function __construct($name="", $age=1)
       {
           $this->name = $name;
           $this->age = $age;
       }
       public function __get($propertyName)
       {  
           if ($propertyName == "age") {
               if ($this->age > 30) {
                   return $this->age - 10;
               } else {
                   return $this->$propertyName;
               }
           } else {
               return $this->$propertyName;
           }
       }
    }
    $Person = new Person("John", 60);   // 用Person类初始化对象,并通过构造方法给属性赋初始值
    echo "Name:" . $Person->name . "<br>";   // 访问私有属性时, __get() 方法会自动被调用,这样就能间接取得属性值
    echo "Age:" . $Person->age . "<br>";    // __get() 方法自动被调用,并返回不同的值

    输出结果如下:

    Name: John
    Age: 50

    6. __set()

    __set($property, $value) 方法用来设置对象的私有属性。当试图设置对象中未定义的属性时,就会触发__set()方法,调用参数为被设置的属性名和属性值。

    示例代码如下:

    <?php
    class Person
    {
       private $name;
       private $age;
       public function __construct($name="",  $age=25)
       {
           $this->name = $name;
           $this->age  = $age;
       }
       public function __set($property, $value) {
           if ($property=="age")
           {
               if ($value > 150 || $value < 0) {
                   return;
               }
           }
           $this->$property = $value;
       }
       public function say(){
           echo "My name is ".$this->name.",I'm ".$this->age." years old";
       }
    }
    $Person=new Person("John", 25); // 注意下面的代码会改变初始值
    $Person->name = "Lili";     // "name" 属性成功赋值。如果没有 __set() 方法,程序就会抛出异常
    $Person->age = 16; // "age" 属性成功赋值
    $Person->age = 160; // 160 是个非法值,所以赋值失败
    $Person->say();  // 输出:My name is Lili, I'm 16 years old.

    下面是输出结果:

    My name is Lili, I'm 16 years old

    7. __isset()

    在介绍__isset()方法之前,我先介绍下issset()方法。isset()方法主要用于判断某个变量是否被设置。

    在对象外部使用isset()方法有两种情况:

    • 如果参数是公有属性,那么可以利用isset()方法判断属性是否被设置;

    • 如果参数是私有属性,isset()方法将无法使用。

    那么,是否有办法判断私有属性被设置呢?当然,只需要在类里定义__isset()方法,就可以在对象外部利用isset()方法判断某个私有属性是否被设置了。

    对未定义或没有权限访问的属性调用isset()或empty()时,就会调用__isset()方法。示例如下:

    <?php
    class Person
    {
       public $sex;
       private $name;
       private $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       /**
        * @param $content
        *
        * @return bool
        */
       public function __isset($content) {
           echo "The {$content} property is private,the __isset() method is called automatically.<br>";
           echo  isset($this->$content);
       }
    }
    $person = new Person("John", 25); // 赋初始值
    echo isset($person->sex),"<br>";
    echo isset($person->name),"<br>";
    echo isset($person->age),"<br>";

    输出结果如下:

    1
    The name property is private,the __isset() method is called automatically.
    1
    The age property is private,the __isset() method is called automatically.
    1

    8. __unset()

    与__isset()类似,在未定义或无权限访问的属性上调用unset()方法时会触发__unset()方法。示例如下:

    <?php
    class Person
    {
       public $sex;
       private $name;
       private $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       /**
        * @param $content
        *
        * @return bool
        */
       public function __unset($content) {
           echo "It is called automatically when we use the unset() method outside the class.<br>";
           echo  isset($this->$content);
       }
    }
    $person = new Person("John", 25); // 赋初始值
    unset($person->sex),"<br>";
    unset($person->name),"<br>";
    unset($person->age),"<br>";

    输出结果如下:

    It is called automatically when we use the unset() method outside the class.
    1
    It is called automatically when we use the unset() method outside the class.
    1

    9. __sleep()

    serialize()方法会检查类中是否存在__sleep()魔术方法。如果存在,就会调用该方法来执行序列化操作。

    __sleep()方法通常用来在保存数据之前指定哪些属性需要被序列化。如果对象中包含一些完全不需要序列化的巨大对象,__sleep()就能派上用场了。

    具体用法请参考以下代码:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       /**
        * @return array
        */
       public function __sleep() {
           echo "It is called when the serialize() method is called outside the class.<br>";
           $this->name = base64_encode($this->name);
           return array('name', 'age'); // 返回值中的元素必须是属性的名称
       }
    }
    $person = new Person('John'); // Initially assigned.
    echo serialize($person);
    echo '<br/>';

    输出结果如下:

    It is called when the serialize() method is called outside the class.
    O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}

    10. __wakeup()

    与__sleep()方法相对的就是__wakeup()方法,常用来反序列化,如重建数据连接,或执行其他初始化操作等。

    示例如下:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       /**
        * @return array
        */
       public function __sleep() {
           echo "It is called when the serialize() method is called outside the class.<br>";
           $this->name = base64_encode($this->name);
           return array('name', 'age'); // 返回值中的元素必须是属性的名称
       }
       /**
        * __wakeup
        */
       public function __wakeup() {
           echo "It is called when the unserialize() method is called outside the class.<br>";
           $this->name = 2;
           $this->sex = 'Male';
           // 这里不需要返回数组
       }
    }
    $person = new Person('John'); // 赋初始值
    var_dump(serialize($person));
    var_dump(unserialize(serialize($person)));

    输出结果如下:

    It is called when the serialize() method is called outside the class.
    string(58) "O:6:"Person":2:{s:4:"name";s:8:"5bCP5piO";s:3:"age";i:25;}"
    It is called when the unserialize() method is called outside the class.
    object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> int(2) ["age"]=> int(25) }

    11. __toString()

    使用echo方法直接输出对象时会调用其__toString()方法。

    注意:该方法必须返回字符串,否则会抛出"E_RECOVERABLE_ERROR"级别的异常。在__toString()方法中也不能抛出异常。

    示例如下:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       public function __toString()
       {
           return  'go go go';
       }
    }
    $person = new Person('John'); // 赋初始值
    echo $person;

    返回结果如下:

    go go go

    如果类中没有定义__toString()会怎样?我们来试试看。

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
    }
    $person = new Person('John'); // 赋初始值
    echo $person;

    返回结果如下:

    Catchable fatal error: Object of class Person could not be converted to string in D:phpStudyWWW	estindex.php on line 18

    可见,它会在页面上报告致命错误,说明这种用法不允许。

    12. __invoke()

    当试图用调用函数的方式调用对象时,就会自动调用其__invoke()方法。

    注意:该功能只在PHP 5.3.0以及以上版本上有效。

    示例如下:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       public function __invoke() {
           echo 'This is an object';
       }
    }
    $person = new Person('John'); // 赋初始值
    $person();

    输出结果如下:

    This is an object

    如果在未定义__invoke()方法的情况下将对象作为函数使用,就会得到以下的结果:

    Fatal error: Function name must be a string in D:phpStudyWWW	estindex.php on line 18

    13. __set_state()

    从PHP 5.1.0开始,__set_state()方法会在调用var_export()导出类代码时自动被调用。

    __set_state()方法的参数是个数组,包含所有属性的值,格式为array('property' => value, ...)。

    下面的示例中没有定义__set_state()方法:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
    }
    $person = new Person('John'); // 赋初始值
    var_export($person);

    输出结果如下:

    Person::__set_state(array( 'sex' => 'Male', 'name' => 'John', 'age' => 25, ))

    可见,输出结果是对象的属性。

    下面来看看如果定义了__set_state()方法会怎样:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       public static function __set_state($an_array)
       {
           $a = new Person();
           $a->name = $an_array['name'];
           return $a;
       }
    }
    $person = new Person('John'); // 赋初始值
    $person->name = 'Jams';
    var_export($person);

    输出结果如下:

    Person::__set_state(array( 'sex' => 'Male', 'name' => 'Jams', 'age' => 25, ))

    14. __clone()

    PHP中可以使用clone关键字来复制对象,其格式如下:

    $copy_of_object = clone $object;

    但是,clone关键字只会进行浅复制,所有引用的属性依然会指向原来的变量。

    如果对象里定义了__clone()方法,那么复制时就会调用__clone()方法,从而允许我们修改被复制的值(如果需要的话)。

    示例如下:

    <?php
    class Person
    {
       public $sex;
       public $name;
       public $age;
       public function __construct($name="",  $age=25, $sex='Male')
       {
           $this->name = $name;
           $this->age  = $age;
           $this->sex  = $sex;
       }
       public function __clone()
       {
           echo __METHOD__."your are cloning the object.<br>";
       }
    }
    $person = new Person('John'); // 赋初始值
    $person2 = clone $person;
    var_dump('persion1:');
    var_dump($person);
    echo '<br>';
    var_dump('persion2:');
    var_dump($person2);

    输出结果如下:

    Person::__clone your are cloning the object.
    string(9) "persion1:" object(Person)#1 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }
    string(9) "persion2:" object(Person)#2 (3) { ["sex"]=> string(3) "Male" ["name"]=> string(6) "John" ["age"]=> int(25) }

    15. __autoload()

    __autoload()方法可以尝试加载未定义的类。

    以前,如果在整个程序的生命周期内创建100个对象,就要用include()或require()包含100个类文件,或者在同一个类文件内定义100个类,例如:

    /**
    * file non_autoload.php
    */
    require_once('project/class/A.php');
    require_once('project/class/B.php');
    require_once('project/class/C.php');
    .
    .
    .
    if (ConditionA) {
       $a = new A();
       $b = new B();
       $c = new C();
       // …
    } else if (ConditionB) {
       $a = newA();
       $b = new B();
       // …
    }

    那么使用__autoload()方法呢?

    /**
    * 文件 autoload_demo.php
    */
    function  __autoload($className) {
       $filePath = “project/class/{$className}.php”;
       if (is_readable($filePath)) {
           require($filePath);
       }
    }
    if (ConditionA) {
       $a = new A();
       $b = new B();
       $c = new C();
       // …
    } else if (ConditionB) {
       $a = newA();
       $b = new B();
       // …
    }

    当PHP引擎第一次使用类A时,如果A没有找到,就会调用__autoload方法,参数为类名"A"。然后我们需要在__autoload()方法中根据类名找到相应的类文件并包含该文件。如果文件没有找到,PHP引擎就会抛出异常。

    16. __debugInfo()

    执行var_dump()方法的时候会调用__debugInfo()方法。如果__debugInfo()没有定义,则var_dump()方法会输出对象中的所有属性。

    示例如下:

    <?php
    class C {
       private $prop;
       public function __construct($val) {
           $this->prop = $val;
       }
       /**
        * @return array
        */
       public function __debugInfo() {
           return [
               'propSquared' => $this->prop ** 2,
           ];
       }
    }
    var_dump(new C(42));

    输出结果如下:

    object(C)#1 (1) { ["propSquared"]=> int(1764) }

    注意:__debugInfo()方法只能用于PHP 5.6.0及更高版本。

    总结

    上面介绍了16个PHP魔术方法,其中最常用的有__set()、__get()和__autoload()。如果你还有问题,可以从PHP官方网站上获得帮助。

    原文:https://www.tutorialdocs.com/article/16-

  • 相关阅读:
    项目实战9—企业级分布式存储应用与实战MogileFS、FastDFS
    项目详解4—haproxy 反向代理负载均衡
    项目实战4—HAProxy实现高级负载均衡实战和ACL控制
    项目实战2.1—nginx 反向代理负载均衡、动静分离和缓存的实现
    zabbix设置报警通知
    zabbix创建触发器
    zabbix的启动和关闭脚本
    zabbix监控第一台服务器
    zabbix的源码安装
    Linux命令之乐--iconv
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/9066704.html
Copyright © 2011-2022 走看看