微擎模組機制分析
阿新 • • 發佈:2019-01-02
利用微擎開發些微信公眾號還是非常方便的;模組機制分析主要從其安裝、解除安裝、使用角度,
一、安裝
安裝介面,主要是module_get_all_unistalled獲取未安裝模組
if ($do == 'not_installed') { if (empty($_W['isfounder'])) { itoast('非法訪問!', referer(), 'info'); } $_W['page']['title'] = '安裝模組 - 模組 - 擴充套件'; $status = $_GPC['status'] == 'recycle'? 'recycle' : 'uninstalled'; $letters = array('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); $title = $_GPC['title']; $letter = $_GPC['letter']; $pageindex = max($_GPC['page'], 1); $pagesize = 20; $uninstallModules = module_get_all_unistalled($status, false); $total_uninstalled = $uninstallModules['module_count']; $uninstallModules = (array)$uninstallModules['modules']; if (!empty($uninstallModules)) { foreach($uninstallModules as $name => &$module) { if (!empty($letter) && strlen($letter) == 1) { $first_char = get_first_pinyin($module['title']); if ($letter != $first_char) { unset($uninstallModules[$name]); continue; } } if (!empty($title)) { if (!strexists($module['title'], $title)) { unset($uninstallModules[$name]); continue; } } if (file_exists(IA_ROOT.'/addons/'.$module['name'].'/icon-custom.jpg')) { $module['logo'] = tomedia(IA_ROOT.'/addons/'.$module['name'].'/icon-custom.jpg'); } elseif (file_exists(IA_ROOT.'/addons/'.$module['name'].'/icon.jpg')) { $module['logo'] = tomedia(IA_ROOT.'/addons/'.$module['name'].'/icon.jpg'); } else { $module['logo'] = tomedia($module['thumb']); } if (!empty($module['main_module'])) { $main_module_installed = module_fetch($module['main_module']); if ($main_module_installed) { $module['main_module_logo'] = $main_module_installed['logo']; } else { if ($module['from'] == 'cloud') { $module['main_module_logo'] = tomedia($uninstallModules[$module['main_module']]['thumb']); } else { if (file_exists(IA_ROOT.'/addons/'.$module['main_module'].'/icon-custom.jpg')) { $module['main_module_logo'] = tomedia(IA_ROOT.'/addons/'.$module['main_module'].'/icon-custom.jpg'); } elseif (file_exists(IA_ROOT.'/addons/'.$module['main_module'].'/icon.jpg')) { $module['main_module_logo'] = tomedia(IA_ROOT.'/addons/'.$module['main_module'].'/icon.jpg'); } } } } } } $total = count($uninstallModules); $uninstallModules = array_slice($uninstallModules, ($pageindex - 1)*$pagesize, $pagesize); $pager = pagination($total, $pageindex, $pagesize); }
module_get_all_unistalled 函式原始碼,可以看到先判斷是否有快取(微擎的快取實現機制還是很清晰易懂的,資料庫、檔案、memcache、Redis),沒有再取,關鍵是cache_build_uninstalled_module;可以看到有云模組的請求,當然這部分你可以幹掉,我還是堅持用正版哈
function module_get_all_unistalled($status, $cache = true) { global $_GPC; load()->func('communication'); load()->model('cloud'); load()->classs('cloudapi'); $status = $status == 'recycle' ? 'recycle' : 'uninstalled'; $uninstallModules = cache_load(cache_system_key('module:all_uninstall')); if (!$cache && $status == 'uninstalled') { $cloud_api = new CloudApi(); $get_cloud_m_count = $cloud_api->get('site', 'stat', array('module_quantity' => 1), 'json'); $cloud_m_count = $get_cloud_m_count['module_quantity']; } else { if(is_array($uninstallModules)){ $cloud_m_count = $uninstallModules['cloud_m_count']; } } if (empty($uninstallModules['modules']) || intval($uninstallModules['cloud_m_count']) !== intval($cloud_m_count) || is_error($get_cloud_m_count)) { $uninstallModules = cache_build_uninstalled_module(); } if (ACCOUNT_TYPE == ACCOUNT_TYPE_APP_NORMAL) { $uninstallModules['modules'] = (array)$uninstallModules['modules'][$status]['wxapp']; $uninstallModules['module_count'] = $uninstallModules['wxapp_count']; return $uninstallModules; } elseif (ACCOUNT_TYPE == ACCOUNT_TYPE_OFFCIAL_NORMAL) { $uninstallModules['modules'] = (array)$uninstallModules['modules'][$status]['app']; $uninstallModules['module_count'] = $uninstallModules['app_count']; return $uninstallModules; } else { return $uninstallModules; } }
cache_build_uninstalled_module原始碼,現在知道為什麼模組要放在addons下了,最後還把模組資訊寫入快取,以便下次直接使用
function cache_build_uninstalled_module() { load()->model('cloud'); load()->classs('cloudapi'); load()->model('extension'); load()->func('file'); $cloud_api = new CloudApi(); $cloud_m_count = $cloud_api->get('site', 'stat', array('module_quantity' => 1), 'json'); $installed_module = pdo_getall('modules', array(), array(), 'name'); $uninstallModules = array('recycle' => array(), 'uninstalled' => array()); $recycle_modules = pdo_getall('modules_recycle', array(), array(), 'modulename'); $recycle_modules = array_keys($recycle_modules); $cloud_module = cloud_m_query(); if (!empty($cloud_module) && !is_error($cloud_module)) { foreach ($cloud_module as $module) { $upgrade_support_module = false; $wxapp_support = !empty($module['site_branch']['wxapp_support']) && is_array($module['site_branch']['bought']) && in_array('wxapp', $module['site_branch']['bought']) ? $module['site_branch']['wxapp_support'] : 1; $app_support = !empty($module['site_branch']['app_support']) && is_array($module['site_branch']['bought']) && in_array('app', $module['site_branch']['bought']) ? $module['site_branch']['app_support'] : 1; if ($wxapp_support == 1 && $app_support == 1) { $app_support = 2; } if (!empty($installed_module[$module['name']]) && ($installed_module[$module['name']]['app_support'] != $app_support || $installed_module[$module['name']]['wxapp_support'] != $wxapp_support)) { $upgrade_support_module = true; } if (!in_array($module['name'], array_keys($installed_module)) || $upgrade_support_module) { $status = in_array($module['name'], $recycle_modules) ? 'recycle' : 'uninstalled'; if (!empty($module['id'])) { $cloud_module_info = array ( 'from' => 'cloud', 'name' => $module['name'], 'version' => $module['version'], 'title' => $module['title'], 'thumb' => $module['thumb'], 'wxapp_support' => $wxapp_support, 'app_support' => $app_support, 'main_module' => empty($module['main_module']) ? '' : $module['main_module'], 'upgrade_support' => $upgrade_support_module ); if ($upgrade_support_module) { if ($wxapp_support == 2 && $installed_module[$module['name']]['wxapp_support'] != 2) { $uninstallModules[$status]['wxapp'][$module['name']] = $cloud_module_info; } if ($app_support == 2 && $installed_module[$module['name']]['app_support'] != 2) { $uninstallModules[$status]['app'][$module['name']] = $cloud_module_info; } } else { if ($wxapp_support == 2) { $uninstallModules[$status]['wxapp'][$module['name']] = $cloud_module_info; } if ($app_support == 2) { $uninstallModules[$status]['app'][$module['name']] = $cloud_module_info; } } } } } } $path = IA_ROOT . '/addons/'; mkdirs($path); $module_file = glob($path . '*'); if (is_array($module_file) && !empty($module_file)) { foreach ($module_file as $modulepath) { $upgrade_support_module = false; $modulepath = str_replace($path, '', $modulepath); $manifest = ext_module_manifest($modulepath); if (!is_array($manifest) || empty($manifest) || empty($manifest['application']['identifie'])) { continue; } $main_module = empty($manifest['platform']['main_module']) ? '' : $manifest['platform']['main_module']; $manifest = ext_module_convert($manifest); if (!empty($installed_module[$modulepath]) && ($manifest['app_support'] != $installed_module[$modulepath]['app_support'] || $manifest['wxapp_support'] != $installed_module[$modulepath]['wxapp_support'])) { $upgrade_support_module = true; } if (!in_array($manifest['name'], array_keys($installed_module)) || $upgrade_support_module) { $module[$manifest['name']] = $manifest; $module_info = array( 'from' => 'local', 'name' => $manifest['name'], 'version' => $manifest['version'], 'title' => $manifest['title'], 'app_support' => $manifest['app_support'], 'wxapp_support' => $manifest['wxapp_support'], 'main_module' => $main_module, 'upgrade_support' => $upgrade_support_module ); $module_type = in_array($manifest['name'], $recycle_modules) ? 'recycle' : 'uninstalled'; if ($upgrade_support_module) { if ($module_info['app_support'] == 2 && $installed_module[$module_info['name']]['app_support'] != 2) { $uninstallModules['uninstalled']['app'][$manifest['name']] = $module_info; } if ($module_info['wxapp_support'] == 2 && $installed_module[$module_info['name']]['wxapp_support'] != 2) { $uninstallModules['uninstalled']['wxapp'][$manifest['name']] = $module_info; } } else { if ($module_info['app_support'] == 2) { $uninstallModules[$module_type]['app'][$manifest['name']] = $module_info; } if ($module_info['wxapp_support'] == 2) { $uninstallModules[$module_type]['wxapp'][$manifest['name']] = $module_info; } } } } } $cache = array( 'cloud_m_count' => $cloud_m_count['module_quantity'], 'modules' => $uninstallModules, 'app_count' => count($uninstallModules['uninstalled']['app']), 'wxapp_count' => count($uninstallModules['uninstalled']['wxapp']) ); cache_write('we7:module:all_uninstall', $cache, CACHE_EXPIRE_LONG); return $cache; }
還有個地方值得學習,模組的資訊定義在manifest.xml中,有點java的感覺,具體資訊讀取都在extension.mod.php中,程式碼如下
function ext_module_manifest_parse($xml) {
if (!strexists($xml, '<manifest')) {
$xml = base64_decode($xml);
}
if (empty($xml)) {
return array();
}
$dom = new DOMDocument();
$dom->loadXML($xml);
$root = $dom->getElementsByTagName('manifest')->item(0);
if (empty($root)) {
return array();
}
$vcode = explode(',', $root->getAttribute('versionCode'));
$manifest['versions'] = array();
if (is_array($vcode)) {
foreach ($vcode as $v) {
$v = trim($v);
if (!empty($v)) {
$manifest['versions'][] = $v;
}
}
$manifest['versions'][] = '0.52';
$manifest['versions'][] = '0.6';
$manifest['versions'] = array_unique($manifest['versions']);
}
$manifest['install'] = $root->getElementsByTagName('install')->item(0)->textContent;
$manifest['uninstall'] = $root->getElementsByTagName('uninstall')->item(0)->textContent;
$manifest['upgrade'] = $root->getElementsByTagName('upgrade')->item(0)->textContent;
$application = $root->getElementsByTagName('application')->item(0);
if (empty($application)) {
return array();
}
$manifest['application'] = array(
'name' => trim($application->getElementsByTagName('name')->item(0)->textContent),
'identifie' => trim($application->getElementsByTagName('identifie')->item(0)->textContent),
'version' => trim($application->getElementsByTagName('version')->item(0)->textContent),
'type' => trim($application->getElementsByTagName('type')->item(0)->textContent),
'ability' => trim($application->getElementsByTagName('ability')->item(0)->textContent),
'description' => trim($application->getElementsByTagName('description')->item(0)->textContent),
'author' => trim($application->getElementsByTagName('author')->item(0)->textContent),
'url' => trim($application->getElementsByTagName('url')->item(0)->textContent),
'setting' => trim($application->getAttribute('setting')) == 'true',
);
$platform = $root->getElementsByTagName('platform')->item(0);
if (!empty($platform)) {
$manifest['platform'] = array(
'subscribes' => array(),
'handles' => array(),
'isrulefields' => false,
'iscard' => false,
'supports' => array(),
);
$subscribes = $platform->getElementsByTagName('subscribes')->item(0);
if (!empty($subscribes)) {
$messages = $subscribes->getElementsByTagName('message');
for ($i = 0; $i < $messages->length; $i++) {
$t = $messages->item($i)->getAttribute('type');
if (!empty($t)) {
$manifest['platform']['subscribes'][] = $t;
}
}
}
$handles = $platform->getElementsByTagName('handles')->item(0);
if (!empty($handles)) {
$messages = $handles->getElementsByTagName('message');
for ($i = 0; $i < $messages->length; $i++) {
$t = $messages->item($i)->getAttribute('type');
if (!empty($t)) {
$manifest['platform']['handles'][] = $t;
}
}
}
$rule = $platform->getElementsByTagName('rule')->item(0);
if (!empty($rule) && $rule->getAttribute('embed') == 'true') {
$manifest['platform']['isrulefields'] = true;
}
$card = $platform->getElementsByTagName('card')->item(0);
if (!empty($card) && $card->getAttribute('embed') == 'true') {
$manifest['platform']['iscard'] = true;
}
$supports = $platform->getElementsByTagName('supports')->item(0);
if (!empty($supports)) {
$support_type = $supports->getElementsByTagName('item');
for ($i = 0; $i < $support_type->length; $i++) {
$t = $support_type->item($i)->getAttribute('type');
if (!empty($t)) {
$manifest['platform']['supports'][] = $t;
}
}
}
$plugins = $platform->getElementsByTagName('plugins')->item(0);
if (!empty($plugins)) {
$plugin_list = $plugins->getElementsByTagName('item');
for ($i = 0; $i < $plugin_list->length; $i++) {
$plugin = $plugin_list->item($i)->getAttribute('name');
if (!empty($plugin)) {
$manifest['platform']['plugin_list'][] = $plugin;
}
}
}
$plugin_main = $platform->getElementsByTagName('plugin-main')->item(0);
if (!empty($plugin_main)) {
$plugin_main = $plugin_main->getAttribute('name');
if (!empty($plugin_main)) {
$manifest['platform']['main_module'] = $plugin_main;
}
}
}
$bindings = $root->getElementsByTagName('bindings')->item(0);
if (!empty($bindings)) {
global $points;
if (!empty($points)) {
$ps = array_keys($points);
$manifest['bindings'] = array();
foreach ($ps as $p) {
$define = $bindings->getElementsByTagName($p)->item(0);
$manifest['bindings'][$p] = _ext_module_manifest_entries($define);
}
}
}
$permissions = $root->getElementsByTagName('permissions')->item(0);
if (!empty($permissions)) {
$manifest['permissions'] = array();
$items = $permissions->getElementsByTagName('entry');
for ($i = 0; $i < $items->length; $i++) {
$item = $items->item($i);
$row = array(
'title' => $item->getAttribute('title'),
'permission' => $item->getAttribute('do'),
);
if (!empty($row['title']) && !empty($row['permission'])) {
$manifest['permissions'][] = $row;
}
}
}
return $manifest;
}
function ext_module_manifest($modulename) {
$filename = IA_ROOT . '/addons/' . $modulename . '/manifest.xml';
if (!file_exists($filename)) {
return array();
}
$xml = file_get_contents($filename);
return ext_module_manifest_parse($xml);
}
好了,現在該分析安裝了,先把安裝程式碼貼上;這部分主要做業務判斷,模組資訊插入資料庫,主要表modules_bindings ,modules,要修改模組的資訊、或者增加顯示功能列表修改表就行了
if ($do =='install') {
$points = ext_module_bindings();
$module_name = trim($_GPC['module_name']);
$is_recycle_module = pdo_get('modules_recycle', array('modulename' => $module_name));
if (empty($_W['isfounder'])) {
itoast('您沒有安裝模組的許可權', '', 'error');
}
$module_info = module_fetch($module_name);
if (!empty($module_info)) {
itoast('模組已經安裝或是唯一標識已存在!', '', 'error');
}
$manifest = ext_module_manifest($module_name);
if (!empty($manifest)) {
$result = cloud_m_prepare($module_name);
if (is_error($result)) {
itoast($result['message'], url('module/manage-system/not_installed', array('account_type' => ACCOUNT_TYPE)), 'error');
}
} else {
$result = cloud_prepare();
if (is_error($result)) {
itoast($result['message'], url('cloud/profile'), 'error');
}
$module_info = cloud_m_info($module_name);
if (!is_error($module_info)) {
if (empty($_GPC['flag'])) {
header('location: ' . url('cloud/process', array('account_type' => ACCOUNT_TYPE, 'm' => $module_name)));
exit;
} else {
define('ONLINE_MODULE', true);
$packet = cloud_m_build($module_name);
$manifest = ext_module_manifest_parse($packet['manifest']);
}
} else {
itoast($module_info['message'], '', 'error');
}
}
if (empty($manifest)) {
itoast('模組安裝配置檔案不存在或是格式不正確,請重新整理重試!', url('module/manage-system/not_installed', array('account_type' => ACCOUNT_TYPE)), 'error');
}
if (!empty($manifest['platform']['main_module'])) {
$plugin_exist = pdo_get('modules_plugin', array('main_module' => $manifest['platform']['main_module'], 'name' => $manifest['application']['identifie']));
if (empty($plugin_exist)) {
itoast('請先更新主模組後再安裝外掛', url('module/manage-system/installed'), 'error');
}
}
$check_manifest_result = manifest_check($module_name, $manifest);
if (is_error($check_manifest_result)) {
itoast($check_manifest_result['message'], '', 'error');
}
$module_path = IA_ROOT . '/addons/' . $module_name . '/';
if (!file_exists($module_path . 'processor.php') && !file_exists($module_path . 'module.php') && !file_exists($module_path . 'receiver.php') && !file_exists($module_path . 'site.php')) {
itoast('模組缺失檔案,請檢查模組檔案中site.php, processor.php, module.php, receiver.php 檔案是否存在!', '', 'error');
}
$module = ext_module_convert($manifest);
$module_group = uni_groups();
if (!$_W['ispost'] || empty($_GPC['flag'])) {
template('system/select-module-group');
exit;
}
if (!empty($manifest['platform']['plugin_list'])) {
foreach ($manifest['platform']['plugin_list'] as $plugin) {
pdo_insert('modules_plugin', array('main_module' => $manifest['application']['identifie'], 'name' => $plugin));
}
}
$post_groups = $_GPC['group'];
ext_module_clean($module_name);
$bindings = array_elements(array_keys($points), $module, false);
if (!empty($points)) {
foreach ($points as $name => $point) {
unset($module[$name]);
if (is_array($bindings[$name]) && !empty($bindings[$name])) {
foreach ($bindings[$name] as $entry) {
$entry['module'] = $manifest['application']['identifie'];
$entry['entry'] = $name;
if ($name == 'page' && !empty($wxapp_support)) {
$entry['url'] = $entry['do'];
$entry['do'] = '';
}
pdo_insert('modules_bindings', $entry);
}
}
}
}
$module['permissions'] = iserializer($module['permissions']);
$module_subscribe_success = true;
if (!empty($module['subscribes'])) {
$subscribes = iunserializer($module['subscribes']);
if (!empty($subscribes)) {
$module_subscribe_success = ext_check_module_subscribe($module['name']);
}
}
if (!empty($module_info['version']['cloud_setting'])) {
$module['settings'] = 2;
}
$module['title_initial'] = get_first_pinyin($module['title']);
if (strexists($manifest['install'], '.php')) {
if (file_exists($module_path . $manifest['install'])) {
include_once $module_path . $manifest['install'];
if (ONLINE_MODULE) {
unlink ($module_path . $manifest['install']);
}
}
} else {
pdo_run($manifest['install']);
}
if (pdo_insert('modules', $module)) {
if (ONLINE_MODULE) {
if (strexists($manifest['upgrade'], '.php') && file_exists($module_path . $manifest['upgrade'])) {
unlink($module_path . $manifest['upgrade']);
}
if (strexists($manifest['uninstall'], '.php') && file_exists($module_path . $manifest['uninstall'])) {
unlink($module_path . $manifest['uninstall']);
}
}
if (defined('ONLINE_MODULE')) {
ext_module_script_clean($module['name'], $manifest);
}
if ($_GPC['flag'] && !empty($post_groups) && $module['name']) {
foreach ($post_groups as $groupid) {
$group_info = pdo_get('uni_group', array('id' => intval($groupid)), array('id', 'name', 'modules'));
if (empty($group_info)) {
continue;
}
$group_info['modules'] = iunserializer($group_info['modules']);
if (in_array($module['name'], $group_info['modules'])) {
continue;
}
$group_info['modules'][] = $module['name'];
$group_info['modules'] = iserializer($group_info['modules']);
pdo_update('uni_group', $group_info, array('id' => $groupid));
}
}
if (!empty($is_recycle_module)) {
pdo_delete('modules_recycle', array('modulename' => $module_name));
}
cache_build_module_subscribe_type();
cache_build_account_modules();
cache_build_uninstalled_module();
cache_build_module_info($module_name);
if (empty($module_subscribe_success)) {
itoast('模組安裝成功!模組訂閱訊息有錯誤,系統已禁用該模組的訂閱訊息,詳細資訊請檢視', url('module/manage-system/module_detail', array('name' => $module['name'])), 'tips');
} else {
itoast('模組安裝成功!', url('module/manage-system', array('account_type' => ACCOUNT_TYPE)), 'success');
}
} else {
itoast('模組安裝失敗, 請聯絡模組開發者!');
}
}
安裝完成後,模組就可以開始使用了,模組的使用稍後分析
先說解除安裝,解除安裝只是將之前的入庫的資料刪了,本質上沒有刪檔案
if ($do == 'uninstall') {
$name = trim($_GPC['name']);
$message = '';
$module = module_fetch($name);
if (!empty($module['plugin'])) {
$plugin_list = module_get_plugin_list($module['name']);
if (!empty($plugin_list) && is_array($plugin_list)) {
$message .= '刪除' . $module['title'] . '並刪除' . $module['title'] . '包含外掛<ul>';
foreach ($plugin_list as $plugin) {
$message .= "<li>{$plugin['title']}</li>";
}
unset($plugin);
$message .= '</ul>';
}
}
if (!isset($_GPC['confirm'])) {
if ($module['isrulefields']) {
$message .= '是否刪除相關規則和統計分析資料<div><a class="btn btn-primary" style="width:80px;" href="' . url('module/manage-system/uninstall', array('name' => $name, 'confirm' => 1)) . '">是</a> <a class="btn btn-default" style="width:80px;" href="' . url('module/manage-system/uninstall', array('account_type' => ACCOUNT_TYPE, 'name' => $name, 'confirm' => 0)) . '">否</a></div>';
} elseif (!empty($plugin_list)) {
$message .= "<a href=" . url('module/manage-system/uninstall', array('name' => $name,'confirm' => 0)) . " class='btn btn-info'>繼續刪除</a>";
}
if (!empty($message)) {
itoast($message, '', 'tips');
}
}
if (!empty($plugin_list) && is_array($plugin_list)) {
foreach ($plugin_list as $plugin) {
module_uninstall($plugin['name']);
}
}
$uninstall_result = module_uninstall($module['name'], $_GPC['confirm'] == 1);
if (is_error($uninstall_result)) {
itoast($uninstall_result['message'], url('module/manage-system'), 'error');
}
itoast('模組已放入回收站!', url('module/manage-system', array('account_type' => ACCOUNT_TYPE)), 'success');
}