• PHP重载以及Laravel门面Facade



    Laravel提供了许多易用的Facade,让我们用起来特步顺手,那么这些Facade的原理是什么呢?
    其实是使用了PHP的重载。

    重载的概念

    PHP所提供的"重载"(overloading)是指动态地"创建"类属性和方法。

    在PHP里,是通过魔术方法(magic methods)来实现的。而在C++/Java里重载指的是,一个类中多个名字相同而参数类型列表不相同的方法。Lua中的元表,倒是和PHP的重载概念类似,对于学习过C++/Java的同学,可以把PHP中的重载当作新的概念即可,不必纠结为啥和C++/Java的不同。

    魔术方法中的重载

    在PHP中,魔术方法有很多,用于重载的有下面这些

    属性重载

    方法 作用
    __set 赋值不可访问属性时
    __get 读取不可访问属性时
    __isset 对不可访问属性调用 isset() 或 empty() 时
    __unset 对不可访问属性调用 unset() 时

    如:(代码来自官方文档)

    class PropertyTest {
         /**  被重载的数据保存在此  */
        private $data = array();
    
     
         /**  重载不能被用在已经定义的属性  */
        public $declared = 1;
    
         /**  只有从类外部访问这个属性时,重载才会发生 */
        private $hidden = 2;
    
        public function __set($name, $value) 
        {
            echo "Setting '$name' to '$value'
    ";
            $this->data[$name] = $value;
        }
    
        public function __get($name) 
        {
            echo "Getting '$name'
    ";
            if (array_key_exists($name, $this->data)) {
                return $this->data[$name];
            }
    
            $trace = debug_backtrace();
            trigger_error(
                'Undefined property via __get(): ' . $name .
                ' in ' . $trace[0]['file'] .
                ' on line ' . $trace[0]['line'],
                E_USER_NOTICE);
            return null;
        }
    
        /**  PHP 5.1.0之后版本 */
        public function __isset($name) 
        {
            echo "Is '$name' set?
    ";
            return isset($this->data[$name]);
        }
    
        /**  PHP 5.1.0之后版本 */
        public function __unset($name) 
        {
            echo "Unsetting '$name'
    ";
            unset($this->data[$name]);
        }
    
        /**  非魔术方法  */
        public function getHidden() 
        {
            return $this->hidden;
        }
    }
    
    
    echo "<pre>
    ";
    
    $obj = new PropertyTest;
    
    $obj->a = 1;
    echo $obj->a . "
    
    ";
    
    var_dump(isset($obj->a));
    unset($obj->a);
    var_dump(isset($obj->a));
    echo "
    ";
    
    echo $obj->declared . "
    
    ";
    
    echo "Let's experiment with the private property named 'hidden':
    ";
    echo "Privates are visible inside the class, so __get() not used...
    ";
    echo $obj->getHidden() . "
    ";
    echo "Privates not visible outside of class, so __get() is used...
    ";
    echo $obj->hidden . "
    ";
    ?>
    

    以上输出

    Setting 'a' to '1'
    Getting 'a'
    1
    
    Is 'a' set?
    bool(true)
    Unsetting 'a'
    Is 'a' set?
    bool(false)
    
    1
    
    Let's experiment with the private property named 'hidden':
    Privates are visible inside the class, so __get() not used...
    2
    Privates not visible outside of class, so __get() is used...
    Getting 'hidden'
    
    
    Notice:  Undefined property via __get(): hidden in  on line 70 in  on line 29
    

    注意只有当这个属性不存在时才被重载。

    方法重载

    方法 作用
    __call 在对象中调用一个不可访问方法时
    __callStatic 在静态类调用一个不可访问方法时

    例:(修改来自官方文档代码)

    <?php
    class MethodTest 
    {
        public function __call($name, $arguments) 
        {
            // 注意: $name 的值区分大小写
            echo "Calling object method '$name' "
                 . implode(', ', $arguments). "
    ";
        }
    
        /**  PHP 5.3.0之后版本  */
        public static function __callStatic($name, $arguments) 
        {
            // 注意: $name 的值区分大小写
            echo "Calling static method '$name' "
                 . implode(', ', $arguments). "
    ";
        }
    
        public function say()
        {
            echo "hello
    ";
        }
    }
    
    $obj = new MethodTest;
    $obj->runTest('in object context');
    
    MethodTest::runTest('in static context');  // PHP 5.3.0之后版本
    ?>
    

    输出

    Calling object method 'runTest' in object context
    Calling static method 'runTest' in static context

    注意当存在类方法时,静态调用是不会调用__callStatic的,如调用MethodTest::say();会报错

    Strict Standards: Non-static method MethodTest::say() should not be called statically

    Laravel中的Facade

    在Laravel中,Facade模式可以直接方便的使用静态方法,来调用对应的业务类。实际上Facade就是利用了__callStatic特性。
    让我们自己实现一个Facade。
    1、Facade父类,主要是__callStatic 重载

    abstract class Facade{
        
        public static function getInstance(){
            die("child class should implement getInstance");
        }
    
        public static function __callStatic($name, $arguments)
        {
            $instance = static::getInstance();
            if(!$instance){
                die("instance not init");
            }
            if(method_exists($instance,$name)){
                $instance->$name(...$arguments);
    // 或者          call_user_func_array([$instance,$name],$arguments);
            }else{
                echo "function $name is not exist";
            }
        }
    }
    

    2、业务类,具体的实现,这里我们简单的输出一句话

    class DemoService {
        public function hello($name)
        {
            echo "hello $name
    ";
        }
    }
    

    3、调用的门面,继承Facade父类,实现getInstance即可

    class Demo extends Facade{
        public static function getInstance()
        {
            return new DemoService();
        }
    }
    

    4、测试Demo::hello("world");,输出

    hello world

    当然在Laravel中稍微要复杂点,通过容器来动态获取业务类的实例。

    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }
    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }
    
        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }
    
        return static::$resolvedInstance[$name] = static::$app[$name];
    }
    

    而这个$app 在框架启动时通过 Facade::setFacadeApplication($app); 赋值,实际就是容器。

    扩展 谈谈__invoke

    魔术方法中另外一个很有趣的方法是__invoke,在C++中类似功能叫做函数对象(Function Object)。

    示例:

    class Add{
        public function __invoke($a,$b)
        {
           return $a+$b;
        }
    }
    
    $add = new Add();
    echo $add(1,3);
    

    函数对象有什么用呢?在C++中主要用来实现算法库STL,其实在PHP中也可以这么做。

  • 相关阅读:
    3D酷炫翻牌效果
    鼠标的默认事件之oncontextmenu及其案例
    键盘事件之keyCode
    用js和jQuery分别实现选项卡功能
    js中事件的绑定与解绑:attachEvent/detachEvent、addEventListener/removeEventListener
    js中的键盘事件:onkeydown、onkeypress、onkeyup
    用javaDBF操作(读、写)DBF文件
    Linux-tar命令
    动态查询
    java中对list进行分页显示数据到页面
  • 原文地址:https://www.cnblogs.com/xdao/p/php_overloading.html
Copyright © 2020-2023  润新知