opencart源码学习(一)——业务逻辑核心controller

来源:互联网 发布:办公室有老鼠 知乎 编辑:程序博客网 时间:2024/05/16 11:10

近期拖鞋接手了一个opencart的二次开发项目,所以不可避免地要把opencart本身的业务逻辑看个清楚,这对于毫无php开发基础的拖鞋来说,无疑是个蛋疼而艰巨的任务。


于是,从自学php,到把opencart的主要业务逻辑看通看懂,前前后后花了近一个星期,其中一手度娘一手gedit的辛酸自不多说,末了觉得收益良多,经验不敢独享,当然也怕自己忘记,遂记于此处,分享如下:


1.概述

正如标题所述,opencart的业务逻辑核心是controller类,源码位置opencart/system/engine/controller.php。作为一个核心业务类,controller定义了opencart所有后台业务的基本特征,从动态收集页面元素,到按照模版渲染页面


横向上,它实现了“收集”与“渲染”的业务分离,在纵向上,它采用了层层嵌套的业务组织,从而使整个页面响应流程有条不紊地进行,清晰易用。以下也将主要从这两个方向,解释controller的这种设计特征。


2.横向的业务分离

controller的横向业务分离特征主要体现在render()函数。

从上面的代码可以看到,foreach循环完成了当前controller页面元素的动态收集,它通过遍历当前controller所属的children(其实就是一个controller数组,下文有述,此处暂表不提),并用getchild()返回每个child的执行结果,然后把这些执行结果放在一个名叫data的数组中,完成收集功能。

foreach之后,紧接一个if else,判断当前controller所需模板是否存在,不存在则输出错误信息,此处我们主要看判断存在的支路,它完成了读取模板,并按照模板渲染页面的功能。

首先是extract()这个函数,度娘可知它是个php预定义函数,作用是把数组元素从键值形式转化成变量形式后,把它们统统置于当前的符号表中,举例来说,就是把数组中的a=>b转成$a=b后抛出,从而能使后面的代码使用$a(可视作临时变量,局限于当前作用域)。

从代码可知,作者正是通过使用这个神奇的函数,从而轻松地把之前存放在data中的执行结果“解包”出来,供后面require进来的模板“使用”。值得一提的是,这种“使用”过程事实上是被动的,是需要纳入模板设计者考虑的,关于这点我们随便打开一个默认模板目录(catalog/view/theme/default/template)下的模板就可以大概了解到,从data提取出的这些变量值,事实上恰恰确定了模板中所有用php书写的动态部分,也正因如此,作者才能用这么简单的几句代码就完成了controller的渲染。

此外,还有一个地方需要注意,关于ob_start()ob_get_contents()ob_end_clean()。ob其实就是缓冲区,ob_start()打开缓冲区后,此后代码的所有输出将全部放在这个用户不可见的缓冲区中,直到ob_end_clean()的出现,关闭并清除缓冲区。作者在此处用缓冲区存放渲染结果,然后再用ob_get_contents()返回缓冲区的输出内容到controller的output中,目的就是令渲染过程完成在缓冲区中,然后方便放于一个变量里,封装返回渲染结果。这对于controller纵向的层层嵌套是有重要意义的,下面也将述及这一点。

3.纵向的业务组织

controller的纵向业务组织特征是render()getchild()的联合作用。
上文已说了render(),此处重点说getchild(),下面将先理清其具体实现:

上文提过,这个函数将返回一个controller的output,条件是它需要一个child参数和一个可缺省的数组参数。这个child可以把它看成是一个controller子类的文件路径,这从它能作为action的构造参数就可以看出。


关于action,在这里我们先转过头去看看它的代码实现,它是一个同样放在engine目录下的类,其意义就是把一个controller子类的文件路径(也可能不是文件路径,而是一个精确到需调用函数的‘函数路径’),解释封装成一个指明了类名需调用函数等“动作”信息的对象。当然,action的需调用函数通常会缺省,也就是当传入的路径参数的确是一个纯粹的文件路径时,action的需调用函数会默认指定为一个名为index的函数,一个所有controller子类都“约定”会有的函数。


关于index函数,我们先不管它的具体实现,以后会有相关的展开,现在只需知道它是一个通知controller子类对象准备相关信息,然后进行渲染工作的函数即可(好吧,其实这就是它的全部意义了……)。


有了这个概念,我们就可以回头看getchild()了。很明显的,new Action之后的一堆东西其实就是调用了controller子类对象index,通知其进行渲染工作,并返回结果。


说完getchild,下面就可以展开对controller纵向特征的分析了:


首先,要说明的一点是,判断一个过程是否是嵌套机制,拖鞋习惯于关注两点,一是任务的下派,二是结果的返回,只要理清这两点,就可以很容易判断过程是否存在嵌套。


而在controller中,任务下派的过程体现在调用render时,遍历controller对象所属的每个child,并用getchild通知每个child执行工作,又因为child实际上是controller子类的对象,这个对象实现了一个准备了相关资料并调用render的index函数,所以这种通知child执行的工作事实上是层层下派的,由此,任务下派过程成立。


结果返回的过程则体现在返回getchild后,所有返回结果都存放在data数组中,而data在render中被用extract“解包”后,与模板共同作用渲染页面,然后再存放在output中。在这里考虑上文任务下派的层次关系,可以假定这个调用render的对象实际上是个controller子类对象,它的渲染任务是上一级controller分派下来的,那么,这个render应该是在这个子类对象的index中被调用,而index则在上一级controller的getchild中被调用。于是,在执行完index后,上一级controller的getchild把下一级的controller子类对象的output作为返回,从而完成逐级返回,由此,结果返回过程成立。


4.总结

要了解opencart的业务逻辑,controller的具体实现是绕不开的一道坎,而要了解controller的具体实现,拖鞋觉得从上面两个角度分析是比较好懂的,至少算是一种把灵活但凌乱的php流程化清晰化的努力,因此,才有了这篇博文。


PS.抱头多说一句,拖鞋典型新手,废话多排版乱难免,各位轻喷,欢迎指点。





原创粉丝点击