• PHP面向对象中的重要知识点(二)


    1. __toString:

        当对象被打印时,如果该类定义了该方法,则打印该方法的返回值,否则将按照PHP的缺省行为输出打印结果。该方法类似于Java中的toString()。

    <?php
    class TestClass {
        public function __toString() {
            return "This is TestClass::__toString.
    ";
        }
    }
    
    $testObj = new TestClass();
    print $testObj;

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    This is TestClass::__toString.

    2. __get和__set:

        这两个方法用于处理类中未声明的属性访问。当对象使用者试图访问未声明的对象属性时,__get()会被调用,并带有一个包含要访问的属性名称字符串作为参数。无论从__get()方法返回什么,都会直接返回给调用者,就如同带有该值的属性存在一样。另外需要注意的是,如果属性存在,但是其访问可见性为private或protected,那么这两个拦截方法同样会被调用,反之,如果属性存在切可访问,那么直接访问属性即可,这两个方法将不再会被调用。以下为__get()拦截方法的示例代码:

    <?php
    class TestClass {
        private $privateField;
        public $publicField;
        public function __construct() {
            $this->privateField = "This is a private Field.
    ";
            $this->publicField = "This is a public Field.
    ";
        }
    
        public function __get($property) {
            print "__get() is called.
    ";
            $method = "get${property}";
            if (method_exists($this, $method)) {
                return $this->$method();
            }
            return "This is undefined field.
    ";
        }
        public function getPrivateField() {
            return $this->privateField;
        }
    }
    
    $testObj = new TestClass();
    print $testObj->privateField;
    print $testObj->undefinedField;
    print $testObj->publicField;

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    __get() is called.
    This is a private Field.
    __get() is called.
    This is undefined field.
    This is a public Field.

        __set()方法被调用的规则和__get()基本相同,差别是用于拦截未定义或不可见类属性的赋值操作。另外,该方法接收两个参数,分别是属性名称和要设定的值。见如下代码示例:

    <?php
    class TestClass {
        private $privateField;
        public $publicField;
        public function __construct() {
            $this->privateField = "This is a private Field.
    ";
            $this->publicField = "This is a public Field.
    ";
        }
        public function __get($property) {
            print "__get() is called.
    ";
            $method = "get${property}";
            if (method_exists($this, $method)) {
                return $this->$method();
            }
            return "This is an undefined field.
    ";
        }
        public function __set($property, $value) {
            print "__set is called.
    ";
            $method = "set${property}";
            if (method_exists($this, $method)) {
                $this->$method($value);
            } else {
                print "This is an undefined field.
    ";
            }
        }
        public function getPrivateField() {
            return $this->privateField;
        }
        public function setPrivateField($value) {
            $this->privateField = $value;
        }
    }
    
    $testObj = new TestClass();
    $testObj->privateField = "This is a private Field after set.
    ";
    $testObj->undefinedField = "This is a undefined Field after set.
    ";
    $testObj->publicField = "This is a public Field after set.
    ";
    
    print $testObj->privateField;
    print $testObj->undefinedField;
    print $testObj->publicField;

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    __set is called.
    __set is called.
    This is an undefined field.
    __get() is called.
    This is a private Field after set.
    __get() is called.
    This is an undefined field.
    This is a public Field after set.

    3. __isset和__unset:

        这两个拦截方法被调用的规则和__get()和__set()非常类似,只是用于类中不存在或不可见属性被isset()和unset()两个全局方法应用时才会被分别触发。 

    <?php
    class TestClass {
        private $privateField;
        public $publicField;
        public function __construct() {
            $this->privateField = "Defined private field";
            $this->publicField = "Defined public field";
        }
        public function __isset($property) {
            print "__isset is called.
    ";
            return isset($this->$property);
        }
        public function __unset($property) {
            print "__unset is called.
    ";
            if (isset($this->$property)) {
                unset($this->$property);
            }
        }
    }
    
    $testObj = new TestClass();
    print 'isset($testObj->privateField) is '.(isset($testObj->privateField) ? "true" : "false")."
    ";
    print 'isset($testObj->undefinedField) is '.(isset($testObj->undefinedField) ? "true" : "false")."
    ";
    print 'isset($testObj->publicField) is '.(isset($testObj->publicField) ? "true" : "false")."
    ";
    
    print "After unset......
    ";
    //下面两个函数调用后,$testObj的两个对象属性均会变为不可用。
    //另外从输出结果来看,__unset方法仅仅被调用一次,因为publicField为可见属性,所以__unset不会因该属性而被调用。
    unset($testObj->privateField);
    unset($testObj->publicField);
    
    print 'isset($testObj->privateField) is '.(isset($testObj->privateField) ? "true" : "false")."
    ";
    print 'isset($testObj->publicField) is '.(isset($testObj->publicField) ? "true" : "false")."
    ";

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    __isset is called.
    isset($testObj->privateField) is true
    __isset is called.
    isset($testObj->undefinedField) is false
    isset($testObj->publicField) is true
    After unset......
    __unset is called.
    __isset is called.
    isset($testObj->privateField) is false
    __isset is called.
    isset($testObj->publicField) is false

    4. __call:

        __call()方法是一个非常有用但又非常容易被滥用的拦截方法。当对象使用者试图访问当前对象未定义的成员函数时,__call()会被自动调用,同时传递两个参数,分别为函数名称和传递给调用函数的所有参数(数组)。__call方法返回的任何值都会返回给函数调用者,就如同该成员函数真实存在一样。下面给出一个非常有用的委托示例。 

    <?php
    class DelegateClass {
        function printMessage($arg1, $arg2) {
            print "DelegateClass:delegatedMethod is called.
    ";
            print '$arg1 = '.$arg1.'and $arg2 = '.$arg2."
    ";
        }
    }
    class TestClass {
        private $delegateObj;
        public function __construct() {
            $this->delegateObj = new DelegateClass();
        }
        public function __call($method, $args) {
            $this->delegateObj->$method($args[0],$args[1]);
        }
    }
    
    $testObj = new TestClass();
    $testObj->printMessage("hello","world");

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    DelegateClass:delegatedMethod is called.
    $arg1 = helloand $arg2 = world

        从以上示例可以看出,TestClass并未声明printMessage成员方法,但是通过__call()方法的巧妙桥接直接传递给了委托对象。个人认为该技巧为双刃剑,切勿过度使用。

    5. 回调函数: 

        回调函数的应用场景无须多述,在C/C++中充斥着无数的回调函数典型用例。 这里只是简单给出PHP中回调函数的使用规则。见如下示例代码和关键性注释: 

    <?php
    class Product {
        public $name;
        public $price;
        public function __construct($name, $price) {
            $this->name = $name;
            $this->price = $price;
        }
    }
    
    class ProcessSale {
        private $callbacks;
        function registerCallback($cb) {
            if (!is_callable($cb)) {
                throw new Exception("callback not callable.");
            }
            $this->callbacks[] = $cb;
        }
        function sale($product) {
            print "{$product->name}: processing 
    ";
            foreach ($this->callbacks as $cb) {
                //以下两种调用方式均可。
                call_user_func($cb, $product);
                $cb($product);
            }
        }
    }
    
    $logger = function($product) {
        print "    logging ({$product->name})
    ";
    };
    
    $processor = new ProcessSale();
    $processor->registerCallback($logger);
    $processor->sale(new Product("shoes",6));
    print "
    ";
    $processor->sale(new Product("coffee",6));

        运行结果如下:

    Stephens-Air:Desktop$ php Test.php 
    shoes: processing 
        logging (shoes)
        logging (shoes)
    
    coffee: processing 
        logging (coffee)
        logging (coffee)

    6. use(闭包):

        在Javascript中存在大量的闭包应用,PHP中的闭包则是通过use关键字来完成的。对于闭包这个概念本身而言,简要的说就是函数内的代码可以访问其父作用域中的变量。见如下示例代码和关键性注释:

    <?php
    class Product {
        public $name;
        public $price;
        public function __construct($name, $price) {
            $this->name = $name;
            $this->price = $price;
        }
    }
    
    class ProcessSale {
        private $callbacks;
        function registerCallback($cb) {
            if (!is_callable($cb)) {
                throw new Exception("callback not callable.");
            }
            $this->callbacks[] = $cb;
        }
        function sale($product) {
            print "{$product->name}: processing 
    ";
            foreach ($this->callbacks as $cb) {
                $cb($product);
            }
        }
    }
    
    class Totalizer {
        static function warnAmount($amt) {
            $count = 0;
            //注意这里的$amt和$count均为闭包变量,其中&$count是以引用的形式传递的,即一旦函数内部修改了该变量的值,
            //那么下次再访问该闭包变量时,$count将为之前调用中修改后的值。
            return function($product) use($amt, &$count) {
                $count += $product->price;
                print "     count: $count
    ";
                if ($count > $amt) {
                    print "     high price reached: {$count}
    ";
                }
            };
        }
    }
    
    $processor = new ProcessSale();
    $processor->registerCallback(Totalizer::warnAmount(8));
    $processor->sale(new Product("shoes",6));
    $processor->sale(new Product("coffee",6));

        运行结果如下:

    shoes: processing 
         count: 6
    coffee: processing 
         count: 12
         high price reached: 12

    注:该Blog中记录的知识点,是在我学习PHP的过程中,遇到的一些PHP和其他面向对象语言相比比较独特的地方,或者是对我本人而言确实需要簿记下来以备后查的知识点。虽然谈不上什么深度,但是还是希望能与大家分享。

  • 相关阅读:
    java.lang.NoClassDefFoundError: org.junit.runner.Runner
    SpringMVC 八大注解
    spring @autowired使用总结
    Git使用入门
    N21_栈的压入弹出序列。
    N20_包含min函数的栈
    N19_顺时针打印指针
    N17_判断树B是不是树A的子结构
    N16_合并两个排序链表
    N15_反转链表后,输出新链表的表头。
  • 原文地址:https://www.cnblogs.com/orangeform/p/3498492.html
Copyright © 2020-2023  润新知