shopnc 是一款非常前卫的商城系统,功能强大
但这样一款强大的系统,遗憾是的没有插件体制,如果想要什么新功能,就都得修改源文件,会很不方便
作为码农,我是这样想的,给她一个插孔,让很多人插
/global.php 添加:
defined('DIR_PLUGINS') ||define('DIR_PLUGINS',BASE_ROOT_PATH.'/plugins');//added by bruce.chou 2016-5-24
建立/core/plugins.php:
<?php
/**
* shopnc 插件管理类
* @author bruce.chou @2016-5-25
*
*
*/
defined('InShopNC') or exit('AccessInvalid!');
defined('PLUGIN_EVENT_1') ||define('PLUGIN_EVENT_1','PLUGIN_EVENT_1');
class Plugins{
/**
* 保存所有插件
*/
publicstatic $all_plugins = array();
/**
*取得所有插件
*
*/
static function get_plugins(){
$result = array();
if($dh = opendir(DIR_PLUGINS)){
while(($f= readdir($dh)) !== false)
{
if($f != '.' && $f != '..'&& is_dir($cur = DIR_PLUGINS.'/'.$f))
{
if($cfg = require($cur ."/config.php"))
{
//if($cfg['active']){
$cfg['path']= $cur; //记录插件的安装目录
$result[$f]= $cfg;
//}
}
}
}
closedir($dh);
}
return self::$all_plugins=$result;
}
/**
*@description 供插件的视图中调用用于显示插件目录下的模板
*@author bruce.chou 2016-5-25
*@param string $page_name 要显示的模板文件
*@param string $layout 要包含的布局文件
*@param object $plugin 插件对象
*//@ string$obj_plugin->module_name 要加载的模块如 admin,home,salers,mobile等
*/
publicstatic function tpl_showpage($page_name,$layout='',$obj_plugin=NULL ){
if(!empty($obj_plugin->plugin_name))
{
if(array_key_exists($obj_plugin->plugin_name,self::$all_plugins)){
$path= $obj_plugin->plugin_path.'/'.$obj_plugin->module_name;
//echo$path;
Tpl::output("this_plugin",$obj_plugin);
Tpl::showpage($page_name,$layout,2000,$path);
}else{
exit("$obj_plugin->plugin_namenot found in plugins array");
}
}
else{
exit("lostplugin_name for show page!");
}
}
/*函数转发*/
publicstatic function tpl_output($arg='',$args){
Tpl::output($arg,$args);
}
publicstatic function triger_event($evt,&$args){
foreach(self::$all_plugins as $key => $value) {
foreach($value['listen'] as $e => $f) {
if($e==$evt){
$path= DIR_PLUGINS . '/' . $key . '/action.php';
$cls= ucwords($key)."Action";
if(file_exists($path)){
require_once($path);
$action_cls= new $cls();
if(method_exists($action_cls,$f)){
$action_cls->$f($args);
}
}
}
}
}
}
publicstatic function url($obj_plugin,$act='',$op='',$args=array()){
//plugin-mp-admin-mb_mpweixin
returnurl("plugin-".$obj_plugin->plugin_name.'-'.$obj_plugin->module_name.'-'.$act,$op,$args);
}
}
$all_plugins = Plugins::get_plugins();
插件目录设定在/plugins/插件名
目录结构:
插件名/
config.php //插件配置文件
action.php //插件监听处理类
models/ ->模型类目录
数据库交互处理功能文件列表
admin/ ->后台功能
control/
控制器文件列表
templates/
视图文件列表
home/ ->前台功能
control/
控制器文件列表
templates/
视图文件列表
mobile/ //手机端功能
…
salers/ ->商户后台功能
…
修改过的原始核心文件:
/core/base.php
/**
* 控制器调度
*
*/
privatestatic function control(){
//二级域名
if($GLOBALS['setting_config']['enabled_subdomain'] == '1' && $_GET['act']== 'index' && $_GET['op'] == 'index'){
$store_id= subdomain();
if($store_id > 0) $_GET['act'] = 'show_store';
}
/*
addedby bruce.chou 2016-5-24
*/
$act= trim($_GET['act']);//'plugin.mp.mb_mpweixin', //方法名 method
$m=array();
if(strpos($act,'plugin-') === 0){
//echoBASE_ROOT_PATH.$act;
$m = explode("-",$act);
//print_r($m);
$act_file = (BASE_ROOT_PATH.'/plugins/'.$m[1].'/'.$m[2].'/control/'.$m[3].'.php');
$_GET['act'] = $m[3];
}else{
$act_file= realpath(BASE_PATH.'/control/'.$_GET['act'].'.php');
}
//bybruce.chou
//die($act_file);
$class_name= $_GET['act'].'Control';
if(!@include($act_file)){
if (C('debug')) {
throw_exception("Base Error:access file isn't exists!");
} else {
showMessage('抱歉!您访问的页面不存在','','html','error');
}
}
if(class_exists($class_name)){
$main= new $class_name();
//bybruce.chou 2016-5-25 给实例类添加动态属性 插件名,插件路径
if(!empty($m)){
$main->plugin_name= $m[1];
$main->plugin_path= BASE_ROOT_PATH.'/plugins/'.$m[1];//.'/'.$m[2];
$main->module_name= $m[2];
$main->act = $_GET['act'];
}
$function= $_GET['op'].'Op';
if(method_exists($main,$function)){
$main->$function();
}elseif(method_exists($main,'indexOp')){
$main->indexOp();
}else{
$error= "Base Error: function $function not in $class_name!";
throw_exception($error);
}
}else{
$error= "Base Error: class $class_name isn't exists!";
throw_exception($error);
}
}
/core/framework/libraries/tpl.php
其实就是新增参数$base_p
* 调用显示模板
*
* @param string $page_name
* @param string $layout
* @param int $time
*/
publicstatic functionshowpage($page_name='',$layout='',$time=2000,$base_p=BASE_PATH){
if(!defined('TPL_NAME')) define('TPL_NAME','default');
self::getInstance();
if(!empty(self::$tpl_dir)){
$tpl_dir= self::$tpl_dir.DS;
}
//默认是带有布局文件
if(empty($layout)){
$layout= 'layout'.DS.self::$layout_file.'.php';
}else{
$layout= 'layout'.DS.$layout.'.php';
}
$layout_file= $base_p.'/templates/'.TPL_NAME.DS.$layout;
$tpl_file= $base_p.'/templates/'.TPL_NAME.DS.$tpl_dir.$page_name.'.php';
//echo $layout_file." >$tpl_file";
if(file_exists($tpl_file)){
//对模板变量进行赋值
$output= self::$output_value;
//页头
$output['html_title']= $output['html_title']!='' ? $output['html_title']:$GLOBALS['setting_config']['site_name'];
$output['seo_keywords']= $output['seo_keywords']!='' ? $output['seo_keywords']:$GLOBALS['setting_config']['site_name'];
$output['seo_description']= $output['seo_description']!='' ? $output['seo_description']:$GLOBALS['setting_config']['site_name'];
$output['ref_url']= getReferer();
Language::read('common');
$lang= Language::getLangContent();
@header("Content-type:text/html; charset=".CHARSET);
//判断是否使用布局方式输出模板,如果是,那么包含布局文件,并且在布局文件中包含模板文件
if($layout != ''){
if(file_exists($layout_file)){
include_once($layout_file);
}else{
$error= 'Tpl ERROR:'.'templates'.DS.$layout.' is not exists';
throw_exception($error);
}
}else{
include_once($tpl_file);
}
}else{
$error= 'Tpl ERROR:'.'templates'.DS.$tpl_dir.$page_name.'.php'.' is not exists';
throw_exception($error);
}
}
修改/core/shopnc.php
//统一ACTION 添加 - 号为合法 bybruce.chou 2016-5-24
$_GET['act'] =preg_match('/^[\w\-]+$/i',$_GET['act']) ? $_GET['act'] : 'index';
$_GET['op'] =preg_match('/^[\w\-]+$/i',$_GET['op']) ? $_GET['op'] : 'index';
并在未处添加
//by bruce.chou
if(file_exists(BASE_CORE_PATH."/plugins.php")){
include(BASE_CORE_PATH."/plugins.php");
}
插件目录下/config.php示例:
<?php
defined('InShopNC') or exit('AccessInvalid!');
return array(
'name' => 'mp',
'description' => '微信端功能扩展,提供后台界面,微信交互处理等',
'active'=>'1', //指示插件是否为活动状态
'install'=>'install.php', //安装文件,应该有MP_INSTALL类实现 install 和 uninstall方法
'listen' => array(
PLUGIN_EVENT_1=>'doSomethings', //要监听的事件数组
),
'menu'=> array(
'top' => 'mobile', //顶层KEY,如果存在原有的将会新增
'text'=> '微信端', //顶层菜单显示的名字,此参数仅在是新的顶层菜单会使用,如果是已存在的,则会忽略
'left'=> array(
array(
'act'=>'plugin-mp-admin-mb_mpweixin', //模块名必须以plugin开头+plugin名称+目录名+control
'op'=>'mb_mpweixin', //方法名 method
'nav'=>'mobile', //必须和顶层KEY一样
'text'=>'微公众号', //在左边要显示的名称
),
/*array(
'op'=>'mb_mpweixin', //模块名 control
'act'=>'mb_mpweixin1', //方法名 method
'nav'=>'mobile', //必须和顶层KEY一样
'text'=>'微公众号1', //在左边要显示的名称
),*/
),
),
'limit'=> array('name'=>$lang['nc_mobile'], 'child'=>array(
array('name'=>'微公众号', 'op'=>NULL,'act'=>'mb_mpweixin'),
))
);
添加菜单到后台操作:
/*
@bybruce.chou 2016-5-24
读取所有插件并加入到管理菜单中
**/
global$all_plugins;
$plugins= $all_plugins;//Plugins::get_plugins();
foreach($plugins as $key => $value) {
//查找顶层菜单,如果未找到则添加顶层菜单
$found= -1;
foreach($array['top'] as $k=>$v) {
if($v['args']== $plugins[$key]['menu']['top'] ){
$found= $k; //找到的话记住KEY
}
}
if($found == -1 ){
$array['top'][]= array('args' => $plugins[$key]['menu']['top'] ,'text'=>$plugins[$key]['menu']['text'] );
$found= count($array['top']) - 1;
$array['left'][]= array('nav' => $plugins[$key]['menu']['top'],
'text'=>$plugins[$key]['menu']['text'] );
}
foreach($plugins[$key]['menu']['left'] as $i => $j) {
#code...
$array['left'][$found]['list'][]= array(
'args' => $j['op'] . ',' . $j['act'] ."," . $j['nav'],
'text' => $j['text'],
);
}
} /*by bruce.chou*/
在插件任何目录下的控制器类中要显示相关视图:
Plugins::tpl_output('变量名', $变量);
Plugins::tpl_showpage('视图文件','布局文件',$this );
插件目录/action.php示例:
<?php
class MPAction{
functiondoSomethings(&$args){
echo"doSomethings:" . $args;
}
}
当然目前未做到尽善尽美,但基本在shopnc系统中实现了插件功能
我的QQ:174487481,有兴趣的可以一起探讨