深入解析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类的执行流程
深入解析OpenCart的代理类proxy的更多相关文章
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- Java代理类Proxy的用法
代理(proxy) 利用代理可以在运行时创建一个实现了一组给定接口的新类.这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用. 何时使用代理 假设有一个表示接口的Class对象(有可能只包含一 ...
- 探索Mybatis之JDK动态代理:探究Proxy.newProxyInstance()生成的代理类解析
Mybatis的Mapper接口UserMapper 1 package com.safin.Mapper; 2 3 import com.safin.Pojo.User; 4 5 import ja ...
- Java中的动态代理以及Proxy类的偷瞄
动态代理机制 所谓动态代理,即通过代理类Proxy的代理,接口和实现类之间可以不直接发生联系,而可以在运行期(Runtime)实现动态关联. Java动态代理类位于Java.lang.reflect包 ...
- Java的动态代理(dynamic proxy)
什么是动态代理(dynamic proxy) 动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对 ...
- 代理(Proxy)模式
代理模式(Proxy):为其他对象提供一种代理以控制对这个对象的反问. * 抽象主题角色(Subject):声明了真实主题和代理主题的共同接口,这样一来在任何使用真实主题的地方都可以使用代理主题. * ...
- 结构类模式(七):代理(Proxy)
定义 为其他对象提供一种代理以控制对这个对象的访问. 代理模式也叫做委托模式,它是一项基本设计技巧.许多其他的模式,如状态模式.策略模式.访问者模式本质上是在更特殊的场合采用了委托模式,而且在日常的应 ...
- Mybatis源码解析,一步一步从浅入深(六):映射代理类的获取
在文章:Mybatis源码解析,一步一步从浅入深(二):按步骤解析源码中我们提到了两个问题: 1,为什么在以前的代码流程中从来没有addMapper,而这里却有getMapper? 2,UserDao ...
- 解析利用wsdl.exe生成webservice代理类的详解
利用wsdl.exe生成webservice代理类:根据提供的wsdl生成webservice代理类1.开始->程序->Visual Studio 2005 命令提示2.输入如下红色标记部 ...
随机推荐
- 记录一次网站漏洞修复过程(二):第一轮处理(IIS目录枚举、应用程序错误)
解决IIS目录枚举 当前的IIS版本为7.5 [IIS] => [请求筛选] => [URL]中添加 [拒绝序列] 符号 ~ 应用程序错误 在Global.asax 中添加异常处理代 ...
- 2018 年 3 月 iOS 面试总结(上市公司,BAT)
序言: 今年2月中下旬因为个人原因,换了一份工作,3月初期间面试了有3,4家,基本都是D轮或者刚刚上市的公司,也有上榜的BAT,也从他们的面试笔试中看到了自己的一些不足,于是就想写出来和大家分享一下, ...
- 简单几步优化你的windows,加快开机速度(重装windows之后要做的几件事)
每个人都想要让自己的系统运行得快一些,开机快一些,我就来说说我自己的经验,我使用的系统是windows8.1,当然这有些方法也适用于其他的系统,我每次重装完系统之后第一件事就是下面几步,当然重装系统之 ...
- 2016最热门的PHP框架
每个PHP框架都拥有各自独特的地方.同时PHP语言已经获得了巨大的认同并且成为了世界上最通用的服务器脚本语言.PHP也俨然成为了最容易学习的web动态开发语言.在PHP发展的同时,PHP框架也迅速崛起 ...
- 基于TODO的开发方法
之前买了一本书,叫<架构探险-从零开始写Java Web框架 >(不推荐购买-),一本标题党书籍!但是我很推崇作者写代码的方式,就是基于TODO的方式进行开发! 个人认为以基于TODO的方 ...
- http.request请求及在node中post请求参数解析
Post请求 var http=require('http'); var qs=require('querystring'); var post_data={a:123,time:new Date() ...
- ReflectASM-invoke,高效率java反射机制原理
前言:前段时间在设计公司基于netty的易用框架时,很多地方都用到了反射机制.反射的性能一直是大家有目共睹的诟病,相比于直接调用速度上差了很多.但是在很多地方,作为未知通用判断的时候,不得不调用反射类 ...
- Online database documentation.
贫道2018年1月正式出道,可以说在IT界我就是个菜鸟.但我有着一颗不服输的心,我相信我会在这条路走上巅峰之道的.下面我来写我的第一份学习笔记: 介绍:大多数公司都有自己的数据文档,估计大多数都是用P ...
- CSS速查列表-3-(font)字体
CSS Fonts(字体) CSS字体属性定义 1.字体:font-family 属性设置文本的字体系列.p{font-family:"Times New Roman", Time ...
- Python下载图片小程序
欢迎大侠们指正批评 思路: 1.引入相关的python文件(import re import urllib) 2.读取对应网页的html文件(使用 urllib) def getHtml(url): ...