Files
BESCMS/dayrui/Fcms/Core/View.php
T

2495 lines
101 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php namespace Phpcmf;
/**
* https://www.besyun.com
* BESCMS
* 本文件是框架系统文件,二次开发时不可以修改本文件
**/
/**
* CMS模板标签解析
*/
class View {
private $_is_admin; // 是否后台模板
private $_is_return; // 是否返回模板名称不输出模板
private $_disp_dir; // display传人的目录参数
private $_dir; // 模板目录
private $_tname; // 判断是否是手机端目录
private $_cache; // 模板缓存目录
private $_root; // 默认前端项目模板目录
private $_mroot; // 默认会员项目模板目录
private $_aroot; // 默认后台项目模板目录
private $_froot; // 默认后台项目的修正模板目录
private $_load_file_tips; // 模板引用提示
private $_options; // 模板变量
private $_filename; // 主模板名称
private $_include_file; // 引用计数
private $_return_sql; // 是否返回sql语句用于运算
private $_view_time; // 模板的运行时间
private $_view_files; // 累计模板的引用文件
private $_view_file; // 当前模板的引用文件
private $pos_map; // 地图定位坐标
private $pos_order; // 是否包含有地图定位的排序
private $action; // 指定action
public $_is_mobile; // 是否是移动端模板
private $_is_pc; // 是否是pc模板
private $_sql; // 最近运行的sql语句
private $_sql_time; // 最近运行的sql语句的时间
private $performanceData = []; // 用于调试栏数据
private $loadjs = []; // 加载的js
private $_page_config = []; // 分页参数
private $_page_config_file = ''; // 分页配置文件
private $_page_urlrule = ''; // 分页地址参数
private $_page_used = 0; // 是否开启分页
private $_list_tag = ''; // 循环体标签
private $_list_param = []; // 标签参数
private $_list_where = []; // 循环体解析的条件数组
private $_list_error = []; // 循环标签遇到的错误
private $_is_list_search = 0; // 搜索标签
private $_page_value = 0; // 页码变量
private $_sum_field; // 求和时的字段
private $_code;
private $_mdir;
private $_safe_template_error = ''; // _safe_template_path 失败原因(开发者模式展示)
private $_select_rt_name = '_XUNRUICMS_RT_'; // select替换字符
public $call_value; // 动态模板返回调用
/**
* 初始化环境
*/
public function init($name = 'pc') {
$this->_is_pc = $name == 'pc'; // 标记为pc端模板
$this->_is_mobile = $name == 'mobile'; // 标记为移动端模板
// 模板缓存目录
$this->_cache = WRITEPATH.'template/';
$this->_tname = $this->_is_mobile ? MOBILE_TPL_DIR : ($name ? $name : 'pc');
$this->_aroot = $this->_froot = COREPATH.'View/';
$this->_mroot = $this->get_client_member_path($this->_tname);
// 当前项目模板目录
if (IS_ADMIN) {
// 后台
$this->admin();
} elseif (IS_MEMBER) {
// 会员
//$this->_is_mobile && !is_dir(TPLPATH.'mobile/'.SITE_TEMPLATE.'/member/') && $this->_tname = 'pc';
$this->_root = $this->get_client_home_path($this->_tname);
$this->_dir = $this->_mroot;
// 当用户中心没有这个模板目录时我们就调用default的用户中心模板
!is_dir($this->_mroot) && $this->_mroot = str_replace('/'.SITE_TEMPLATE.'/', '/default/', $this->_mroot);
// 项目的会员中心
APP_DIR != 'member' && $this->_dir = $this->get_client_member_path($this->_tname, APP_DIR);
} elseif (IS_API || (APP_DIR && dr_is_app_dir(APP_DIR))) {
// 插件模块或者api
$dir = IS_API ? 'api' : APP_DIR;
//$this->_is_mobile && !is_dir(TPLPATH.'mobile/'.SITE_TEMPLATE.'/home/') && $this->_tname = 'pc';
$this->_root = $this->get_client_home_path($this->_tname);
$this->_dir = $this->get_client_home_path($this->_tname, $dir);
} else {
// 首页前端页面
//$this->_is_mobile && !is_dir(TPLPATH.'mobile/'.SITE_TEMPLATE.'/home/') && $this->_tname = 'pc';
$this->_dir = $this->_root = $this->get_client_home_path($this->_tname);
}
// 系统action
$this->action = [
'category', 'module', 'content', 'related', 'share', 'table', 'form', 'mform', 'member', 'page',
'tag', 'hits', 'search', 'category_search_field', 'linkage', 'sql', 'function', 'comment',
'cache', 'navigator'
,'modules'
];
}
// 判断是pc端模板
public function is_pc() {
return $this->_is_pc;
}
// 判断是移动端模板
public function is_mobile() {
return $this->_is_mobile;
}
// 终端路径
private function get_client_home_path($name, $mid = '') {
if ($name == 'NULL') {
return TPLPATH.($mid ? $mid.'/' : '');
}
return dr_get_app_tpl($mid).$name.'/'.SITE_TEMPLATE.'/home/'.($mid ? $mid.'/' : '');
}
// 终端路径
private function get_client_member_path($name, $mid = '') {
return dr_get_app_tpl($mid).$name.'/'.SITE_TEMPLATE.'/member/'.($mid ? $mid.'/' : '');
}
/**
* 强制设置模块模板目录
*
* @param string $dir 模板名称
* @param string $isweb 强制前台
*/
public function module($module, $isweb = 0) {
if ($isweb == 0 && (IS_ADMIN || IS_MEMBER)) {
return;
}
// 默认模板目录
$this->_is_admin = 0;
$this->_root = $this->get_client_home_path($this->_tname);
$this->_dir = $this->get_client_home_path($this->_tname, $module);
}
// 外部获取当前应用的模板路径
public function get_dir() {
return $this->_dir;
}
/**
* 强制设置为当前默认的模板目录(一般用于api外部接入)
*/
public function dir($dir) {
$this->_dir = $this->_mdir = TPLPATH.$dir;
}
/**
* 强制设置为当前默认的模板目录
*/
public function set_dir($path) {
$this->_dir = $this->_mdir = $path;
}
/**
* 设置是否返回模板名称不显示
*/
public function set_return($is) {
$this->_is_return = $is;
}
/**
* 强制设置为后台模板目录
*/
public function admin($path = '', $fix_path = '') {
$this->_aroot = COREPATH.'View/';
$this->_dir = $path ? $path : (APP_DIR ? APPPATH.'Views/' : $this->_aroot);
$this->_froot = $fix_path ? $fix_path : (APP_DIR ? APPPATH.'Views/' : $this->_aroot);
$this->_is_admin = 1;
}
/**
* 当前模板对应的URL地址
*/
public function now_php_url() {
if (isset($this->_options['my_php_url']) && $this->_options['my_php_url']) {
return $this->_options['my_php_url'];
} elseif (isset($this->_options['my_web_url']) && $this->_options['my_web_url']) {
return $this->_options['my_web_url'];
}
return FC_NOW_URL;
}
/**
* 输出模板
*
* @param string $_name 模板文件名称(含扩展名)
* @param string $_dir 模块名称
* @param boll $is_destruction 是否销毁变量
* @return void
*/
public function display($phpcmf_name, $phpcmf_dir = '', $is_destruction = true) {
if ($this->_is_return) {
return $phpcmf_name;
}
$phpcmf_start = microtime(true);
// 定义当前模板的url地址
if (!isset($this->_options['my_web_url']) or !$this->_options['my_web_url']) {
$this->_options['my_web_url'] = isset($this->_options['fix_html_now_url']) && $this->_options['fix_html_now_url'] ? $this->_options['fix_html_now_url'] : dr_now_url();
}
// 定义当前url参数值
if (!isset($this->_options['get'])) {
$this->_options['get'] = \Phpcmf\Service::L('input')->xss_clean($_GET);
}
// 如果是来自api就不解析模板,直接输出变量
if (IS_API_HTTP) {
\Phpcmf\Service::C()->_json(1, '当前是get请求方式,返回当前模板可用的变量', $this->_options);
}
// 生成静态时退出账号记录
if (defined('SC_HTML_FILE')) {
$this->_options['member'] = [];
}
// 挂钩点 模板加载之前
\Phpcmf\Hooks::trigger('cms_view_display', $this->_options, $phpcmf_name, $phpcmf_dir);
extract($this->_options, EXTR_SKIP);
$ci = \Phpcmf\Service::C(); // 控制器对象简写
$phpcmf_name && $this->_filename = str_replace('..', '[removed]', $phpcmf_name);
// 加载编译后的缓存文件
$this->_disp_dir = $phpcmf_dir;
$this->_view_file = $_view_file = $this->get_file_name($this->_filename, $phpcmf_dir);
$is_dev = 0;
if ((IS_DEV || (IS_ADMIN && SYS_DEBUG))
&& !isset($_GET['callback']) && !isset($_GET['is_ajax'])
&& !IS_API_HTTP && !IS_AJAX) {
$is_dev = 1;
echo "<!--当前页面的模板文件是:$_view_file (本代码只在开发者模式下显示)-->".PHP_EOL;
}
// 兼容php8
!defined('IS_SHARE') && define('IS_SHARE', 1);
!defined('IS_OEM_CMS') && define('IS_OEM_CMS', 0);
!defined('MOD_DIR') && define('MOD_DIR', '');
!defined('MODULE_NAME') && define('MODULE_NAME', '');
!defined('MODULE_URL') && define('MODULE_URL', '');
!defined('SITE_TITLE') && define('SITE_TITLE', SITE_NAME);
!defined('IS_PC') && define('IS_PC', \Phpcmf\Service::IS_PC_USER());
!defined('IS_MOBILE') && define('IS_MOBILE', \Phpcmf\Service::IS_MOBILE_USER());
!defined('IS_MOBILE_USER') && define('IS_MOBILE_USER', \Phpcmf\Service::IS_MOBILE_USER());
!defined('IS_COMMENT') && define('IS_COMMENT', dr_is_app('comment'));
!defined('IS_CLIENT') && define('IS_CLIENT', '');
$LANG_PATH = LANG_PATH;
$THEME_PATH = THEME_PATH;
$_temp_file = $this->load_view_file($_view_file);
// 挂钩点 模板加载之后
\Phpcmf\Hooks::trigger('cms_view', $this->_options, $_temp_file);
include $_temp_file;
// 挂钩点 模板结束之后
\Phpcmf\Hooks::trigger('cms_view_end');
$this->_view_time = round(microtime(true) - $phpcmf_start, 2);
// 消毁变量
if ($is_destruction) {
unset($this->loadjs);
unset($this->_include_file);
if (!$is_dev) {
unset($this->_options);
}
if (!defined('IS_INSTALL') && (CI_DEBUG || !defined('SC_HTML_FILE'))) {
\Phpcmf\Service::M()->close();
}
}
}
// 动态加载js
public function load_js($js) {
if (isset($this->loadjs[$js])) {
return '';
} else {
$this->loadjs[$js] = 1;
return '<script type=\'text/javascript\' src=\''.$js.'\'></script>';
}
}
/**
* 设置模块/应用的模板目录
*
* @param string $file 文件名
* @param string $dir 模块/应用名称
* @param string $include 是否使用的是include标签
*/
public function get_file_name($file, $dir = null, $include = FALSE) {
if (!$file) {
$this->show_error('模板文件没有设置');
}
$dir = $dir ? $dir : $this->_disp_dir;
$file = str_replace('..', '', (string)$file); // 安全规范化模板名称引入
if ($dir == 'admin' || $this->_is_admin) {
// 后台操作时,不需要加载风格目录,如果文件不存在可以尝试调用主项目模板
if (APP_DIR && is_file(MYPATH.'View/'.APP_DIR.'/'.$file)) {
return MYPATH . 'View/' . APP_DIR . '/' . $file;
} elseif (is_file(MYPATH.'View/'.$file) && is_file(COREPATH.'View/'.$file)) {
if (APP_DIR && is_file($this->_dir.$file)) {
// 防止app目录加载主目录的模板
return $this->_dir.$file;
}
return MYPATH.'View/'.$file;
} elseif ((!APP_DIR or $dir == 'admin') && is_file(MYPATH.'View/'.$file)) {
// 强制定位admin目录时验证my目录文件
return MYPATH.'View/'.$file;
} elseif (is_file($this->_dir.$file)) {
return $this->_dir.$file; // 调用当前后台的模板
} elseif (is_file($this->_froot.$file)) {
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用主目录的模板['.$this->_froot.$file.']';
return $this->_froot.$file; // 当前项目目录模板不存在时调用主项目的
} elseif (is_file($this->_aroot.$file)) {
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用主目录的模板['.$this->_aroot.$file.']';
return $this->_aroot.$file; // 当前项目目录模板不存在时调用主项目的
} elseif (APP_DIR && is_file(MYPATH.'View/'.$file)) {
return MYPATH.'View/'.$file; // 当前项目目录模板不存在时调用主项目的My目录
} elseif ($dir != 'admin' && is_file(APPSPATH.ucfirst($dir).'/Views/'.$file)) {
return APPSPATH.ucfirst($dir).'/Views/'.$file; // 指定模块时调用模块下的文件
}
$error = $this->_dir.$file;
} elseif (IS_MEMBER || $dir == 'member') {
// 会员操作时,需要加载风格目录,如果文件不存在可以尝试调用主项目模板
if ($dir === '/' && is_file($this->_root.$file)) {
return $this->_root.$file;
}
if (IS_XRDEV) {
$this->_dir = dr_get_app_tpl($dir ? $dir : 'member').$this->_tname.'/'.SITE_TEMPLATE.'/member/'.($dir ? $mid.'/' : '');
}
if (is_file($this->_dir.$file)) {
// 调用当前的会员模块目录
return $this->_dir.$file;
} elseif (is_file($this->_mroot.$file)) {
// 调用默认的会员模块目录
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用会员模块目录的模板['.$this->_mroot.$file.']';
return $this->_mroot.$file;
} elseif (is_file($this->_root.$file)) {
// 调用网站主站模块目录
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用主目录的模板['.$this->_root.$file.']';
return $this->_root.$file;
} else {
// 仍然没有找到文件
if (IS_XRDEV && APP_DIR) {
$this->_dir = dr_get_app_tpl(APP_DIR).$this->_tname.'/default/member/'.APP_DIR.'/';
if (is_file($this->_dir.$file)) {
// 调用当前的会员模块目录
return $this->_dir.$file;
}
$this->_dir = dr_get_app_tpl(APP_DIR).$this->_tname.'/default/member/';
if (is_file($this->_dir.$file)) {
// 调用当前的会员模块目录
return $this->_dir.$file;
}
}
}
$error = $dir === '/' ? $this->_root.$file : $this->_dir.$file;
} elseif ($file == 'goto_url') {
// 转向字段模板
return COREPATH.'View/go.html';
} elseif (CLIENT_NAME == '') {
if (is_file($this->_dir.$file)) {
return $this->_dir.$file;
}
return $this->_root.$file;
} else {
if (IS_XRDEV) {
if (\Phpcmf\Service::C()->module) {
$this->_dir = dr_get_app_tpl(APP_DIR).$this->_tname.'/'.SITE_TEMPLATE.'/home/'.APP_DIR.'/';
if (is_file($this->_dir.$file)) {
return $this->_dir.$file;
}
$this->_dir = dr_get_app_tpl('module').$this->_tname.'/'.SITE_TEMPLATE.'/home/'.($dir ? $mid.'/' : '');
}
}
if ($dir === '/' && is_file($this->_root.$file)) {
// 强制主目录
return $this->_root.$file;
} elseif (is_file($this->_dir.$file)) {
// 调用本目录
return $this->_dir.$file;
} elseif (is_file($this->_root.$file)) {
// 再次调用主程序下的文件
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用主目录的模板['.$this->_root.$file.']';
return $this->_root.$file;
}
// 尝试判断主defualt目录
$default_file = TPLPATH.$this->_tname.'/default/home/'.(APP_DIR ? APP_DIR.'/' : '').$file;
if (is_file($default_file)) {
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用default目录的模板['.$default_file.']';
return $default_file;
}
$error = $dir === '/' ? $this->_root.$file : $this->_dir.$file;
$default_file = str_replace($this->_root, TPLPATH.$this->_tname.'/default/home/', $error);
if (is_file($default_file)) {
$this->_load_file_tips[$file] = '由于模板文件['.$this->_dir.$file.']不存在,因此本页面引用default目录的模板['.$default_file.']';
return $default_file;
}
}
/*
// 如果移动端模板不存在就调用主网站风格
if ($this->_is_mobile && is_file(str_replace('/mobile/', '/pc/', $error))) {
return str_replace('/mobile/', '/pc/', $error);
} elseif ($this->_is_mobile && is_file(str_replace('/mobile/', '/pc/', $this->_root.$file))) {
return str_replace('/mobile/', '/pc/', $this->_root.$file);
}*/
if (in_array($file, ['msg.html', '404.html'])) {
if (IS_USE_MODULE && is_file(TPLPATH.'pc/default/home/'.$file)) {
return TPLPATH.'pc/default/home/'.$file;
} elseif (is_file(TPLPATH.$file)) {
return TPLPATH.$file;
}
}
if (CI_DEBUG) {
log_message('error', '模板文件['.$error.']不存在');
if ($file == 'msg.html' || $file == '404.html') {
return COREPATH.'View/api_msg.html';
}
if ($this->_is_mobile && $default_file) {
$pc = str_replace('/mobile/', '/pc/', (string)$default_file);
if (is_file($pc)) {
$this->show_error('移动端模板文件不存在', $error, $pc);exit;
}
}
}
$this->show_error('模板文件不存在', $error);
}
/**
* 设置模板变量
*/
public function assign($key, $value = '') {
if (!$key) {
return FALSE;
}
if (is_array($key)) {
foreach ($key as $k => $v) {
$this->_options[$k] = $v;
}
} else {
$this->_options[$key] = $value;
}
}
/**
* 获取模板变量
*/
public function get_value($key) {
if (!$key) {
return '';
}
return $this->_options[$key];
}
/**
* 重新赋值模板变量
*/
public function set_value($key, $value = '', $replace = '') {
if (!$key) {
return '';
}
$this->_options[$key] = $replace ? str_replace($replace, $value, $this->_options[$key]) : $value;
}
/**
* 模板标签include/template
*
* @param string $name 模板文件
* @param string $dir 应用、模块目录
* @return bool
*/
public function _include($name, $dir = '') {
$dir = $dir == 'MOD_DIR' ? MOD_DIR : $dir;
$file = $this->get_file_name($name, $dir, TRUE);
$fname = md5($file);
isset($this->_include_file[$fname]) ? $this->_include_file[$fname] ++ : $this->_include_file[$fname] = 0;
if ($this->_include_file[$fname] > 500) {
$this->show_error('模板文件标签template引用文件目录结构错误', $file);
}
return $this->load_view_file($file);
}
/**
* 模板标签load
*
* @param string $file 模板文件
* @return bool
*/
public function _load($file) {
$file = (string)$file;
if ($file === '' || strpos($file, '://') !== false) {
$this->show_error('模板文件路径非法', $file);
}
if (!in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), ['html', 'htm'])) {
$this->show_error('模板文件只能是html、htm格式', $file);
}
$fname = md5($file);
isset($this->_include_file[$fname]) ? $this->_include_file[$fname] ++ : $this->_include_file[$fname] = 0;
if ($this->_include_file[$fname] > 500) {
$this->show_error('模板文件标签load引用文件目录结构错误', $file);
}
$safe_name = $this->_safe_template_path($file);
if ($safe_name && is_file($safe_name)) {
// 安全目录
} else {
$file = $this->get_file_name($file);
}
return $this->load_view_file($file);
}
/**
* 允许的模板根目录(FCPATH / TPLPATH / WRITEPATH
*/
private function _template_roots() {
static $roots = null;
if ($roots !== null) {
return $roots;
}
$path = [FCPATH, TPLPATH, WRITEPATH];
if (defined('XRDEV_PATH')) {
$path[] = XRDEV_PATH;
}
$roots = [];
foreach ($path as $dir) {
$r = realpath($dir);
$r && $roots[] = $r;
}
return $roots;
}
/**
* 校验模板源文件:仅允许 html/htm,且位于允许的模板根目录下
* 失败时写入 $_safe_template_error,开发者模式下由调用方展示
*
* @param string $path 模板绝对/相对路径
* @return string|false 合法绝对路径,失败返回 false
*/
public function _safe_template_path($path) {
$this->_safe_template_error = '';
if (!$path || strpos($path, "\0") !== false) {
$this->_safe_template_error = '路径为空或包含非法空字符';
return false;
}
$real = realpath($path);
if (!$real) {
$this->_safe_template_error = 'realpath失败,文件不存在或路径不可解析:'.$path;
return false;
}
if (!is_file($real)) {
$this->_safe_template_error = '路径不是普通文件:'.$real;
return false;
}
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
if (!in_array($ext, ['html', 'htm'])) {
$this->_safe_template_error = '扩展名必须是 html/htm,当前为:'.($ext !== '' ? $ext : '(无)');
return false;
}
$roots = $this->_template_roots();
if (!$roots) {
$this->_safe_template_error = '未配置可用模板根目录(FCPATH/TPLPATH/WRITEPATH 等 realpath 均为空)';
return false;
}
foreach ($roots as $root) {
if (strncmp($real, $root, strlen($root)) === 0) {
return $real;
}
}
$this->_safe_template_error = '文件不在允许的模板根目录内。文件:'.$real.';允许目录:'.implode('、', $roots);
return false;
}
/**
* 加载缓存文件
*
* @param string
* @return string
*/
public function load_view_file($name) {
$safe_name = $this->_safe_template_path($name);
if (!$safe_name) {
$msg = '模板文件不存在或路径非法';
if ((defined('IS_DEV') && IS_DEV) || CI_DEBUG) {
$msg = $this->_safe_template_error ? ''.$this->_safe_template_error : '';
}
$this->show_error($msg, $name);
}
$name = $safe_name;
$cache_file = $this->_cache.str_replace([
WEBPATH, '/', '\\', DIRECTORY_SEPARATOR, ':', '?', '*', '|', '<', '>'
], [
'', '_DS_', '_DS_', '_DS_', '', '', '', '', '', ''
], $name).'.cache.php';
$this->_view_files[$name] = [
'name' => pathinfo($name, PATHINFO_BASENAME),
'path' => $name,
];
// 当缓存文件不存在时或者缓存文件创建时间少于了模板文件时,再重新生成缓存文件
// 开发者模式下关闭缓存
if (IS_DEV || !is_file($cache_file) || (is_file($cache_file) && is_file($name) && filemtime($cache_file) < filemtime($name))) {
// 防止目录未创建的情况
$path = dirname($cache_file);
if (!is_dir($path)) {
dr_mkdirs($path);
}
// 写入新文件
$content = $this->handle_view_file(file_get_contents($name));
if (file_put_contents($cache_file, $content, LOCK_EX) === FALSE) {
if (IS_DEV) {
$this->show_error('模板缓存文件 ('.$cache_file.') 创建失败,请将cache目录权限设为777');
} else {
$this->show_error('请将模板缓存目录 ('.$path.') 权限设为777');
}
}
}
return $cache_file;
}
// 将模板代码转化为php
public function code2php($code, $time = 0, $include = 1) {
$file = md5($code).$time.'.code.php';
if (!$include) {
$code = preg_replace([
'#{template.*}#Uis',
'#{load.*}#Uis'
], [
'--',
'--',
], $code);
}
if (!is_file($this->_cache.$file)) {
file_put_contents($this->_cache.$file, str_replace('$this->', '\Phpcmf\Service::V()->', $this->handle_view_file($code)));
}
return $this->_cache.$file;
}
/**
* 解析模板文件
*
* @param string
* @param string
* @return string
*/
public function handle_view_file($view_content) {
if (!$view_content) {
return '';
}
if (function_exists('my_parser_view_rule')) {
$view_content = my_parser_view_rule($view_content);
}
// 正则表达式匹配的模板标签
$regex_array = [
// 3维数组变量
'#{\$(\w+?)\.(\w+?)\.(\w+?)\.(\w+?)}#i',
// 2维数组变量
'#{\$(\w+?)\.(\w+?)\.(\w+?)}#i',
// 1维数组变量
'#{\$(\w+?)\.(\w+?)}#i',
// 3维数组变量
'#\$(\w+?)\.(\w+?)\.(\w+?)\.(\w+?)#Ui',
// 2维数组变量
'#\$(\w+?)\.(\w+?)\.(\w+?)#Ui',
// 1维数组变量
'#\$(\w+?)\.(\w+?)#Ui',
// PHP函数
'#{([a-z_0-9]+)\((.*)\)}#Ui',
// PHP常量
'#{([A-Z_0-9]+)}#',
// PHP变量
'#{\$(.+?)}#i',
// 类库函数
'#{([A-Za-z_]+)::(.+)\((.*)\)}#Ui',
// 引入模板
'#{\s*template\s+"([\$\-_\/\w\.]+)",\s*"(.+)"\s*}#Uis',
'#{\s*template\s+"([\$\-_\/\w\.]+)",\s*MOD_DIR\s*}#Uis',
'#{\s*template\s+"([\$\-_\/\w\.]+)"\s*}#Uis',
'#{\s*template\s+([\$\-_\/\w\.]+)\s*}#Uis',
// 加载指定文件到模板
'#{\s*load\s+"([\$\-_\/\w\.]+)"\s*}#Uis',
'#{\s*load\s+([\$\-_\/\w\.]+)\s*}#Uis',
// php标签
'#{php\s+(.+?)}#is',
// list标签
'#{list\s+(.+?)return=([\w]+)}#i', //(.+?)\s?
'#{list\s+(.+?)\s?}#i',
'#{\s?\/list\s?}#i',
// if判断语句
'#{\s?if\s+(.+?)\s?}#i',
'#{\s?else\sif\s+(.+?)\s?}#i',
'#{\s?elseif\s+(.+?)\s?}#i',
'#{\s?else\s?}#i',
'#{\s?\/if\s?}#i',
// 循环语句
'#{\s?loop\s+\$(.+?)\s+\$(\w+?)\s?\$(\w+?)\s?}#i',
'#{\s?loop\s+\$(.+?)\s+\$(\w+?)\s?}#i',
'#{\s?loop\s+\$(.+?)\s+\$(\w+?)\s?=>\s?\$(\w+?)\s?}#i',
'#{\s?\/loop\s?}#i',
// for
'#{for\s+(.+?)\s+(.+?)\s+(.+?)\s?}#i',
'#{\s?\/for\s?}#i',
// 结束标记
'#{\s?php\s?}#i',
'#{\s?\/php\s?}#i',
'#\?\>\s*\<\?php\s#s',
// 结果为空时
'#{\s?empty\s?}#i',
'#{\s?\/empty\s?}#i',
];
// 替换直接变量输出
$replace_array = [
"<?php echo \$\\1['\\2']['\\3']['\\4']; ?>",
"<?php echo \$\\1['\\2']['\\3']; ?>",
"<?php echo \$\\1['\\2']; ?>",
"\$\\1['\\2']['\\3']['\\4']",
"\$\\1['\\2']['\\3']",
"\$\\1['\\2']",
"<?php echo \\1(\\2); ?>",
"<?php echo \\1; ?>",
"<?php echo \$\\1; ?>",
"<?php echo \\Phpcmf\\Service::L('\\1')->\\2(\\3); ?>",
//"<?php echo \$this->library_method(\"\\1\",\"\\2\", \$this->_get_method(\\3));,
"<?php if (\$fn_include = \$this->_include(\"\\1\", \"\\2\")) { include(\$fn_include); } ?>",
"<?php if (\$fn_include = \$this->_include(\"\\1\", \"MOD_DIR\")) { include(\$fn_include); } ?>",
"<?php if (\$fn_include = \$this->_include(\"\\1\")) { include(\$fn_include); } ?>",
"<?php if (\$fn_include = \$this->_include(\"\\1\")) { include(\$fn_include); } ?>",
"<?php if (\$fn_include = \$this->_load(\"\\1\")) { include(\$fn_include); } ?>",
"<?php if (\$fn_include = \$this->_load(\"\\1\")) { include(\$fn_include); } ?>",
"<?php \\1 ?>",
"<?php \$return_\\2 = [];\$list_return_\\2 = \$this->list_tag(\"\\1 return=\\2\"); if (\$list_return_\\2) { extract(\$list_return_\\2, EXTR_OVERWRITE); \$count_\\2=dr_count(\$return_\\2);} if (is_array(\$return_\\2) && \$return_\\2) { \$key_\\2=-1;foreach (\$return_\\2 as \$\\2) { \$key_\\2++; \$is_first=\$key_\\2==0 ? 1 : 0;\$is_last=\$count_\\2==\$key_\\2+1 ? 1 : 0; ?>",
"<?php \$return = [];\$list_return = \$this->list_tag(\"\\1\"); if (\$list_return) { extract(\$list_return, EXTR_OVERWRITE); \$count=dr_count(\$return);} if (is_array(\$return) && \$return) { \$key=-1; foreach (\$return as \$t) { \$key++; \$is_first=\$key==0 ? 1 : 0;\$is_last=\$count==\$key+1 ? 1 : 0; ?>",
"<?php } } ?>",
"<?php if (\\1) { ?>",
"<?php } else if (\\1) { ?>",
"<?php } else if (\\1) { ?>",
"<?php } else { ?>",
"<?php } ?>",
"<?php if (isset(\$\\1) && is_array(\$\\1) && \$\\1) { \$key_\\3=-1;\$count_\\3=dr_count(\$\\1);foreach (\$\\1 as \$\\2=>\$\\3) { \$key_\\3++; \$is_first=\$key_\\3==0 ? 1 : 0;\$is_last=\$count_\\3==\$key_\\3+1 ? 1 : 0; ?>",
"<?php if (isset(\$\\1) && is_array(\$\\1) && \$\\1) { \$key_\\2=-1;\$count_\\2=dr_count(\$\\1);foreach (\$\\1 as \$\\2) { \$key_\\2++; \$is_first=\$key_\\2==0 ? 1 : 0;\$is_last=\$count_\\2==\$key_\\2+1 ? 1 : 0;?>",
"<?php if (isset(\$\\1) && is_array(\$\\1) && \$\\1) { \$key_\\3=-1;\$count_\\3=dr_count(\$\\1);foreach (\$\\1 as \$\\2=>\$\\3) { \$key_\\3++; \$is_first=\$key_\\3==0 ? 1 : 0;\$is_last=\$count_\\3==\$key_\\3+1 ? 1 : 0; ?>",
"<?php } } ?>",
"<?php for (\\1 ; \\2 ; \\3) { ?>",
"<?php } ?>",
"<?php ",
" ?>",
" ",
"<?php } } else { ?>",
"<?php } ?>",
];
// 统计和求和
foreach (['sum', 'count'] as $name) {
// 正则表达式匹配的模板标签
$regex_array[] = '#{'.$name.'\s+(.+?)return=([\w]+)}#i';// 去掉\swin平台
$regex_array[] = '#{'.$name.'\s+(.+?)\s?}#i';
// 替换直接变量输出
$replace_array[] = "<?php \$return_".$name."_\\2 = [];\$list_return_".$name."_\\2 = \$this->list_tag(\"".$name." \\1 return=\\2\"); if (\$list_return_".$name."_\\2 && is_array(\$list_return_".$name."_\\2)) { extract(\$list_return_".$name."_\\2, EXTR_OVERWRITE); \$\\2_".$name."=(\$return_".$name." ? \$return_".$name."[0]['ct'] : 0); } else { \$\\2_".$name."= 0; } ?>";
$replace_array[] = "<?php \$return_".$name." = [];\$list_return_".$name." = \$this->list_tag(\"".$name." \\1\"); if (\$list_return_".$name." && is_array(\$list_return_".$name.")) { extract(\$list_return_".$name.", EXTR_OVERWRITE); echo (\$return_".$name." ? \$return_".$name."[0]['ct'] : 0); } else { echo 0; } ?>";
}
// list标签别名
foreach ($this->action as $name) {
// 正则表达式匹配的模板标签
$regex_array[] = '#{'.$name.'\s+(.+?)return=([\w]+)}#i';// 去掉\swin平台
$regex_array[] = '#{'.$name.'\s+(.+?)\s?}#i';
$regex_array[] = '#{\s?\/'.$name.'\s?}#i';
// 替换直接变量输出
$replace_array[] = "<?php \$return_\\2 = [];\$list_return_\\2 = \$this->list_tag(\"action=".$name." \\1 return=\\2\"); if (\$list_return_\\2 && is_array(\$list_return_\\2)) { extract(\$list_return_\\2, EXTR_OVERWRITE); } \$count_\\2=dr_count(\$return_\\2); if (is_array(\$return_\\2) && \$return_\\2) { \$key_\\2=-1; foreach (\$return_\\2 as \$\\2) { \$key_\\2++; \$is_first=\$key_\\2==0 ? 1 : 0;\$is_last=\$count_\\2==\$key_\\2+1 ? 1 : 0; ?>";
$replace_array[] = "<?php \$return = [];\$list_return = \$this->list_tag(\"action=".$name." \\1\"); if (\$list_return && is_array(\$list_return)) { extract(\$list_return, EXTR_OVERWRITE); } \$count=dr_count(\$return); if (is_array(\$return) && \$return) { \$key=-1; foreach (\$return as \$t) { \$key++; \$is_first=\$key==0 ? 1 : 0;\$is_last=\$count==\$key+1 ? 1 : 0; ?>";
$replace_array[] = "<?php } } ?>";
}
// 注释内容
$view_content = preg_replace('#{note}(.+){/note}#Us', '', $view_content);
// 保护代码
$this->_code = [];
$view_content = preg_replace_callback('#{code}(.+){/code}#Us', function ($match) {
$key = count($this->_code);
$this->_code[$key] = $match[1];
return '<!--phpcmf'.$key.'-->';
}, $view_content);
$view_content = preg_replace($regex_array, $replace_array, $view_content);
$view_content = preg_replace_callback("/_get_var\('(.*)'\)/Ui", function ($match) {
return "_get_var('".preg_replace('#\[\'(\w+)\'\]#Ui', '.\\1', $match[1])."')";
}, $view_content);
$view_content = preg_replace_callback("/list_tag\(\"(.*)\"\)/Ui", function ($match) {
return "list_tag(\"".preg_replace('#\[\'(\w+)\'\]#Ui', '[\\1]', $match[1])."\")";
}, $view_content);
// 恢复代码:仅转义 PHP 标签边界,HTML/JS 原样输出,PHP 源码在浏览器可见且不执行
if ($this->_code) {
foreach ($this->_code as $key => $code) {
$code = preg_replace_callback('#<\?(?:php|=)?|\?>#i', function ($m) {
return htmlspecialchars($m[0], ENT_QUOTES, 'UTF-8');
}, $code);
$view_content = str_replace(
'<!--phpcmf'.$key.'-->',
$code,
$view_content
);
}
}
return $view_content;
}
// 方法类解析
public function library_method($class, $method, $param) {
return call_user_func_array(
[\Phpcmf\Service::L($class), $method],
$param
);
}
// 替换方法变量
public function _get_method(...$params) {
if (!$params) {
return [];
}
$value = [];
foreach ($params as $var) {
if ($var && strpos($var, '$') === 0) {
$value[] = preg_replace('/\[(.+)\]/U', '[\'\\1\']', $var);
} else {
$value[] = $var;
}
}
return $value;
}
// 存储缓存
private function _save_cache_data($cache_name, $data, $time) {
\Phpcmf\Service::L('cache')->set_data($cache_name, $data, $time);
}
// 解析参数变量和值
private function _get_params_var_val($t) {
if (!$t) {
return ['', ''];
}
$arr = ['!=' => 'NOT_', '>=' => 'EGT_', '<=' => 'ELT_', '==' => '', '=' => '', '>' => 'GT_', '<' => 'LT_'];
foreach ($arr as $a => $v) {
if (strpos($t, $a) !== FALSE) {
$var = substr($t, 0, strpos($t, $a));
$val = substr($t, strpos($t, $a) + strlen($a));
return [$v.$var, $val];
}
}
return ['', ''];
}
// list 标签解析
public function list_tag($_params) {
$param = $where = [];
$system = [
'db' => '', // 数据源
'app' => '', // 指定插件时
'num' => '', // 显示数量
'limit' => '', // 显示数量
'maxlimit' => '', // 显示数量
'sum' => '', // 求和字段
'form' => '', // 表单
'page' => '', // 是否分页
'site' => '', // 站点id
'flag' => '', // 推荐位id
'not_flag' => '', // 排除推荐位id
'show_flag' => '', // 显示推荐位名称
'more' => '', // 是否显示栏目模型表
'catid' => '', // 栏目id,支持多id
'field' => '', // 显示字段
'order' => '', // 排序
'space' => '', // 空间uid
'table' => '', // 表名变量
'table_site' => '', // 当前站点表名变量
'total' => '', // 分页总数据
'join' => '', // 关联表名
'on' => '', // 关联表条件
'cache' => SYS_CACHE ? SYS_CACHE_LIST * 3600 : 0, // 默认缓存时间
'action' => '', // 动作标识
'return' => '', // 返回变量
'sbpage' => '', // 不按默认分页
'module' => '', // 模块名称
'urlrule' => '', // 自定义分页规则
'firsturl' => '', // 分页是第一页的固定地址
'pagesize' => '', // 自定义分页数量
'pagefile' => '', // 自定义分页配置文件
];
if (!$system['pagefile'] && $this->_is_admin) {
$system['pagefile'] = 'admin';
}
$_params = trim($_params);
$this->_list_tag = '{list '.$_params.'}';
$this->_list_param = [];
// 过滤掉自定义where语句
if (preg_match('/where=\'(.+)\'/sU', $_params, $match)) {
$param['where'] = $match[1];
$_params = str_replace($match[0], '', $_params);
}
// 判断是否是统计
if (stripos($_params, 'count') === 0) {
$_params = trim(substr($_params, 5));
$this->_return_sql = 'count';
} elseif (stripos($_params, 'sum') === 0) {
$_params = trim(substr($_params, 3));
$this->_return_sql = 'sum';
} else {
$this->_return_sql = '';
}
$params = explode(' ', $_params);
if (in_array($params[0], $this->action)) {
$params[0] = 'action='.$params[0];
}
$sysadj = [
'IN', 'BEWTEEN', 'BETWEEN', 'LIKE', 'NOT', 'BW',
'NOTLIKE', 'NOTJSON', 'NOTFIND', 'NOTIN', 'NOTNULL', 'NULL',
'GT', 'EGT', 'LT', 'ELT',
'DAY', 'MONTH', 'MAP', 'YEAR', 'SEASON', 'WEEK',
'JSON', 'FIND'
];
foreach ($params as $t) {
list($var, $val) = $this->_get_params_var_val($t);
if (!$var) {
continue;
}
$val = defined($val) ? constant($val) : dr_rp_view($val, true);
if ($var == 'fid' && !$val) {
continue;
}
if (isset($system[$var])) { // 系统参数,只能出现一次,不能添加修饰符
if ($var != 'cache' && $system[$var]) {
continue; // 防止重复记录
}
$system[$var] = in_array($var, ['urlrule']) ? $val : dr_safe_replace($val);
} else {
if (preg_match('/^([A-Z_]+)(.+)/', $var, $match)) { // 筛选修饰符参数
$_adj = '';
$_pre = explode('_', $match[1]);
foreach ($_pre as $p) {
if (in_array($p, $sysadj)) {
$_adj = $p;
}
}
$where[$var] = [
'adj' => $_adj,
'name' => $match[2],
'value' => $val
];
} else {
$where[$var] = [
'adj' => '',
'name' => $var,
'value' => $val
];
}
$param[$var] = $val; // 用于特殊action
}
$this->_list_param[$var] = $val;
}
// return位置判断
if ($system['return'] && strpos(end($params), 'return=') === false) {
return $this->_return($system['return'], $this->_return_sql.'标签中的return参数只能放在标签的尾部');
}
// 替换order中的非法字符
isset($system['order']) && $system['order'] && $system['order'] = str_ireplace(
['"', "'", ';', 'select', 'insert'], //, '`', ')', '('
'',
$system['order']
);
// limit别名映射num
if (isset($system['limit']) && !isset($system['num'])) {
$system['num'] = $system['limit'];
}
// 开发模式下显示求和字段
if (CI_DEBUG) {
$this->_sum_field = $system['sum'];
}
$action = $system['action'];
// 当hits动作时,定位到moule动作
$system['action'] == 'hits' && $system['action'] = 'module';
// 默认站点参数
$system['site'] = !$system['site'] ? SITE_ID : $system['site'];
// 默认模块参数
$system['module'] = $dirname = strtolower($system['module'] ? (string)$system['module'] : (string)\Phpcmf\Service::C()->module['dirname']);
// 格式化field
$system['field'] && $system['field'] = urldecode($system['field']);
// 分页页码变量
$this->_page_value = $system['page'];
if (in_array(strtoupper($system['order']), ['RAND()', 'RAND'])) {
$cache_name = 'view-'.IS_CLIENT.'-'.$this->_return_sql.md5($_params.dr_now_url().$this->_get_page_id($system['page']).$this->_is_mobile);
} else {
$cache_name = 'view-'.IS_CLIENT.'-'.$this->_return_sql.($system['page'] ? $this->_get_page_id($system['page']) : 'one').md5($_params.$this->_is_mobile);
}
if (!CI_DEBUG && SYS_CACHE && $system['cache']) {
$cache_data = \Phpcmf\Service::L('cache')->get_data($cache_name);
if ($cache_data) {
$this->_page_used = $cache_data['page_used'];
$this->_page_urlrule = $cache_data['page_urlrule'];
return $this->_return($system['return'], $cache_data['data'], $cache_data['sql'], $cache_data['total'], $cache_data['pages'], $cache_data['pagesize'], $system['cache']);
}
}
// 运算排除
if ($this->_return_sql && !in_array($action,
['sql', 'module', 'member', 'form', 'mform', 'comment', 'table', 'tag', 'related'])) {
return $this->_return($system['return'], $this->_return_sql.'标签不支持[acntion='.$action.']的运算');
}
// 判断标签是否是搜索标签
$this->_is_list_search = 0;
// action
switch ($system['action']) {
case 'function': //执行函数
if (!isset($param['name'])) {
return $this->_return($system['return'], 'name参数不存在');
} elseif (strpos($param['name'], 'my_') !== 0) {
return $this->_return($system['return'], '自定义函数['.$param['name'].']必须以my_开头');
} elseif (!function_exists($param['name'])) {
return $this->_return($system['return'], '自定义函数['.$param['name'].']未定义');
}
$name = 'function-'.md5(dr_array2string($param));
$cache = \Phpcmf\Service::L('cache')->get_data($name);
if (!$cache) {
$p = [];
foreach ($param as $var => $t) {
if (strpos($var, 'param') === 0) {
$n = intval(substr($var, 5));
$p[$n] = $t;
}
}
if ($p) {
$rt = call_user_func_array($param['name'], $p);
} else {
$rt = call_user_func($param['name']);
}
$cache = [
$rt
];
$system['cache'] && \Phpcmf\Service::L('cache')->set_data($name, $cache, $system['cache']);
}
return $this->_return($system['return'], $cache, '');
break;
case 'cache': // 系统缓存数据
if (!isset($param['name'])) {
return $this->_return($system['return'], 'name参数不存在');
}
$pos = strpos($param['name'], '.');
if ($pos !== FALSE) {
$_name = substr($param['name'], 0, $pos);
$_param = substr($param['name'], $pos + 1);
} else {
$_name = $param['name'];
$_param = NULL;
}
$cache = $this->_cache_var($_name, $system['site']);
if (!$cache) {
return $this->_return($system['return'], "缓存({$_name})不存在,请在后台更新缓存");
} elseif ($_name == 'module-content') {
// 指定模块
if ($system['module'] && $system['module'] != 'all') {
$mid = explode(',', $system['module']);
foreach ($cache as $i => $t) {
if (!in_array($t['dirname'], $mid)) {
unset($cache[$i]);
}
}
if (!$cache) {
return $this->_return($system['return'], '指定的模块('.$system['module'].')都不存在');
}
}
// 读取内容模块更多信息
if ($system['more']) {
foreach ($cache as $i => $t) {
$cache[$i] = \Phpcmf\Service::L('cache')->get('module-'.$system['site'].'-'.$t['dirname']);
}
}
}
if ($_param) {
$data = dr_get_param_var($cache, $_param);
//eval('$data=$cache'.$this->_get_var($_param).';');
if (!$data) {
return $this->_return($system['return'], "缓存({$_name})参数不存在!!");
}
} else {
$data = $cache;
}
return $this->_return($system['return'], $data, '');
break;
case 'linkage': // 联动菜单
if (isset($param['pid']) && $param['pid']) {
$link = dr_linkage($param['code'], $param['pid']);
if (!$link) {
return $this->_return($system['return'], "联动菜单{$param['code']}不存在,请在后台一键生成数据");
}
if (!$link['child']) {
// 表示本身就是子菜单,我们需要调用父级作为同级显示
$param['pid'] = $link['pid'];
}
}
$linkage = dr_linkage_list($param['code'], isset($param['pid']) && $param['pid'] ? $param['pid'] : 0);
if (!$linkage) {
return $this->_return($system['return'], "联动菜单{$param['code']}不存在,请在后台一键生成数据");
}
$i = 0;
$return = [];
foreach ($linkage as $t) {
if ($system['num'] && $i >= $system['num']) {
break;
} elseif (isset($param['id']) && !dr_in_array($t['id'], explode(',', $param['id']))) {
continue;
} elseif (isset($param['ii']) && !dr_in_array($t['ii'], explode(',', $param['ii']))) {
continue;
}
$return[] = $t;
$i ++;
}
$data = isset($param['call']) && $param['call'] ? array_reverse($return) : $return;
// 存储缓存
$system['cache'] && $data && $this->_save_cache_data($cache_name, [
'data' => $data,
'sql' => '',
'total' => '',
'pages' => '',
'pagesize' => '',
'page_used' => $this->_page_used,
'page_urlrule' => $this->_page_urlrule,
], $system['cache']);
return $this->_return($system['return'], $data, '');
break;
case 'sql': // 直接sql查询
if (preg_match('/sql=\'(.+)\'/sU', $_params, $sql)) {
// 替换前缀
$sql = str_replace(
['@#S', 'S@#', '@#'],
[\Phpcmf\Service::M()->dbprefix($system['site']), \Phpcmf\Service::M()->dbprefix($system['site']), \Phpcmf\Service::M()->dbprefix()],
trim(urldecode($sql[1]))
);
if (stripos($sql, 'SELECT') !== 0) {
return $this->_return($system['return'], 'SQL语句只能是SELECT查询语句');
} elseif (preg_match('/select(.*)into outfile(.*)/i', $sql)) {
return $this->_return($system['return'], 'SQL语句只能是SELECT查询语句');
} elseif (preg_match('/select(.*)into dumpfile(.*)/i', $sql)) {
return $this->_return($system['return'], 'SQL语句只能是SELECT查询语句');
}
$total = 0;
$pages = '';
// 统计标签
if ($this->_return_sql) {
$sql = preg_replace('/select .* from /iUs', 'SELECT '.$this->_select_rt_name.' FROM ', $sql, 1);
} else {
// 如存在分页条件才进行分页查询
if ($system['page']) {
$page = $this->_get_page_id($system['page']);
$row = $this->_query(preg_replace('/select .* from /iUs', 'SELECT count(*) as c FROM ', $sql, 1), $system, FALSE);
$total = (int)$row['c'];
$pagesize = $system['pagesize'] ? $system['pagesize'] : 10;
// 没有数据时返回空
if (!$total) {
return $this->_return($system['return'], '没有查询到内容', $sql, 0);
}
$sql.= ' LIMIT '.intval($pagesize * ($page - 1)).','.$pagesize;
$pages = $this->_new_pagination($system, $pagesize, $total);
}
}
$data = $this->_query($sql, $system);
// 存储缓存
$system['cache'] && $data && $this->_save_cache_data($cache_name, [
'data' => $data,
'sql' => $sql,
'total' => $total,
'pages' => $pages,
'pagesize' => $pagesize,
'page_used' => $this->_page_used,
'page_urlrule' => $this->_page_urlrule,
], $system['cache']);
return $this->_return($system['return'], $data, $sql, $total, $pages, $pagesize);
} else {
return $this->_return($system['return'], '参数不正确,SQL语句必须用单引号包起来'); // 没有查询到内容
}
break;
case 'table': // 表名查询
if (!$system['table'] && !$system['table_site']) {
return $this->_return($system['return'], 'table参数不存在');
}
// 填充当前站点id的写法
if ($system['table_site']) {
$system['table'] = dr_site_table_prefix($system['table_site'], SITE_ID);
}
$tableinfo = \Phpcmf\Service::L('cache')->get_data('table-'.$system['table']);
if (!$tableinfo) {
$tableinfo = \Phpcmf\Service::M('Table')->get_field($system['table']);
\Phpcmf\Service::L('cache')->set_data('table-'.$system['table'], $tableinfo, 36000);
}
if (!$tableinfo) {
return $this->_return($system['return'], '表('.$system['table'].')结构不存在');
}
// 是否操作自定义where
if ($param['where']) {
$where[] = [
'adj' => 'SQL',
'value' => urldecode($param['where'])
];
unset($param['where']);
}
$table = \Phpcmf\Service::M()->dbprefix($system['table']);
$where = $this->_set_where_field_prefix($where, $tableinfo, $table); // 给条件字段加上表前缀
$system['field'] = $this->_set_select_field_prefix($system['field'], $tableinfo, $table); // 给显示字段加上表前缀
$_order = [];
$_order[$table] = $tableinfo;
$total = 0;
$sql_from = \Phpcmf\Service::M()->quote_table($table); // sql的from子句
// 关联表
if ($system['join'] && $system['on']) {
$rt = $this->_join_table($table, $system, $where, $_order, $sql_from);
if (!$rt['code']) {
return $this->_return($system['return'], $rt['msg']);
}
list($system, $where, $_order, $sql_from) = $rt['data'];
}
// catid 参数
if ($system['catid']) {
$where[] = [
'adj' => 'SQL',
'value' => urldecode(\Phpcmf\Service::M()->quote_field($table, 'catid').' IN ('.$system['catid'].')')
];
}
$sql_limit = $pages = '';
$sql_where = $this->_get_where($where); // sql的where子句
// 统计标签
if ($this->_return_sql) {
$sql = "SELECT ".$this->_select_rt_name." FROM $sql_from ".($sql_where ? "WHERE $sql_where" : "")." ORDER BY NULL";
} else {
if ($system['page']) {
$page = $this->_get_page_id($system['page']);
$pagesize = (int) $system['pagesize'];
$pagesize = $pagesize ? $pagesize : 10;
$sql = "SELECT count(*) as c FROM $sql_from ".($sql_where ? "WHERE $sql_where" : "")." ORDER BY NULL";
$row = $this->_query($sql, $system, FALSE);
$total = (int)$row['c'];
if ($system['maxlimit'] && $total > $system['maxlimit']) {
$total = $system['maxlimit']; // 最大限制
if ($page * $pagesize > $total) {
$return_data = $this->_return($system['return'], 'maxlimit设置最大显示'.$system['maxlimit'].'条,当前('.$total.')已超出', $sql, 0);
return;
}
}
// 没有数据时返回空
if (!$total) {
return $this->_return($system['return'], '没有查询到内容', $sql, 0);
}
$sql_limit = 'LIMIT '.intval($pagesize * ($page - 1)).','.$pagesize;
$pages = $this->_new_pagination($system, $pagesize, $total);
} elseif ($system['num']) {
$sql_limit = "LIMIT {$system['num']}";
}
$system['order'] = $this->_set_orders_field_prefix($system['order'], $_order); // 给排序字段加上表前缀
$sql = "SELECT ".$this->_get_select_field($system['field'] ? $system['field'] : "*")." FROM $sql_from ".($sql_where ? "WHERE $sql_where" : "")." ".($system['order'] ? "ORDER BY {$system['order']}" : "")." $sql_limit";
}
$data = $this->_query($sql, $system);
if (is_array($data) && $data) {
// 表的系统字段
$myfield = \Phpcmf\Service::M('field')->get_mytable_field($system['table'], 0);
if (!$myfield) {
$myfield = \Phpcmf\Service::M('field')->get_mytable_field($system['table'], SITE_ID);
}
if ($myfield) {
$dfield = \Phpcmf\Service::L('Field')->app();
foreach ($data as $i => $t) {
$data[$i] = $dfield->format_value($myfield, $t, 1);
}
}
// 存储缓存
$system['cache'] && $data && $this->_save_cache_data($cache_name, [
'data' => $data,
'sql' => $sql,
'total' => $total,
'pages' => $pages,
'pagesize' => $pagesize,
'page_used' => $this->_page_used,
'page_urlrule' => $this->_page_urlrule,
], $system['cache']);
}
return $this->_return($system['return'], $data, $sql, $total, $pages, $pagesize);
break;
default :
// 插件自定义标签
if (!$system['app']) {
if (in_array($system['action'], [
'member',
'page', 'navigator', 'tag',
'comment', 'form', 'mform'
])) {
$system['app'] = $system['action'];
} elseif (in_array($system['action'], [
'category_search_field', 'content', 'category',
'related', 'search', 'modules', 'module'
])) {
$system['app'] = 'module';
}
}
$return_data = [];
// 当来自插件目录
if ($system['app']) {
if (!dr_is_app($system['app'])) {
return $this->_return($system['return'], '本插件('.$system['app'].')没有安装');
}
$myfile = dr_get_app_dir($system['app']).'Action/'.dr_safe_filename(ucfirst($system['action'])).'.php';
if (is_file($myfile)) {
$rs = require $myfile;
if (!$return_data && is_array($rs)) {
return $rs;
}
return $return_data;
} else {
return $this->_return($system['return'], '本插件('.$system['app'].')没有('.$system['action'].')标签');
}
} else {
// 识别自定义标签
$myfile = MYPATH.'Action/'.dr_safe_filename(ucfirst($system['action'])).'.php';
if (is_file($myfile)) {
$rs = require $myfile;
if (!$return_data && is_array($rs)) {
return $rs;
}
return $return_data;
} else {
return $this->_return($system['return'], '无此标签('.$system['action'].')');
}
}
break;
}
}
/**
* 查询缓存
*/
public function _query($sql, $system, $all = TRUE) {
$mysql = \Phpcmf\Service::M()->db;
if (isset($system['db']) && $system['db']) {
list($mysql) = \Frame\Model::_load_db_source($system['db']);
}
// 运算替换
if ($this->_return_sql && strpos($sql, $this->_select_rt_name) !== false) {
switch ($this->_return_sql) {
case 'count':
$sql = str_replace($this->_select_rt_name, 'count(*) as ct', $sql);
break;
case 'sum':
if (!$system['sum']) {
return '缺少参数sum,指定求和的字段名称';
}
$sql = str_replace($this->_select_rt_name, 'sum('.$system['sum'].') as ct', $sql);
break;
}
}
// 执行SQL
$t = microtime(TRUE);
$query = $mysql->query($sql);
$time = microtime(TRUE) - $t;
// 记录慢日志
if ($time > 1 && is_file(WRITEPATH.'database/sql.lock')) {
$file = WRITEPATH.'database/Sql/sql.txt';
$path = dirname($file);
if (!is_dir($path)) {
dr_mkdirs($path);
}
$size = filesize($file);
$json = json_encode([SYS_TIME, $sql, $time, $this->_view_file, $this->_list_tag, SITE_URL.SELF.'?'.http_build_query($_GET)]);
if ($size > 1024*1024*2) {
copy($file, $path.SYS_TIME.'.txt');
file_put_contents($file, $json.PHP_EOL, LOCK_EX);
} else {
file_put_contents($file, $json.PHP_EOL, FILE_APPEND | LOCK_EX);
}
}
$this->_sql = $sql;
$this->_sql_time = $time;
// 挂钩点 模板中的sql语句
\Phpcmf\Hooks::trigger('cms_view_sql', $sql, $time, $this->_view_file, $this->_list_tag);
if (!$query) {
return 'SQL查询解析不正确:'.$sql;
}
// 查询结果
$data = $all ? $query->getResultArray() : $query->getRowArray();
return $data;
}
// 获取当前执行后的sql语句
public function get_sql_query() {
return $this->_sql;
}
// 设置分页参数
public function set_page_config($config) {
$this->_page_config = $config;
}
/**
* 新分页
*/
public function _new_pagination($system, $pagesize, $total) {
return $this->_get_pagination($system['urlrule'], $pagesize, $total, $system['pagefile'], $system['firsturl']);
}
/**
* 分页
*/
public function _get_pagination($url, $pagesize, $total, $name = 'page', $first_url = '') {
$this->_page_used = 1;
$this->_page_config_file = '';
if ($name == 'admin') {
// 使用后台分页规则
$this->_page_config_file = CMSPATH.'Config/Apage.php';
} else {
// 这里要支持移动端分页条件
!$name && $name = 'page';
$file = 'page/'.($this->_is_mobile ? 'mobile' : 'pc').'/'.(dr_safe_filename($name)).'.php';
if (is_file(WEBPATH.'config/'.$file)) {
$this->_page_config_file = WEBPATH.'config/'.$file;
} elseif (is_file(CONFIGPATH.$file)) {
$this->_page_config_file = CONFIGPATH.$file;
} else {
exit('无法找到分页配置文件【'.$file.'】');
}
}
$config = require $this->_page_config_file;
if ($this->_page_config) {
$config = dr_array22array($config, $this->_page_config);
}
!$url && $url = '此标签没有设置urlrule参数';
$this->_page_urlrule = str_replace(['[page]', '%7Bpage%7D', '%5Bpage%5D', '%7bpage%7d', '%5bpage%5d'], '{page}', $url);
$config['base_url'] = $this->_page_urlrule;
$config['first_url'] = $first_url ? $first_url : '';
$config['per_page'] = $pagesize;
$config['page_name'] = $this->_page_value;
$config['total_rows'] = $total;
$config['use_page_numbers'] = TRUE;
$config['query_string_segment'] = 'page';
return \Phpcmf\Service::L('Page')->initialize($config)->create_links();
}
private function _get_where_in($str) {
if (!$str) {
return 0;
}
$arr = explode(',', $str);
foreach ($arr as $i => $t) {
$arr[$i] = intval($t);
}
return implode(',', $arr);
}
// 条件子句格式化
public function _get_where($where) {
if (!$where) {
return '';
}
$m = \Phpcmf\Service::M();
$string = '';
foreach ($where as $i => $t) {
// 过滤字段
if (isset($t['use']) && $t['use'] == 0 || !strlen($t['value'])) {
continue;
}
$join = $string ? ' AND' : '';
// 条件组装
switch ($t['adj']) {
case 'MAP':
// 地图
if ($t['value'] == '') {
$string .= $join." ".$t['name']." = ''";
} else {
list($a, $km) = explode('|', $t['value']);
list($lng, $lat) = explode(',', $a);
if ($lat && $lng) {
$this->pos_map = [
'lng' => $lng,
'lat' => $lat,
'km' => $km,
];
$squares = dr_square_point($lng, $lat, $km);
$table = !empty($t['table']) ? $t['table'] : '';
if ($table !== '') {
$lat_col = $m->quote_field($table, $t['name'].'_lat');
$lng_col = $m->quote_field($table, $t['name'].'_lng');
} else {
// 兼容旧 prefix(已加引的 `table`.
$lat_col = $m->quote_column(($t['prefix'] ?? '').$t['name'].'_lat');
$lng_col = $m->quote_column(($t['prefix'] ?? '').$t['name'].'_lng');
}
$string .= $join.' ('.$m->where_between($lat_col, $squares['right-bottom']['lat'], $squares['left-top']['lat'])
.' AND '.$m->where_between($lng_col, $squares['left-top']['lng'], $squares['right-bottom']['lng']).')';
} else {
$string .= $join." ".$t['name']." = '没有定位到您的坐标'";
}
}
break;
case 'JSON':
case 'NOTJSON':
if ($t['value'] == '') {
$string .= $join." ".$t['name']." = ''";
} else {
$or_and = strpos($t['value'], ',');
if ($or_and) {
$arr = explode(',', $t['value']);
} elseif (strpos($t['value'], '|')) {
$arr = explode('|', $t['value']);
} else {
$arr = [$t['value']];
}
$vals = [];
foreach ($arr as $value) {
if ($value) {
$vals[] = $m->where_json('', $t['name'], $m->db->escapeString(dr_safe_replace($value), true));
}
}
if ($t['adj'] == 'NOTJSON') {
$join .= ' NOT';
}
$string .= $vals ? $join.'('.$m->where_ne($t['name'], '').' AND ('.implode($or_and ? ' AND ' : ' OR ', $vals).'))' : '';
}
break;
case 'FIND':
case 'NOTFIND':
$vals = [];
if (dr_safe_replace($t['value'])) {
foreach (explode('|', $t['value']) as $v) {
if ($v !== '' && $v !== null) {
$vals[] = $m->where_find_in_set($t['name'], $v);
}
}
}
if ($t['adj'] == 'NOTFIND') {
$join .= ' NOT';
}
$string .= $vals ? $join.' ('.implode(' OR ', $vals).')' : '';
break;
case 'LIKE':
case 'NOTLIKE':
$vals = [];
if (dr_safe_replace($t['value'])) {
foreach (explode('|', $t['value']) as $value) {
if ($value) {
$ns = strpos($value, '%');
// % 不在开头 → 尾部模糊;否则首尾模糊
$side = ($ns !== false && $ns !== 0) ? 'right' : 'both';
$vals[] = $m->where_like($t['name'], trim($value, '%'), $side);
}
}
}
if ($t['adj'] == 'NOTLIKE') {
$join .= ' NOT';
}
$string .= $vals ? $join.' ('.implode(' OR ', $vals).')' : '';
break;
case 'IN':
$sql = $m->where_in_expr($t['name'], explode(',', dr_safe_replace($t['value'])));
$string .= $sql ? $join.' '.$sql : '';
break;
case 'NOTIN':
$sql = $m->where_in_expr($t['name'], explode(',', dr_safe_replace($t['value'])), true);
$string .= $sql ? $join.' '.$sql : '';
break;
case 'NOT':
$string .= $join.' '.$m->where_ne(
$t['name'],
dr_is_numeric($t['value']) ? $t['value'] : ($t['value'] == "''" ? '' : dr_safe_replace($t['value']))
);
break;
case 'BEWTEEN':
case 'BW':
case 'BETWEEN':
list($s, $e) = explode(',', $t['value']);
$string .= $join.' '.$m->where_between($t['name'], (int) $s, (int) $e);
break;
case 'GT':
$string .= $join.' '.$m->where_cmp($t['name'], '>', intval($t['value']));
break;
case 'EGT':
$string .= $join.' '.$m->where_cmp($t['name'], '>=', intval($t['value']));
break;
case 'LT':
$string .= $join.' '.$m->where_cmp($t['name'], '<', intval($t['value']));
break;
case 'ELT':
$string .= $join.' '.$m->where_cmp($t['name'], '<=', intval($t['value']));
break;
case 'SQL':
$string .= $join.' '.str_replace('@#', $m->dbprefix(), $t['value']);
break;
case 'DAY':
if (substr($t['value'], 0, 1) == 'E') {
$stime = strtotime('-'.intval(substr($t['value'], 1)).' day');
$stime = strtotime(date('Y-m-d', $stime).' 00:00:00');
$etime = strtotime(date('Y-m-d 23:59:59', $stime));
} elseif (strpos($t['value'], ',')) {
list($s, $e) = explode(',', $t['value']);
$stime = strtotime($s.' 00:00:00');
$etime = strtotime(($e ? $e : date('Y-m-d')).' 23:59:59');
} else {
$time = strtotime('-'.intval($t['value']).' day');
$stime = strtotime(date('Y-m-d', $time).' 00:00:00');
$etime = SYS_TIME;
}
if ($stime > $etime) {
$tp = $stime;
$stime = $etime;
$etime = $tp;
}
$string .= $join.' ('.$m->where_between($t['name'], $stime, $etime).')';
break;
case 'WEEK':
if (substr($t['value'], 0, 1) == 'E') {
$num = intval(substr($t['value'], 1));
if ($num) {
$stime = strtotime(date('Y-m-d', strtotime('-'.($num + 1).' monday')).' 00:00:00');
$etime = strtotime(date('Y-m-d 23:59:59', ($stime + (8 - (date('w') == 0 ? 8 : date('w'))) * 24 * 3600)));
} else {
$stime = strtotime(date('Y-m-d 00:00:00', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 24 * 3600)));
$etime = strtotime(date('Y-m-d 23:59:59', ($stime + (8 - (date('w') == 0 ? 8 : date('w'))) * 24 * 3600)));
}
} else {
$num = intval($t['value']);
if ($num) {
$stime = strtotime(date('Y-m-d', strtotime('-'.($num + 1).' monday')).' 00:00:00');
} else {
$stime = strtotime(date('Y-m-d 00:00:00', (time() - ((date('w') == 0 ? 7 : date('w')) - 1) * 24 * 3600)));
}
$etime = SYS_TIME;
}
if ($stime > $etime) {
$tp = $stime;
$stime = $etime;
$etime = $tp;
}
$string .= $join.' ('.$m->where_between($t['name'], $stime, $etime).')';
break;
case 'SEASON':
list($s, $y) = explode('-', $t['value']);
if (!$y) {
$y = date('Y');
}
if (!$s || !in_array($s, [1, 2, 3, 4])) {
$s = ceil(date('n') / 3);
}
$season = [
1 => ['-01-01 00:00:00', '-03-31 23:59:59'],
2 => ['-04-01 00:00:00', '-06-30 23:59:59'],
3 => ['-07-01 00:00:00', '-09-30 23:59:59'],
4 => ['-10-01 00:00:00', '-12-31 23:59:59'],
];
$stime = strtotime($y.$season[$s][0]);
$etime = strtotime($y.$season[$s][1]);
if ($stime > $etime) {
$tp = $stime;
$stime = $etime;
$etime = $tp;
}
$string .= $join.' ('.$m->where_between($t['name'], $stime, $etime).')';
break;
case 'MONTH':
if (substr($t['value'], 0, 1) == 'E') {
$stime = strtotime(date('Y-m-01 00:00:00').' - '.intval(substr($t['value'], 1)).' month');
$etime = strtotime(date('Y-m', $stime).'-1 +1 month -1 day 23:59:59');
} elseif (strpos($t['value'], ',')) {
list($s, $e) = explode(',', $t['value']);
if (!$e) {
$e = $s;
}
$stime = strtotime($s.'-01 00:00:00');
$etime = strtotime($e.' +1 month -1 day 23:59:59');
} else {
$time = strtotime('-'.intval($t['value']).' month');
$stime = strtotime(date('Y-m', $time).'-01 00:00:00');
$etime = SYS_TIME;
}
if ($stime > $etime) {
$tp = $stime;
$stime = $etime;
$etime = $tp;
}
$string .= $join.' ('.$m->where_between($t['name'], $stime, $etime).')';
break;
case 'YEAR':
if (strlen($t['value']) == 4) {
$stime = strtotime($t['value'].'-01-01 00:00:00');
$etime = strtotime($t['value'].'-12-31 23:59:59');
} elseif (substr($t['value'], 0, 1) == 'E') {
$stime = strtotime(date('Y', strtotime('-'.intval(substr($t['value'], 1)).' year')).'-01-01 00:00:00');
$etime = strtotime(date('Y', $stime).'-12-31 23:59:59');
} elseif (strpos($t['value'], ',')) {
list($s, $e) = explode(',', $t['value']);
$stime = strtotime(intval($s).'-01-01 00:00:00');
$etime = strtotime(intval($e ? $e : date('Y')).'-12-31 23:59:59');
} else {
$stime = strtotime(date('Y', strtotime('-'.intval($t['value']).' year')).'-01-01 00:00:00');
$etime = SYS_TIME;
}
if ($stime > $etime) {
$tp = $stime;
$stime = $etime;
$etime = $tp;
}
$string .= $join.' ('.$m->where_between($t['name'], $stime, $etime).')';
break;
case 'NOTNULL':
$string .= $join.' '.$m->where_ne($t['name'], '');
break;
case 'NULL':
$string .= $join." ({$t['name']}='' or {$t['name']} IS NULL)";
break;
default:
if ($t['name'] && (
(!empty($t['field']) && $t['field'] === 'thumb')
|| $m->bare_field($t['name']) === 'thumb'
)) {
$sql = $m->where_field_thumb('', $t['name'], $t['value']);
$string .= $sql ? $join.' '.$sql : '';
} elseif (!$t['name'] && $t['value']) {
$string .= $join.' '.$t['value'];
} else {
$string .= $join.(dr_is_numeric($t['value']) ? " {$t['name']} = ".intval($t['value']) : " {$t['name']} = '".($t['value'] == "''" ? '' : dr_safe_replace($t['value']))."'");
}
break;
}
}
return trim($string);
}
// 给条件字段加上表前缀
public function _set_where_field_prefix($where, $field, $prefix, $myfield = []) {
if (!$where) {
return [];
}
$m = \Phpcmf\Service::M();
$this->_list_where = $where;
foreach ($where as $i => $t) {
if (dr_in_array($t['name'], $field)) {
$fname = $t['name'];
$where[$i]['use'] = 1;
$where[$i]['field'] = $fname;
$where[$i]['table'] = $prefix;
$where[$i]['name'] = $m->quote_field($prefix, $fname);
if ($myfield && $t['value']) {
if ($myfield[$fname]['fieldtype'] == 'Linkages') {
// 联动多选
$arr = explode('|', $t['value']);
$link_where = [];
foreach ($arr as $value) {
$data = dr_linkage($myfield[$fname]['setting']['option']['linkage'], $value);
if ($data) {
if ($data['child']) {
$link_where = dr_array2array($link_where, explode(',', $data['childids']));
} else {
$link_where[] = intval($data['ii']);
}
}
}
$where[$i]['adj'] = 'JSON';
if ($link_where) {
$where[$i]['value'] = implode('|', array_unique($link_where));
} else {
// 没有找到就当做普通数据库查询
$where[$i]['value'] = $t['value'];
}
} elseif (isset($myfield[$fname]['fieldtype']) && in_array($myfield[$fname]['fieldtype'], ['Members', 'Related'])) {
// 关联字段
if (!$where[$i]['adj']) {
$where[$i]['adj'] = 'FIND';
$where[$i]['value'] = intval($t['value']);
}
} elseif ($myfield[$fname]['fieldtype'] == 'Linkage') {
// 联动菜单
$arr = explode('|', $t['value']);
$link_where = [];
$col = $where[$i]['name'];
foreach ($arr as $value) {
$data = dr_linkage($myfield[$fname]['setting']['option']['linkage'], $value);
if ($data) {
if ($data['child']) {
$link_where[] = $col.' IN ('.$data['childids'].')';
} else {
$link_where[] = $col.' = '.intval($data['ii']);
}
}
}
if ($link_where) {
$where[$i]['adj'] = 'SQL';
$where[$i]['value'] = '('.implode(' OR ', array_unique($link_where)).')';
} else {
// 没有找到就当做普通数据库查询
$where[$i]['value'] = $t['value'];
}
}
}
} elseif (!$t['name'] && $t['value']) {
// 标示只有where的条件查询
$where[$i]['use'] = 1;
} elseif ($t['adj'] == 'MAP' && dr_in_array($t['name'].'_lat', $field) && dr_in_array($t['name'].'_lng', $field)) {
$where[$i]['use'] = 1;
$where[$i]['table'] = $prefix;
$where[$i]['prefix'] = $m->quote_table($prefix).'.';
} else {
if (!$t['use'] && !in_array($t['name'], ['where'])) {
$this->_list_error[] = '在['.$prefix.']表中字段['.$t['name'].']不存在(可用字段:'.implode('、', $field).'';
}
$where[$i]['use'] = $t['use'] ? 1 : 0;
}
}
return $where;
}
// 给显示字段加上表前缀
public function _set_select_field_prefix($select, $field, $prefix) {
if ($select) {
$m = \Phpcmf\Service::M();
$array = explode(',', $select);
foreach ($array as $i => $t) {
$field_prefix = '';
if (strpos($t, 'DISTINCT_') === 0) {
$t = str_replace('DISTINCT_', '', $t);
$field_prefix = 'DISTINCT ';
}
if (dr_in_array($t, $field)) {
$array[$i] = $field_prefix.$m->quote_field($prefix, $t);
} elseif (strpos($t, '.') !== false && !$m->has_id_quotes($t)) {
list($a, $b) = explode('.', $t);
if (($prefix == $a || substr($prefix, strlen($m->dbprefix())) == $a)) {
if (strpos($b, ':') !== false) {
// 存在别名
list($b, $cname) = explode(':', $b);
if (dr_in_array($b, $field)) {
$array[$i] = $field_prefix.$m->quote_field($prefix, $b).' as '.$m->quote_id($cname);
}
} else {
if (dr_in_array($b, $field)) {
$array[$i] = $field_prefix.$m->quote_field($prefix, $b);
}
}
}
}
}
return implode(',', $array);
}
return $select;
}
// 获取分页页数
private function _get_page_id($page) {
if (dr_is_numeric($page)) {
return max(1, (int)$_GET['page']);
} else {
return max(1, isset($_GET[$page]) ? (int)$_GET[$page] : 0);
}
}
// 格式化查询参数
private function _get_select_field($field) {
if ($field != '*') {
$my = [];
$array = explode(',', $field);
foreach ($array as $t) {
// 已加表前缀/引号的字段才保留
if (\Phpcmf\Service::M()->is_column_expr($t)) {
$my[] = $t;
continue;
}
}
if (!$my) {
$field = '*';
} else {
$field = implode(',', $my);
}
}
// 定位范围搜索
if ($this->pos_order) {
if ($this->pos_map && $this->pos_map['lat'] && $this->pos_map['lng']) {
if (version_compare(\Phpcmf\Service::M()->db->getVersion(), '5.7.0') < 0) {
$field .= ',ROUND(6378.138*2*ASIN(SQRT(POW(SIN((' . $this->pos_map['lat'] . '*PI()/180-' . $this->pos_order . '_lat*PI()/180)/2),2)+COS(' . $this->pos_map['lat'] . '*PI()/180)*COS(' . $this->pos_order . '_lat*PI()/180)*POW(SIN((' . $this->pos_map['lng'] . '*PI()/180-' . $this->pos_order . '_lng*PI()/180)/2),2)))*1000) AS ' . $this->pos_order . '_map';
} else {
$field.= ',ST_Distance_Sphere(POINT('.$this->pos_order.'_lng, '.$this->pos_order.'_lat), POINT('.$this->pos_map['lng'].', '.$this->pos_map['lat'].')) AS '.$this->pos_order.'_map';
}
} else {
$field.= '没有定位到您的坐标';
}
}
return $field;
}
// join 联合查询表
public function _join_table($main, $system, $where, $_order, $sql_from) {
$table = \Phpcmf\Service::M()->dbprefix($system['join']); // 关联表
$tableinfo = \Phpcmf\Service::L('cache')->get_data('table-join-'.$system['join']);
if (!$tableinfo) {
$tableinfo = \Phpcmf\Service::M('Table')->get_field($system['join']);
if (!$tableinfo) {
return dr_return_data(0, '关联数据表('.$system['join'].')结构不存在');
}
\Phpcmf\Service::L('cache')->set_data('table-join-'.$system['join'], $tableinfo, 36000);
}
list($a, $b) = explode(',', $system['on']);
$b = $b ? $b : $a;
$where = $this->_set_where_field_prefix($where, $tableinfo, $table); // 给条件字段加上表前缀
$system['field'] = $this->_set_select_field_prefix($system['field'], $tableinfo, $table); // 给显示字段加上表前缀
$_order[$table] = $tableinfo;
$sql_from.= ' LEFT JOIN '.\Phpcmf\Service::M()->quote_table($table)
.' ON '.\Phpcmf\Service::M()->quote_field($main, $a)
.'='.\Phpcmf\Service::M()->quote_field($table, $b);
return dr_return_data(1, 'ok', [$system, $where, $_order, $sql_from]);
}
// 给排序字段加上多表前缀
public function _set_orders_field_prefix($order, $fields) {
if (!$order) {
return NULL;
} elseif (strtoupper($order) == 'FIXNULL') {
// NULL排序
return 'NULL';
} elseif (in_array(strtoupper($order), ['RAND()', 'RAND'])) {
// 随机排序
return 'RAND()';
}
$order = urldecode($order);
if (\Phpcmf\Service::M()->has_id_quotes($order)) {
return $order;
}
// 字段排序
$m = \Phpcmf\Service::M();
$my = [];
$array = explode(',', $order);
foreach ($array as $i => $t) {
$a = explode('_', $t);
$b = end($a);
if (in_array(strtolower($b), ['desc', 'asc', 'instr', 'field'])) {
$a = str_replace('_'.$b, '', $t);
} else {
$a = $t;
$b = '';
}
$b = strtoupper($b);
foreach ($fields as $prefix => $field) {
if (is_array($field)) {
if (dr_in_array($a, $field)) {
$col = $m->quote_field($prefix, $a);
if ($b == 'INSTR') {
if (isset($this->_list_where['IN_' . $a]) && $this->_list_where['IN_' . $a]) {
$my[$i] = 'instr("'.$this->_list_where['IN_' . $a]['value'].'",'.$col.')';
} else {
$this->_list_error[] = '无法找到字段' . $a . '的IN通配符参数,order参数将会无效';
}
} elseif ($b == 'FIELD') {
if (isset($this->_list_where['IN_'.$a]) && $this->_list_where['IN_'.$a]) {
$my[$i] = 'FIELD('.$col.', '.$this->_list_where['IN_'.$a]['value'].')';
} else {
$this->_list_error[] = '无法找到字段'.$a.'的IN通配符参数,order参数将会无效';
}
} else {
$my[$i] = $col.' '.($b ? $b : 'DESC');
}
} elseif (dr_in_array($a.'_lat', $field) && dr_in_array($a.'_lng', $field)) {
if ($this->pos_map) {
$my[$i] = $m->quote_id($a.'_map').' ASC';
$this->pos_order = $a;
} else {
$this->_list_error[] = '没有定位到您的坐标,order参数将会无效';
}
}
}
}
}
if ($my) {
return implode(',', $my);
}
return NULL;
}
// 给排序字段加上表前缀
public function _set_order_field_prefix($order, $field, $prefix) {
return $this->_set_orders_field_prefix($order, [$prefix => $field]);
}
// list 返回
public function _return($return, $data = [], $sql = '', $total = 0, $pages = '', $pagesize = 0, $is_cache = 0) {
$this->pos_map = $this->pos_order = null;
$total = isset($total) && $total ? $total : dr_count($data);
$page = $this->_get_page_id($this->_page_value);
$nums = $pagesize ? ceil($total/$pagesize) : 0;
if (CI_DEBUG) {
// 显示debug数据
$debug = '<pre style="background-color: #f5f5f5; border: 1px solid #ccc;padding:10px; overflow: auto; text-align: left">';
if ($this->_list_tag) {
$debug.= '<p>标签解析:'.str_replace('{list action=', '{', $this->_list_tag).'</p>';
}
if ($this->_list_error) {
$debug.= '<p>错误提示:'.implode('、', $this->_list_error).'</p>';
}
if (strpos($this->_list_tag, 'return=') !== false) {
$arr = explode('return=', $this->_list_tag);
if (strpos($arr[1], ' ') !== false) {
$debug.= '<p>错误提示:return参数必须放在最后,且后面不能带有空格符号</p>';
}
}
if ($this->_is_list_search) {
if (!$this->_options['is_search_page']) {
$debug.= '<p>使用范围:search标签只能用于搜索页面,当前页面不是搜索页面,可能会无效</p>';
}
$debug.= '<p>搜索解析:'.$this->_options['search_sql'].'</p>';
} elseif ($this->_options['is_search_page'] && (strpos($this->_list_tag, 'page=1') || $this->_page_used)) {
$debug.= '<p>标签提醒:当前页面是搜索页面,只有search标签才适用于搜索页面,可能会引起本标签分页无效</p>';
}
if ($sql) {
// 运算替换
if ($this->_return_sql && strpos($sql, $this->_select_rt_name) !== false) {
switch ($this->_return_sql) {
case 'count':
$sql = str_replace($this->_select_rt_name, 'count(*) as ct', $sql);
break;
case 'sum':
$sql = str_replace($this->_select_rt_name, 'sum('.$this->_sum_field.') as ct', $sql);
break;
}
}
$debug.= '<p>查询解析: '.$sql.'</p>';
$debug.= '<p>查询耗时: '.$this->_sql_time.'s</p>';
}
$debug.= '<p>当前路由:'.\Phpcmf\Service::L('router')->uri().'</p>';
CMSURI && $debug.= '<p>当前地址:'.CMSURI.'</p>';
$debug.= '<p>动态地址:'.SELF.'?'.http_build_query($_GET).'</p>';
if ($data && !is_array($data)) {
$debug.= '<p>'.$data.'</p>';
$data = [];
}
$debug.= '<p>变量前缀:'.($return ? $return : 't').'</p>';
} else {
$debug = 'debug诊断信息需要在index.php文件中开启开发者模式才能查看';
}
// 开始返回
if ($this->_return_sql) {
if (CI_DEBUG) {
$debug .= '<p>运算数量:' . ($data[0]['ct']) . '</p>';
$debug .= '<p>运算变量:' . ($return ? '{$' . $return . '_' . $this->_return_sql . '} 不输出,需要手动调用变量' : '自动输出') . '</p>';
$debug .= '</pre>';
}
return [
'debug_'.$this->_return_sql => $debug,
'return_'.$this->_return_sql => $data,
];
} else {
if ($data) {
$rt2 = \Phpcmf\Hooks::trigger_callback('view_return', $data, $this->_list_tag, $this->_list_param);
if ($rt2 && isset($rt2['code']) && $rt2['code']) {
$data = $rt2['data'];
}
}
if (CI_DEBUG) {
$debug.= '<p>总记录数:'.$total.'</p>';
if ($this->_page_used) {
$debug.= '<p>分页功能:已开启</p>';
$debug.= '<p>当前页码:'.$page.'</p>';
$debug.= '<p>总页数量:'.$nums. ($nums == 1 ? '(数据量未达到分页数据,因此只有一页)' : '').'</p>';
$debug.= '<p>每页数量:'.$pagesize.'</p>';
$debug.= '<p>分页地址:'.$this->_page_urlrule.'</p>';
$debug.= '<p>分页配置:'.$this->page_config_file.'</p>';
} else {
$debug.= '<p>分页功能:未开启</p>';
}
isset($data[0]) && is_array($data[0]) && $debug.= '<p>可用字段:'.implode('、', array_keys($data[0])).'</p>';
$debug.= '</pre>';
}
// 返回数据格式
if ($return) {
return [
'nums_'.$return => $nums,
'page_'.$return => $page,
'pages_'.$return => $pages,
'debug_'.$return => $debug,
'total_'.$return => $total,
'return_'.$return => $data,
'pagerule_'.$return => $this->_page_urlrule,
'pagesize_'.$return => $pagesize,
];
} else {
return [
'nums' => $nums,
'debug' => $debug,
'page' => $page,
'total' => $total,
'pages' => $pages,
'return' => $data,
'pagerule' => $this->_page_urlrule,
'pagesize' => $pagesize,
];
}
}
}
// 替换变量
public function _get_var($param) {
$array = explode('.', $param);
if (!$array) {
return '';
}
$string = '';
foreach ($array as $var) {
$var = dr_safe_replace($var);
$string.= '[';
if (strpos($var, '$') === 0) {
$string.= preg_replace('/\[(.+)\]/U', '[\'\\1\']', $var);
} elseif (preg_match('/[A-Z_]+/', $var)) {
$string.= ''.$var.'';
} else {
$string.= '\''.$var.'\'';
}
$string.= ']';
}
return $string;
}
// 公共变量参数
public function _cache_var($name, $siteid = 0) {
$data = null;
$name = strtolower($name);
switch ($name) {
case 'member':
$data = \Phpcmf\Service::C()->member_cache;
break;
case 'member_group':
$data = \Phpcmf\Service::C()->member_cache['group'];
break;
case 'site_info':
$data = \Phpcmf\Service::C()->site_info;
break;
case 'urlrule':
$data = \Phpcmf\Service::L('cache')->get('urlrule');
break;
case 'module-content':
$data = \Phpcmf\Service::L('cache')->get('module-'.$siteid.'-content');
break;
case 'category':
$data = \Phpcmf\Service::L('cache')->get('module-'.$siteid.'-'.MOD_DIR, 'category');
break;
case 'page':
$data = \Phpcmf\Service::L('cache')->get('page-'.$siteid, 'data');
break;
default:
if ($siteid) {
$data = \Phpcmf\Service::L('cache')->get($name.'-'.$siteid);
} else {
$data = \Phpcmf\Service::L('cache')->get($name);
}
break;
}
return $data;
}
// 模板中的全部变量
public function get_data() {
return $this->_options;
}
// 模板中的全部变量
public function getData() {
return [];
}
// 模板中的文件数
public function get_view_files() {
return $this->_view_files;
}
// 模板中的文件引用提示
public function add_load_tips($name, $msg) {
if ($name) {
$this->_load_file_tips[$name] = $msg;
} else {
$this->_load_file_tips[] = $msg;
}
}
// 模板中的文件引用提示
public function get_load_tips() {
if (!$this->_load_file_tips) {
return [
[
'name' => $this->_filename,
'tips' => ''
]
];
}
$rt = [];
foreach ($this->_load_file_tips as $i => $t) {
$rt[] = [
'name' => $i,
'tips' => $t,
];
}
return $rt;
}
// 模板中的运行时间
public function get_view_time() {
return $this->_view_time;
}
/**
* 主要用于调试工具栏
*
* @return array
*/
public function getPerformanceData(): array
{
return $this->performanceData;
}
/**
* 记录模板的性能数据
*
* @param float $start
* @param float $end
* @param string $view
*/
protected function logPerformance(float $start, float $end, string $view)
{
if (!CI_DEBUG)
return;
$this->performanceData[] = [
'start' => $start,
'end' => $end,
'view' => $view
];
}
// 错误提示
public function show_error($msg, $file = '', $fixfile = '') {
if (CI_DEBUG || defined('SC_HTML_FILE')) {
// 开发者模式下,静态生成模式下,显示详细错误
if ($file) {
$msg.= ''.$file.'';
if ($fixfile) {
$msg.= '<br>你可以将PC模板('.$fixfile.')手动复制过来作为本模板';
}
}
log_message('error', $this->_options['my_web_url'].''.$msg);
if (defined('SC_HTML_FILE')) {
if (isset($_GET['iframe'])) {
\Phpcmf\Service::C()->_html_msg(0, $this->_options['my_web_url'].''.$msg);
} else {
\Phpcmf\Service::C()->_json(0, $this->_options['my_web_url'].''.$msg);
}
}
}
dr_show_error($msg);
}
}