深入解析OpenCart的代理类proxy

1.什么是代理类

  代理类指的是连接远程对象或不可见对象的接口,通常被客户端调用来连接真实的服务对象。更准确的定义参见维基百科 

2.代理的作用

  • 作为一个包装类,提供额外的功能
  • 延迟加载 

  在本文讲到的opencart框架中,从实例化的角度讲,我们认为这是延迟加载(按需加载)。当然, 我们可以认为这是为基础代理类库提供额外的功能。以下是opencart的基础代理类(文件路径:system/engine/proxy.php)

<?php
class Proxy {
    public function __get($key) {
        return $this->{$key};
    }    
    
    public function __set($key, $value) {
         $this->{$key} = $value;
    }
    
    public function __call($key, $args) {
        $arg_data = array();
        
        $args = func_get_args();
        
        foreach ($args as $arg) {
            if ($arg instanceof Ref) {
                $arg_data[] =& $arg->getRef();
            } else {
                $arg_data[] =& $arg;
            }
        }
        
        if (isset($this->{$key})) {        
            return call_user_func_array($this->{$key}, $arg_data);    
        } else {
            $trace = debug_backtrace();
            
            exit('<b>Notice</b>:  Undefined property: Proxy::' . $key . ' in <b>' . $trace[1]['file'] . '</b> on line <b>' . $trace[1]['line'] . '</b>');
        }
    }
}

在该代理类中,我们可以看到,它只声明了三个魔术方法,__get()(当获取类中不存在或者受保护的属性时,自动调用该方法),__set()(当设置类中不存在或者受保护的属性时,自动调用该方法),__call()(当调用类中不存在的方法是,自动调用该方法)。

3.代理类是怎么在opencart中的model类起到作用的呢?

  在本节中,我就以opencart中常见的调用方法$this->model_catalog_category->getCategory($category_id)为例来分析。 

  事实上,我们在调用该方法之前,我们需要执行如下一条代码:

$this->load->model('catalog/category');

  我们分析下这条代码怎么通过opencart框架执行的:

  1.$this->load在model类中没有定义,自动调用model类中的魔术方法__get(),$this->load等同于$this->registry->get(‘load’),

  2.opencart框架将所有实例化的对象注册到Register这个对象中,调用某个实例化的对象方法:$this->register->get($key),

  3.通过以上分析,我们知道此时已经进入opencart中的loader类(文件路径:system/engine/loader.php)中的model方法

public function model($route) {
    // 剔除一些非法的字符
    $route = preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route);
    
    // 触发前缀事件,如果在配置文件中配置了'model'.$route.'/before'路径,通过该方法,可以改变$route的值,
    $this->registry->get('event')->trigger('model/' . $route . '/before', array(&$route));
    
    //如果还没有注册,则进入
    if (!$this->registry->has('model_' . str_replace(array('/', '-', '.'), array('_', '', ''), $route))) { 
        $file  = DIR_APPLICATION . 'model/' . $route . '.php';
        $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', $route); //注意PHP中,类名,方法名,函数名是不区分大小写的。
        
        if (is_file($file)) {
            include_once($file);

            $proxy = new Proxy(); //实例化基础代理类
            
            foreach (get_class_methods($class) as $method) { //循环类中的方法
                $proxy->{$method} = $this->callback($this->registry, $route . '/' . $method); //返回匿名函数作为代理类属性值
            }
            
            $this->registry->set('model_' . str_replace(array('/', '-', '.'), array('_', '', ''), (string)$route), $proxy); //注册代理对象,以便可以通过$this->model_catalog_category的形式访问代理对象
        } else {
            throw new \Exception('Error: Could not load model ' . $route . '!');
        }
    }
    
    // 触发后缀事件(需要配置)
    $this->registry->get('event')->trigger('model/' . $route . '/after', array(&$route));
}

   现在我们再来分析下以下这条代码:

$this->model_catalog_category->getCategory($category_id)

  1.$this->model_catalog_category在Model类中没有找到,会自动调用Model类中的魔术方法__get(),$this->model_catalog_category等同于$this->register->get('model_catalog_category');

  2.通过上面的分析,我们已经知道将实例化的代理类注册到了Register这个对象上,因此以上代码等同于$proxy->getCategory($category_id);

  3.在proxy代理类中,由于没有getCategory()方法,自动调用了魔术方法__call();

  接下来我们自己分析一下proxy代理类中__call()方法

    public function __call($key, $args) {  //$key:方法名,$args:以数组的形式代表参数的集合
        $arg_data = array();
        

        $args = func_get_args(); //返回包含方法名以及参数的数组,即array($key,$args);
        
        foreach ($args as $arg) {
            if ($arg instanceof Ref) {

                $arg_data[] =& $arg->getRef();
            } else {
                $arg_data[] =& $arg;
            }
        }
        
        if (isset($this->{$key})) {        //$this->{$key} 指的是上文提到的通过$this->callback()返回的匿名函数
            return call_user_func_array($this->{$key}, $arg_data);    //执行该匿名函数,$arg_data:参数数组
        
        } else {
            $trace = debug_backtrace();
            
            exit('<b>Notice</b>:  Undefined property: Proxy::' . $key . ' in <b>' . $trace[1]['file'] . '</b> on line <b>' . $trace[1]['line'] . '</b>');
        }
    }

  通过call_user_func_array(),最终执行到system/engine/loader.php中callback()方法里面的代码

$output = null;
            
            // 触发前缀事件,该事件可以不改动核心代码的前提下,按照自己的需求改动做改动。(需要配置)
            $result = $registry->get('event')->trigger('model/' . $route . '/before', array(&$route, &$args, &$output));
            
            if ($result) {
                return $result;
            }
            
            // 存贮model对象
            if (!isset($model[$route])) {
                //本例中,$route是指catalog/category/getCategory
                $file = DIR_APPLICATION . 'model/' .  substr($route, 0, strrpos($route, '/')) . '.php';
                $class = 'Model' . preg_replace('/[^a-zA-Z0-9]/', '', substr($route, 0, strrpos($route, '/')));

                if (is_file($file)) {
                    include_once($file);
                
                    $model[$route] = new $class($registry); //实例化Modelcatagorycategory
                } else {
                    throw new \Exception('Error: Could not load model ' . substr($route, 0, strrpos($route, '/')) . '!');
                }
            }

            $method = substr($route, strrpos($route, '/') + 1);//方法名
            
            $callable = array($model[$route], $method);

            if (is_callable($callable)) {
                $output = call_user_func_array($callable, $args);//最终执行了Modelcategorycategory类中的getCategory()方法。
            } else {
                throw new \Exception('Error: Could not call model/' . $route . '!');
            }
            
            // 触发后缀事件(需要配置)
            $result = $registry->get('event')->trigger('model/' . $route . '/after', array(&$route, &$args, &$output));
            
            if ($result) {
                return $result;
            }
                        
            return $output;

以上就是整个opencart框架中Model类配合proxy类的执行流程

转载于:https://www.cnblogs.com/freelyflying/p/7091051.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值