标签:shopex wordpress mywife.cc mvc架构分析
此文档旨在为 shopex 二次开发提供一个便利的参考信息,借以打开 shopex 架构的大门,希望能带给读者一种登堂入室的感觉。由于 shopex 是半开放源代码的形式发布的,而且官方也没有意思要做好文档的公开工作,因此,二次开发中难题较多,这篇文章正是出于答疑解难的观点编写的。如果对诸位有大有裨益,则我会心花怒放;即是小有启发,也是我写作的最大满足;如果不才,读者未可以领略到一丝一息的有用内容,口下留情,适当一批或一评即好。本文要求读者有 PHP、HTML 语言的基础,不扎实也是可以的,还要求一点 MVC 编辑模型的理解,如果会 Wordpress 开发的说,那就肯定没问题了。
……
注意:此文非完整版,省略号表示原文内容在此省略!如有前后不接应,请看客走马观花,skip it next!
磨刀不误砍柴工!在正式了解ShopEx模板机制之前,我们很有必要了解一下ShopEx的文件结构及文件目录在shopex48系列和分销王、店掌柜等产品中,文件目录大体相同,介绍一下模板相关的几个文件夹。以ShopEx4.8.5系统文件目录为例:
static文件夹
系统资源文件夹,这里面包含ShopEx系统的公共资源,也包括了系统前台的背景图片,css和js文件。在制作模板时,不建议修改这里的任何文件,而是希望通过模板层级的css定义和css覆盖法则来达到想要的效果
plugin下的widgets文件夹
挂件文件夹,这里每一个文件夹就是一个挂件!在ShopEx48系统里,这里通常会包含系统挂件和模板开发者挂件;明眼人都知道,挂件才是ShopEx模板的核心。
home下的cache文件夹
缓存文件夹。这个文件夹下包含admin_tmpl和front_tmpl两个文件夹,一个是后台缓存,一个是前台缓存。可以手动删除里面的所有缓存内容文件,只保留两个空文件夹"admin_tmpl"和"front_tmpl",也可以通过后台 关于菜单的缓存选项卡来删除。修改挂件之后需要预览前台效果,记得要提前清空这里的缓存,或是直接通过配置 /config/config.php 文件禁止缓存!
define(‘WITHOUT_CACHE‘,false);
这句话是配置文件控制模板后台缓存的。一般模板开发,你需要将false该成ture,改成数字“1”也等价 true。这样在后台网店的缓存选项里,是看到“缓存已关闭”。
core下的shop下的view文件夹
系统页面文件夹,说的简单一点就是你经常在可视化编辑里看到的“系统功能区块,无法可视化编辑”的html内容,所有页面的对应的html在这里都能找到。友情提示:非专业前端人员,请勿随意修改!
themes文件夹
模板文件夹,同理,这里每一个文件夹就是一个模板,我们再来观察模板文件夹的构成:
了解一下 info.xml 的配置内容:
……
随后我们在 theme.xml 和 info.xml 两个文件里关联一下"notice_border.html"这个文件,主要是 info.xml ,theme.xml 中的 borders 部分可以清空:
<?xml version="1.0" encoding="UTF-8" ?> <theme> <name>Reduck v1.0</name> <id>reduck</id> <version>ShopEx4.8</version> <info></info> <author>Jimbo</author> <site>mailto:jimboyeah(AT)gmail.com</site> <update_url></update_url> <borders> <set key="零像素边框" tpl="borders/divwrap.html" /> <set key="首页搜索栏" tpl="borders/searchbar.html" /> <set key="首页商品分类" tpl="borders/frame-nav.html" /> <set key="首页商品展示" tpl="borders/frame-banner.html" /> <set key="首页商品分区" tpl="borders/frame-food.html" /> <set key="首页地图" tpl="borders/frame-map.html" /> <set key="全宽圆角边框" tpl="borders/frame-activity.html" /> <set key="虚拟商品分类" tpl="borders/virtual-cats.html" /> <set key="侧栏购物车" tpl="borders/cart-items.html" /> <set key="侧栏菜单" tpl="borders/nav-menu.html" /> <set key="通知边框" tpl="borders/notice_border.html" /> </borders> <views></views> <config> <set key="color_1" value="#ff0000" label="红色" /> <set key="image_1" value="images/bg.jpg" label="背景图片" /> <set key="style_1"
type="fullstyle"
value="images/blue.css" label="蓝色"
color="#0000ff" preview="blue.jpg"/> </config> </theme>
到此时,可视化编辑里,依然没有我这个边框,这是因为 xml 中的数据是在 shopex 第一次读取时缓存下来的。为了清除模板缓存,需要在模板管理中使用 编辑模板》恢复默认 操作来实现,这个动作会将自己操作的可视化配置内容也一并清除,因此你可以先下载模板备份。为了深入了解模板机制的动作,可以阅读和模板相关的数据模块或视图代码,这些数据模块包含了模板的配置功能,如恢复模板功能的 resetTheme 函数,模板的肤色设置 template-color:
……首先,说说模板下的theme.xml,所有模板的配置信息都是存在这个 xml 中的,包括板块的位置、配置信息,边框的信息等等。第一次加载模板时,theme.xml中包含的信息被读入数据库,即模板的初始化,之后用户便可以在可视化界面中进行板块的增删改和对原有的板块进行配置。配置信息保存在数据库中,用户可以通过导出模板来下载到最新的带配置信息的theme.xml,通过“恢复默认”来重新读取 theme.xml 和 info.xml 的设置信息,以达到还原模板的功能。系统会自动导出一个tgz的文件,里面带有类 widgets 的配置数据的文件。在制作模板时会经常要使用模板编辑》恢复默认来重新读取 theme.xml 和 info.xml 的配置信息,以使修改内容重新生效。如有必要再通过后台可视化编辑配置后,再次使用模板下载功能,将自动化配置信息保存到模板中。模板开发者注意,模板中的widgets是具有序号属性的,按其在模板文件中出现的先后作为一个序号在 theme.xml 中的 widget 节点的 slot 属性出现。file 属性则指示渲染页面时 widgets 将要嵌入的文件,baseid 属性指定和对应模板文件的 widgets 的 id 值,因此相同的 baseid 值可以在不同模板文件中出现。type 属性指示 widgets 的类型,即在可视编辑时选择的板块类型。在增删模板的 widgets 配置后,会导致 shopex 可视配置无法正确运行,需要对 theme.xml 进行修改。另外,通过后台新建模板文件时,会从 default.html 拷贝一分蓝本。知道这些信息后,就可以手动配置模板的板块设置了,以下就是当前页面位置的导航条,即 shopex 系统的 商品分类路径显示 板块的配置信息:
<widget file="default.html" slot="0" type="nav" order="10" title="" domid="" border="__none__" classname="" tpl="default.html" baseid="nav">a:0:{}</widget> <widget file="page.html" slot="0" type="nav" order="9" title="" domid="" border="__none__" classname="" tpl="default.html" baseid="nav">a:0:{} </widget>
相看相应板块的源文件会有助于理解这些参数的使用,例如,goodscar 商品分类板块,找到源文件对应的目录 \plugins\widgets\goodscat 。里面的 _config.html 文件就是配置页的细节,还有 default.html、dropdown.html、toggle.html 三个模板文件对应了默认、折叠、弹出三种输出方式,在 widgets.php 文件中也可以看到这些模板信息。需要注意的一个问题是,xml 中 & 作为一个保留符号,是不能在节点名称、属性中出现的,但是 shopex 却直接将 widgets 的配置参数的 & 符号存储到了 widget 节点内。这样的节点在 theme.xml 是无效的,因为重置模板时会发现根本不起作用。解决问题的方法是,手动将节点出现 & 字符的地方修改成转义符号格式,即将 & 符号替换成 &。
在手动生成 widget 配置信息时,可以使用 php 脚本来辅助。例如,我要在 page-app.html 文件的 <{widget id="iphone_app"}> 位置配置一个自定义的 HTML 内容,即 usercustom 板块配置,它只有一个参数 usercustom,和版块同名。接下一就要使用 php 的 serialize 来生成配置内容:
<?php header("content-type: plain/text"); $s = array("usercustom"=>‘<a href="#iphone-app">下载</a>‘); echo htmlspecialchars(serialize($s),ENT_QUOTES); ?>
通过 php 解析后得到串行化的数据。或者使用 editplus,配置一下用户工具,添加一个 PHP 命令工具,参数设置为 -f $(FilePath),那么就可以直接使用的工具来得到串行化数据。
a:1:{s:10:"usercustom";s:32:"<a href="#iphone-app">下载</a>";}
将其放入 theme.xml 后就是类似这样的节点:
<widget file="page-app.html" slot="0" type="usercustom" order="1" title="" domid="" border="borders/divwrap.html" classname="" tpl="default.html" baseid="app_iphone"> a:1:{s:10:"usercustom";s:32:"<a href="#iphone-app">下载</a>";} </widget>
一键换肤机制的原理现在市面上都差不多,通过更换不同的CSS文件来更换不同的色系风格。ShopEx模板机制秉承了这一先进理念,在ShopEx48系列和EcStore上都得到了完美的传承。如果使用了 TexturePacker 来做 CSS Sprite ,那做一键换肤就更加便利,非常感谢 Andreas Loew 可以编写一个这么强大而又小巧的工具,如果要我用一句话来形容,想来只能是和市面动则上GB的软件相比,它的大小和功能实在是不成比例啊!
……
<config> <set key="style_1" type="fullstyle" value="images/blue.css" label="蓝色" color="#0000ff" preview="blue.jpg"/> <set key="style_2" type="fullstyle" value="images/green.css" label="绿色" color="#32ac16" preview="green.jpg"/> <set key="style_3" type="fullstyle" value="images/yellow.css" label="黄色" color="#f6ff00" preview="yellow.jpg"/> </config>
使用皮肤设置后,系统会在 <{header}> 输出 JavaScript 代码来加载肤色样式文件:
window.addEvent(‘domready‘,function(){
new Element(‘link‘,{href:‘YOUR.CSS‘,type:‘text/css‘,rel:‘stylesheet‘}).injectBottom(document.head);
});
smarty这个单词我觉得是很美的,它也是一个 php 模板库的名称。在 shopex 开发中其实不需要太多的了解smarty,然而要用好 shopex 必须深入理解 smarty,目前最新的版本是 smarty 3.1.18 稳定版。
ShopEx48的模板体系引入了Smarty引擎,并作了修改,在框架、版块、边框中都可以应用Smarty语法, 具体的写法是<{Smarty语句}>,如
<{foreach from=$custid item=curr_id}> id: <{$curr_id}><br> <{/foreach}>
至于挂件,亦可以称之为插件,无歧义可以用两者之一表示。首先介绍一个插件的文档结构,目录 /plugins/widgets 下一个文件夹对应一个插件,其中文件夹名称就是一个插件的标识 ,这个在存入数据库要用到。
拿goods插件作为例子:
在页面渲染时遇到有配置好的插件,系统就会调用插件入口函数,此函数要实现数据的收集,然后返回。系统会将返回的数据以 $data 变量传入插件的前台模板,即这里指的 default.html。注意,每个插件可以拥有多个前端模板,通过配置文件 widgets.php 中的 $setting[‘template‘] 指定。在入口函数内,需要做的工作更多是和系统打交道,函数的部分结构:
function widget_goods(&$setting,&$system){
$o=$system->loadModel(‘goods/products‘);
...
$config=$system->getConf(‘site.save_price‘);
...
$search = $system->loadModel(‘goods/search‘);
...
$result[‘link‘][($i-1)]=$system->mkUrl(‘gallery‘...);
...
$result[‘goods‘][]=$o->getList(null,$filter[$i],0,$limit,$order[‘sql‘]);
}
注意最后的 $result[‘goods‘][] 的赋值,它将产品对象取得的数据的记录存储到 $result 中,这样系统会将它以 $data 变量传入到前端模板页面,然后就可以通过 $data.goods 取出使用,要了解产品的字段,可以查看 shopex 系统 sdb_goods 数据表的设计。$system 是在 kernel 类实列化时产生的全局引用 /core/kernel.php 文件中,任何时候,只有一个 kernel 类的实列在运行:
include
class kernel \core\kernel.php class shop_api extends kernel \core\api\shop_api.php class adminCore extends kernel \core\include_v5\adminCore.php class aslibCore extends kernel \core\include_v5\aslibCore.php class crontab extends kernel \core\include_v5\crontab.php class shopCore extends kernel \core\include_v5\shopCore.php class ucCore extends shopCore \api\uc.php class shopPreview extends shopCore \core\include_v5\shopPreview.php class assisCore extends shopCore \core\assistant\api.php class shoppreview extends shopcore \core\
\shopPreview.php class pluginCore extends shopCore \plugins\loader.php class shopdav extends kernel \core\include_v5\shopdav.php
这个类最值得关注的方法就是 loadModel 和 getConf 方法:
……
数据模型在系统中可以说是最重要的部分之一,特别是在插件的开发中。可以说插件是前端页面和后端的桥梁,面数据模型就是桥墩!这里有一分关于数据模块的类关系表,基类 modelFactory 结构很简单,只是打包了核心类 shop_api 和数据接口类 AloneDB。特别提示一下,mdl_certificate 这个类,还有 sdb_setting 的 certificate,它们是和 shopex 的授权相关的,如果你想要做点什么的话,这个信息很有帮助:
class modelFactory \core\include_v5\modelFactory.php class shopObject extends modelFactory \core\include_v5\shopObject.php class mdl_adminProfile extends modelFactory \core\model_v5\mdl.adminprofile.php class mdl_sitemap extends modelFactory \core\model_v5\content\mdl.sitemap.php class mdl_systmpl extends modelFactory \core\model_v5\content\mdl.systmpl.php class mdl_widgets extends modelFactory \core\model_v5\content\mdl.widgets.php class mdl_costsync extends modelFactory \core\model_v5\distribution\mdl.costsync.php class mdl_datasync extends modelFactory \core\model_v5\distribution\mdl.datasync.php class mdl_platformsync extends modelFactory \core\model_v5\distribution\mdl.platformsync.php class mdl_syncjob extends modelFactory \core\model_v5\distribution\mdl.syncjob.php class mdl_b2bcur extends modelFactory \core\model_v5\purchase\mdl.b2bcur.php class mdl_apiclient extends modelFactory \core\model_v5\service\mdl.apiclient.php class mdl_certificate extends modelFactory \core\model_v5\service\mdl.certificate.php class mdl_data_install extends modelFactory \core\model_v5\service\mdl.data_install.php class mdl_kft extends modelFactory \core\model_v5\service\mdl.kft.php class mdl_appmgr extends modelFactory \core\model_v5\system\mdl.appmgr.php class mdl_backup extends modelFactory \core\model_v5\system\mdl.backup.php class mdl_dataio extends modelFactory \core\model_v5\system\mdl.dataio.php class mdl_debug extends modelFactory \core\model_v5\system\mdl.debug.php class mdl_entity extends modelFactory \core\model_v5\system\mdl.entity.php class mdl_math extends modelFactory \core\model_v5\system\mdl.math.php class mdl_seo extends modelFactory \core\model_v5\system\mdl.seo.php class mdl_sfile extends modelFactory \core\model_v5\system\mdl.sfile.php class mdl_status extends modelFactory \core\model_v5\system\mdl.status.php class mdl_template extends modelFactory \core\model_v5\system\mdl.template.php class mdl_upgrade extends modelFactory \core\model_v5\system\mdl.upgrade.php class mdl_cart extends modelFactory \core\model_v5\trading\mdl.cart.php class mdl_memberExperience extends modelFactory \core\model_v5\trading\mdl.memberExperience.php class mdl_memberPoint extends modelFactory \core\model_v5\trading\mdl.memberPoint.php class mdl_point extends modelFactory \core\model_v5\trading\mdl.point.php class mdl_pointHistory extends modelFactory \core\model_v5\trading\mdl.pointHistory.php class mdl_sale extends modelFactory \core\model_v5\trading\mdl.sale.php class mdl_archive extends modelFactory \core\model_v5\utility\mdl.archive.php class mdl_schemas extends modelFactory \core\model_v5\utility\mdl.schemas.php class mdl_url extends modelFactory \core\model_v5\utility\mdl.url.php class mdl_vcode extends modelFactory \core\model_v5\utility\mdl.vcode.php class shopObject extends modelFactory \core\include_v5\shopObject.php class plugin extends shopObject \core\include_v5\plugin.php class mdl_adminroles extends shopObject \core\model_v5\admin\mdl.adminroles.php class mdl_operator extends shopObject \core\model_v5\admin\mdl.operator.php class mdl_comment extends shopObject \core\model_v5\comment\mdl.comment.php class mdl_article extends shopObject \core\model_v5\content\mdl.article.php class mdl_frendlink extends shopObject \core\model_v5\content\mdl.frendlink.php class mdl_autosync extends shopObject \core\model_v5\distribution\mdl.autosync.php class mdl_supplier extends shopObject \core\model_v5\distribution\mdl.supplier.php class mdl_brand extends shopObject \core\model_v5\goods\mdl.brand.php class mdl_finderPdt extends shopObject \core\model_v5\goods\mdl.finderPdt.php class mdl_goodsNotify extends shopObject \core\model_v5\goods\mdl.goodsNotify.php class mdl_gtype extends shopObject \core\model_v5\goods\mdl.gtype.php class mdl_gtypetransform extends shopObject \core\model_v5\goods\mdl.gtypetransform.php class mdl_productCat extends shopObject \core\model_v5\goods\mdl.productCat.php class mdl_products extends shopObject \core\model_v5\goods\mdl.products.php class mdl_specification extends shopObject \core\model_v5\goods\mdl.specification.php class mdl_virtualcat extends shopObject \core\model_v5\goods\mdl.virtualcat.php class mdl_account extends shopObject \core\model_v5\member\mdl.account.php class mdl_advance extends shopObject \core\model_v5\member\mdl.advance.php class mdl_level extends shopObject \core\model_v5\member\mdl.level.php class mdl_member extends shopObject \core\model_v5\member\mdl.member.php class mdl_memberattr extends shopObject \core\model_v5\member\mdl.memberattr.php class mdl_order_po extends shopObject \core\model_v5\purchase\mdl.order_po.php class mdl_po extends shopObject \core\model_v5\purchase\mdl.po.php class mdl_message extends shopObject \core\model_v5\resources\mdl.message.php class mdl_tmpimage extends shopObject \core\model_v5\resources\mdl.tmpimage.php class mdl_order extends shopObject \core\model_v5\sync\order\mdl.order.php class mdl_addons extends shopObject \core\model_v5\system\mdl.addons.php class mdl_adv extends shopObject \core\model_v5\system\mdl.adv.php class mdl_cur extends shopObject \core\model_v5\system\mdl.cur.php class mdl_magicvars extends shopObject \core\model_v5\system\mdl.magicvars.php class mdl_matrix extends shopObject \core\model_v5\system\mdl.matrix.php class mdl_platform extends shopObject \core\model_v5\system\mdl.platform.php class mdl_pubfile extends shopObject \core\model_v5\system\mdl.pubfile.php class mdl_sms extends shopObject \core\model_v5\system\mdl.sms.php class mdl_tag extends shopObject \core\model_v5\system\mdl.tag.php class mdl_trigger extends shopObject \core\model_v5\system\mdl.trigger.php class mdl_coupon extends shopObject \core\model_v5\trading\mdl.coupon.php class mdl_couponGenerate extends shopObject \core\model_v5\trading\mdl.couponGenerate.php class mdl_delivery extends shopObject \core\model_v5\trading\mdl.delivery.php class mdl_deliveryarea extends shopObject \core\model_v5\trading\mdl.deliveryarea.php class mdl_deliverycorp extends shopObject \core\model_v5\trading\mdl.deliverycorp.php class mdl_dly_centers extends shopObject \core\model_v5\trading\mdl.dly_centers.php class mdl_dly_printer extends shopObject \core\model_v5\trading\mdl.dly_printer.php class mdl_exchangeCoupon extends shopObject \core\model_v5\trading\mdl.exchangeCoupon.php class mdl_gift extends shopObject \core\model_v5\trading\mdl.gift.php class mdl_giftcat extends shopObject \core\model_v5\trading\mdl.giftcat.php class mdl_goods extends shopObject \core\model_v5\trading\mdl.goods.php class mdl_order extends shopObject \core\model_v5\trading\mdl.order.php class mdl_package extends shopObject \core\model_v5\trading\mdl.package.php class mdl_payment extends shopObject \core\model_v5\trading\mdl.payment.php class mdl_paymentcfg extends shopObject \core\model_v5\trading\mdl.paymentcfg.php class mdl_promotion extends shopObject \core\model_v5\trading\mdl.promotion.php class mdl_promotionActivity extends shopObject \core\model_v5\trading\mdl.promotionActivity.php class mdl_refund extends shopObject \core\model_v5\trading\mdl.refund.php class mdl_return_product extends shopObject \core\model_v5\trading\mdl.return_product.php class mdl_taobaoordercsv extends shopObject \core\model_v5\trading\mdl.taobaoordercsv.php class mdl_salescount extends shopObject \core\model_v5\utility\mdl.salescount.php
为了完整地展示一个插件的工作流程,这里就以文章列表插件为列。文章列表挂件,即 article 目录下的挂件。在可视编辑时可以了解到,需要给它指定一个文件所在的栏目属性,这个属性可以在其配置文件 _config.html 中看到,即 显示文章分类 中指定的 id1。在页面渲染时,shopex 会将配置信息通过 $setting 这个变量来传递,这样就可以通过 $setting.id1 来使用这个栏目 ID 来做其它一些事了。
<{input name="id1" options=$data.cat value=$setting.id1|default:‘1‘ required="true" type="select"}>
要深入其它数据模块的细节可以通过这条继承链来追踪 mdl_article-〉shopObject -〉modelFactory。注意,shopex 核心代码是经过 zend 加密的,以上展示代码通过 DeZender DeIoncuber 反解得,不一定完整,但逻辑是符合原有程序的。
……
为了分析 ShopEx 的 MVC 架构,在缺少文档的情况下,只能从入口追踪。入口文件 /index.php 开始处定义了几个函数,用来过滤信息防止注入动作,如filterData、filterArray、htmltotxt、filter 和 filterxs等等,主要是针对 $_POST 和 $_GET 进行过滤。完成过滤动作后,发现有意思的东西 crontab ,这个类相关的文件已经报出过漏洞,即 \core\include_v5\crontab.php 远程文件写入漏洞:
if (isset($_GET[‘cron‘]) && $_GET[‘cron‘]) { require(CORE_INCLUDE_DIR.‘/crontab.php‘); $_GET[‘action‘] = $_GET[‘cron‘]; new crontab(); }
紧接着才加载系统,实例化 kernel 类,并产生 $system 全局引用。除非还没有完成安装,没有生效的配置文件 /config.php :
require(CORE_DIR.‘/include_v5/shopCore.php‘); new shopCore();
系统原有的 shopCore 是加密的,通过 DeZender.DeIoncuber 可以解开一部分有效代码,解密得到的代码会打上标记,视加密技术的差异,得到的文件也有较大的出入:
/*********************/ /* */ /* Dezend for PHP5 */ /* NWS */ /* Nulled.WS */ /* */ /*********************/ require_once( CORE_DIR."/kernel.php" ); require_once( CORE_DIR."/func_ext.php" ); …… class shopCore extends kernel ……
重点是加色的三条语句,func_ext.php 提供了 ShopEx 定义的扩展功能函数,kernel.php 则是整个系统的核心,shopCore 要继承它。shopCore 提供了一个方法 mkUrl ,这个方法会调用 kernel 类的 realUrl 方法来重新构造一个标准化的内部 URL。shopCore 在构造函数中运行下面两个方法就是系统进行会话处理的开始:
$this->__session_start( ); $this->run( );
前者将对会话的 Cookie 进行处理,产生一个名为 SHOPEX_SID_MEMBER 的会话 ID 值,然后根据会此值于 sdb_op_sessions 数据表中查找相关的会话信息。run 方法中首先会检查,请求是否为 gOo,这似乎意味着旧版的兼容规则。随之加载了一个数据模块 utility/url ,即 \core\model_v5\utility\mdl_url.php 文件中定义的 mdl_url 类,它会加载一个基本的 URL 映射规则文件:
……
而在 run 方法中,还要对配置文件中 BLACKLIST 指定的 IP 进行过滤,禁止列表内的 IP 主机登录。 再接着来,run 才会将一个页面送到 _frontend 方法中处理,这里加载了 shopPage 类,它继承自 pageFactory,还通过 getController 方法加载了控制器。可以想像,pageFactory 这个基类其实就是所有前端页面的原型了,即视图类,但事实上,pageFactory 是控制器类的基类,而视图却是以 smary 为基础的静态模板,它们位于
/core/shop/view 目录下。尽管如此,pageFactory 还是和静态模板视图的关系密切,它会通过 getVeiw 方法来取得视图模板内容,后面再讲。按照前面给出 URL ,系统会装入 ctl_artlist 这个控制器,并执行其 index 方法产生页面内容,相关的类继承关系参考:
class pageFactory \core\include_v5\pageFactory.php class adminPage extends pageFactory \core\include_v5\adminPage.php class ctl_dashboard extends adminPage \core\admin\controller\ctl.dashboard.php class ctl_default extends adminPage \core\admin\controller\ctl.
default.php class ctl_editor extends adminPage \core\admin\controller\ctl.editor.php class ctl_content extends adminPage \core\admin\controller\content\ctl.content.php class ctl_menus extends adminPage \core\admin\controller\content\ctl.menus.php class ctl_pages extends adminPage \core\admin\controller\content\ctl.pages.php class ctl_sitemaps extends adminPage \core\admin\controller\content\ctl.sitemaps.php class ctl_supplier extends adminPage \core\admin\controller\distribution\ctl.supplier.php class shopPage extends pageFactory \core\include_v5\shopPage.php class ctl_article extends shopPage \core\shop\controller\ctl.article.php class ctl_artlist extends shopPage \core\shop\controller\ctl.artlist.php class ctl_brand extends shopPage \core\shop\controller\ctl.brand.php class ctl_cart extends shopPage \core\shop\controller\ctl.cart.php class ctl_comment extends shopPage \core\shop\controller\ctl.comment.php class ctl_custompage extends shopPage \core\shop\controller\ctl.custompage.php class ctl_gallery extends shopPage \core\shop\controller\ctl.gallery.php class ctl_gift extends shopPage \core\shop\controller\ctl.gift.php class ctl_link extends shopPage \core\shop\controller\ctl.link.php class ctl_member extends shopPage \core\shop\controller\ctl.member.php class ctl_message extends shopPage \core\shop\controller\ctl.message.php class ctl_order extends shopPage \core\shop\controller\ctl.order.php class ctl_package extends shopPage \core\shop\controller\ctl.package.php class ctl_page extends shopPage \core\shop\controller\ctl.page.php class ctl_passport extends shopPage \core\shop\controller\ctl.passport.php class ctl_paycenter extends shopPage \core\shop\controller\ctl.paycenter.php class ctl_previewtheme extends shopPage \core\shop\controller\ctl.previewtheme.php class ctl_product extends shopPage \core\shop\controller\ctl.product.php class ctl_search extends shopPage \core\shop\controller\ctl.search.php class ctl_sitemaps extends shopPage \core\shop\controller\ctl.sitemaps.php class ctl_tools extends shopPage \core\shop\controller\ctl.tools.php class ctl_wholesale extends shopPage \core\shop\controller\ctl.wholesale.php class mdl_frontend extends pageFactory \core\model_v5\system\mdl.frontend.php
pageFactory 对于整个 MVC 架构来说,就是要的 V 所指代的视图 View 部分,即用户所看见的东西。M 所指代的 Model 数据模块部分在前面的小节已经较详细地解释过了。接下来就只有 MVC 的 C 所指代的 Controller 控制器了,它负责如何使数据流入到视图,通过视图来展示数据。URL 的结构与控制器的关系是密切的,因为站点程序的交互就是通过 URL 和 表单数据来实现的,而视图类就起作解释 URL 并作相应动作的作用,包括 Smarty 引擎的起用也是在视图类的指导下进行的。还有诸如
<{header}> 和 <{footer}>,还有不可以编辑的神秘的 <{main}> 系统功能,都是在视图完成内容输出的。
来看看 shopPage 类的成员信息,有些成员是很容易和 HTML 页面关联起来的,其中 $title 就是 HTML 标题的相关内容。
public $noCache = false;
public $_running = true;
public $contentType = "text/html;charset=utf-8";
public $member = NULL;
public $header = "";
public $keyWords = null;
public $metaDesc = null;
public $title = null;
public $type = null;
public $transaction_start = false;
public $__tmpl = null;
public $path = array( );
appmgr 需要通过 addons 的 plugin_instance 方法来实列化插件。addons 的构造函数内定义约束了插件的类型有 io 输入输出、 schema 商品插件、 hook 事件处理、 pmt 优惠规则、 local 地区插件、 messenger 消息发送、 pay 支付插件、 passport 登陆插件、 action 网店机器人动作 ,共9种插件类型,另外还有 app、mdl 这样插件类型出现,据官方的《ShopEx二次开发必备》说明,app 类型是软件功能包,而且补充了前台功能插件 shop、后台功能插件 admin、单独页面布局插件(layout)、地区数据插件(location)和图片存储方式插件(storager)等等。从插件的文件组织方式上可以分成目录型和文件型两大类插件。在4.8.5版本之后,不同类型插件不需要放在各自类型的目录里。只要放在plugins下面就都可以被扫描到。这是为下一步app机制作的准备。每个单独的php文件就是一个插件,文件名和其中的类的名称要符合一下规则:
o 文件名: <标示名>.插件名.php
o 类名: <标示名>_插件名
插件名 类型 标示名 默认路径 用户登录插件 - passport plugins/passport 图片存储方式插件 - storager plugins/storager 支付方式插件 - payment plugins/payment 促销方式插件 - pmtScheme plugins/pmtScheme 网店机器人动作插件 - action plugins/action 数据导入导出插件 - dataio plugins/dataio 用户消息插件 - messenger plugins/messenger 单独页面布局插件 - layout plugins/layout 地区数据插件 - location plugins/location 网页挂件 - widgets plugins/widgets 商品插件 - schema plugins/schema 前台功能插件 - shop - 后台功能插件 - admin - ……
传入 plugin_instance 的参数 $data 就是 sdb_plugins 数据表中的一条记录,它的 plugin_path 字段包含插件的目录路径,其中 plugin_struct 是序列化后的 PHP 数据,包含插件的配置信息。以手机短信和营销统计两个插件为例,前者是 message 插件,后者是 app 插件,它们的 plugin_struct 内容如下:
array(3) { ["class_name"]=> "messenger_sms" ["props"]=> array(14) { ["name"]=> "手机短信" ["iconclass"]=> ""sysiconBtn sms"" ["name_show"]=> "发短信" ["version"]=> "$ver$" ["updateUrl"]=> bool(false) ["isHtml"]=> bool(false) ["hasTitle"]=> bool(false) ["maxtime"]=> "300" ["maxbodylength"]=> "300" ["allowMultiTarget"]=> bool(false) ["withoutQueue"]=> bool(false) ["dataname"]=> "mobile" ["sms_service_ip"]=> "124.74.193.222" ["sms_service"]=> "http://idx.sms.shopex.cn/service.php" } ["funcs"]=> array(6) { ["messenger_sms"]=> "messenger_sms" ["send"]=> "send" ["apply"]=> "apply" ["ready"]=> "ready" ["finish"]=> "finish" ["extravars"]=> "extravars" } } |
array(3) { ["class_name"]=> "app_shopex_stat" ["props"]=> array(4) { ["ver"]=> "3.6.4" ["name"]=> "营销统计工具" ["website"]=> "http://www.shopex.cn" ["author"]=> "Rocky Anjiaxin Fuxiudong" } ["funcs"]=> array(5) { ["output_modifiers"]=> "output_modifiers" ["getmenu"]=> "getmenu" ["modify_pagedata"]=> "modify_pagedata" ["get_js"]=> "get_js" ["get_cart_data"]=> "get_cart_data" } } |
又再次往回跳到过滤器的问题上,以 shop:* 过滤器为例,appmgr.get_func() 将指定的 shopex_stat:modifiers:modify 分解成 $package、$class、$method 三个部分,然后 $package."_".$class 组成了类名,即系统存在一个类 shopex_stat_modifiers,有一个对应文件名,文件所在的目录将由数据表中的 plugin_path 给出 。get_func() 再将 $package 传入 load 方法,load 方法将其作为 sdb_plugins 表的 plugin_ident 字段的值来检索数据记录。得到数据记录后,交由 addons.plugin_instance() 方法来实例化插件,被实例化的类就是 plugin_struct 中指定的 class_name,这里即是指 app_shopex_stat 类,对应文件所在将由数据表中的 plugin_path 字段给出。通过查看数据表,可知 shopex_stat 其实是 营销统计工具 这个插件,类文件位于 /app/shopex_stat/app.shopex_stat.php。整个过程其实是很隐秘地加载了两个文件,两个类。最后返回到 kernel 中的是一个包含过滤器实列和方法字符串的数组,过滤器的 app 成员则引用了另一个被实列化的类 app_shopex_stat :
……
shopCore.run() -〉 shopCore.parseRequest() -〉 shopCore._frontend() -〉 load shopPage and getController() -〉 kernel.callAction() -〉 exec controller -〉 shopPage.output() -〉shopPage._get_view() -〉pageFactory._fetch_compile() -〉 loadModel( "system/tramsy" ) and Smarty Fill -〉 kernel.apply_modifiers() -〉 shopCore.display()
如果用一幅结构图来表示 shopex ,可以这样,尽管不是很全面:
至此,对 shopex 的 MVC 已经有较深入的认识,有这些信息就可以完成更高级的二次开发任务。
配图的文章列表相信是一个很受欢迎的页面展示方式,却未明 shopex 的系统自带的文章竟然没有这样一个优秀了内容展示方式的支持。后台也不支持设置文件的配图,只能通过编辑器来上传图片,而不能像 Wordpress 一样,使文章成为一部相册!我又再次显示了对 Wordpress 的爱慕之情!真是情不自禁啊。
……Shopex模板机制总览(摘要版),布布扣,bubuko.com
标签:shopex wordpress mywife.cc mvc架构分析
原文地址:http://blog.csdn.net/winsenjiansbomber/article/details/33418417