【phpcms-v9】index.php文件分析-前台首页模板文件的解析过程分析

来源:互联网 发布:editplus是什么软件 编辑:程序博客网 时间:2024/05/19 22:02

第一步:前台首页默认执行的是:index.php?m=content&c=index&a=init

//首页public function init() {if(isset($_GET['siteid'])) {$siteid = intval($_GET['siteid']);//当前站点ID} else {$siteid = 1;//当前站点ID}$siteid = $GLOBALS['siteid'] = max($siteid,1);define('SITEID', $siteid);$_userid = $this->_userid;$_username = $this->_username;$_groupid = $this->_groupid;$SEO = seo($siteid);//查看第二步,获取当前站点当前栏目下生成的SEO信息$sitelist  = getcache('sitelist','commons');        //缓存后台设置的所有站点配置信息$default_style = $sitelist[$siteid]['default_style'];//当前站点默认模板风格配置$CATEGORYS = getcache('category_content_'.$siteid,'commons');//当前站点所有栏目详细配置信息include template('content','index',$default_style);//查看第三步:模版调用}

第二步:获取SEO信息:phpcms/libs/functions/global.func.php

/** * 生成SEO * @param $siteid       站点ID * @param $catid        栏目ID * @param $title        标题 * @param $description  描述 * @param $keyword      关键词 */function seo($siteid, $catid = '', $title = '', $description = '', $keyword = '') {if (!empty($title))$title = strip_tags($title);//过滤titleif (!empty($description)) $description = strip_tags($description);  //过滤descriptionif (!empty($keyword)) $keyword = str_replace(' ', ',', strip_tags($keyword));//过滤keyword$sites = getcache('sitelist', 'commons');//所有站点详细配置信息$site = $sites[$siteid]; //当前站点详细配置信息$cat = array();if (!empty($catid)) {//栏目ID不为空$siteids = getcache('category_content','commons');//所有栏目对应的站点ID缓存文件,格式:栏目ID=>站点ID$siteid = $siteids[$catid];  //当前栏目对应的站点ID$categorys = getcache('category_content_'.$siteid,'commons');//当前站点下所有栏目的详细配置信息$cat = $categorys[$catid];//当前站点下当前栏目的详细配置信息  $cat['setting'] = string2array($cat['setting']);//当前站点当前栏目详细配置信息的setting设置信息,转化为数组}//站点title$seo['site_title'] =isset($site['site_title']) && !empty($site['site_title']) ? $site['site_title'] : $site['name'];//关键词$seo['keyword'] = !empty($keyword) ? $keyword : $site['keywords'];//描述$seo['description'] = isset($description) && !empty($description) ? $description : (isset($cat['setting']['meta_description']) && !empty($cat['setting']['meta_description']) ? $cat['setting']['meta_description'] : (isset($site['description']) && !empty($site['description']) ? $site['description'] : ''));//标题$seo['title'] =  (isset($title) && !empty($title) ? $title.' - ' : '').(isset($cat['setting']['meta_title']) && !empty($cat['setting']['meta_title']) ? $cat['setting']['meta_title'].' - ' : (isset($cat['catname']) && !empty($cat['catname']) ? $cat['catname'].' - ' : ''));foreach ($seo as $k=>$v) {$seo[$k] = str_replace(array("\n","\r"),'', $v);//将seo信息中\n和\r替换为空}return $seo;//返回seo数组信息}

第三步:模板调用:phpcms/libs/functions/global.func.php

/** * 模板调用 * * @param $module * @param $template * @param $istag * @return unknown_type */function template($module = 'content', $template = 'index', $style = '') {if(strpos($module, 'plugin/')!== false) { //一般情况下不会执行if里面的代码$plugin = str_replace('plugin/', '', $module);return p_template($plugin, $template,$style);}$module = str_replace('/', DIRECTORY_SEPARATOR, $module);if(!empty($style) && preg_match('/([a-z0-9\-_]+)/is',$style)) {//如果模板风格不为空} elseif (empty($style) && !defined('STYLE')) {   //如果模板风格为空if(defined('SITEID')) {   //是否定义了SITEID常量$siteid = SITEID;} else {$siteid = param::get_cookie('siteid');}if (!$siteid) $siteid = 1;$sitelist = getcache('sitelist','commons');//获取所有站点的详细配置信息if(!empty($siteid)) {$style = $sitelist[$siteid]['default_style']; //获取当前站点的默认模板风格}} elseif (empty($style) && defined('STYLE')) {$style = STYLE;} else {$style = 'default';}if(!$style) $style = 'default';$template_cache = pc_base::load_sys_class('template_cache');//模板解析类,路径:phpcms/libs/classes/template_cache.class.php//编译文件缓存路径:根目录/caches/caches_template/default/content/index.php$compiledtplfile = PHPCMS_PATH.'caches'.DIRECTORY_SEPARATOR.'caches_template'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.php';//路径:phpcms/templates/dafault/content/index.html ,如:首页模板文件if(file_exists(PC_PATH.'templates'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html')) {//如果编译文件不存在或者说模板文件的创建时间大于编译文件的生成时间,则重新编译if(!file_exists($compiledtplfile) || (@filemtime(PC_PATH.'templates'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html') > @filemtime($compiledtplfile))) {$template_cache->template_compile($module, $template, $style);//查看第四步:适用模板风格不是default的情况}} else {//编译文件缓存路径:根目录/caches/caches_template/default/content/index.php$compiledtplfile = PHPCMS_PATH.'caches'.DIRECTORY_SEPARATOR.'caches_template'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.php';//如果编译文件不存在或者说前台公共的模板文件存在,并且前台公共模板文件的创建时间大于编译文件的生成时间if(!file_exists($compiledtplfile) || (file_exists(PC_PATH.'templates'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html') && filemtime(PC_PATH.'templates'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html') > filemtime($compiledtplfile))) {//重新编译$template_cache->template_compile($module, $template, 'default');//查看第四步:适用于模板风格为default的情况} elseif (!file_exists(PC_PATH.'templates'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html')) {//如果前台公共的模板文件不存在的话,则提示模板不存在showmessage('Template does not exist.'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html');}}//返回编译文件return $compiledtplfile;}

第四步:模板解析:phpcms/libs/classes/template_cache.class.php

        /** * 编译模板 * * @param $module模块名称 * @param $template模板文件名 * @param $istag是否为标签模板 * @return unknown */public function template_compile($module, $template, $style = 'default') {if(strpos($module, '/')=== false) {//如果"/"不存在//路径:phpcms/templates/default/content/index.html ,如:首页公共模板文件$tplfile = $_tpl = PC_PATH.'templates'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html';} elseif (strpos($module, 'yp/') !== false) {$module = str_replace('/', DIRECTORY_SEPARATOR, $module);$tplfile = $_tpl = PC_PATH.'templates'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html';} else {$plugin = str_replace('plugin/', '', $module);$module = str_replace('/', DIRECTORY_SEPARATOR, $module);$tplfile = $_tpl = PC_PATH.'plugin'.DIRECTORY_SEPARATOR.$plugin.DIRECTORY_SEPARATOR.'templates'.DIRECTORY_SEPARATOR.$template.'.html';}if ($style != 'default' && !file_exists ( $tplfile )) {$style = 'default';$tplfile = PC_PATH.'templates'.DIRECTORY_SEPARATOR.'default'.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.'.html';}if (! file_exists ( $tplfile )) {//如果公共模板文件不存在,则提示模板文件不存在,如:/templates/default/content/index.html is not exists!showmessage ( "templates".DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.$template.".html is not exists!" );}//获取公共模板文件中的内容$content = @file_get_contents ( $tplfile );//要生成的编译文件所在目录$filepath = CACHE_PATH.'caches_template'.DIRECTORY_SEPARATOR.$style.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR;    if(!is_dir($filepath)) {    //如果目录不存在,则层级创建所有目录mkdir($filepath, 0777, true);    }    //编译文件的全路径$compiledtplfile = $filepath.$template.'.php';//解析公共模板文件中的内容及标签,并返回解析后的内容$content = $this->template_parse($content);//查看第五步//将解析后的公共模板文件内容写入到要生成的编译文件中$strlen = file_put_contents ( $compiledtplfile, $content );//给生成的编译文件设置权限chmod ( $compiledtplfile, 0777 );return $strlen;//返回写入编译文件的字节数}

第五步:模板解析:phpcms/libs/classes/template_cache.class.php

        /** * 解析模板 * * @param $str模板内容 * @return ture */public function template_parse($str) {$str = preg_replace ( "/\{template\s+(.+)\}/", "<?php include template(\\1); ?>", $str );$str = preg_replace ( "/\{include\s+(.+)\}/", "<?php include \\1; ?>", $str );$str = preg_replace ( "/\{php\s+(.+)\}/", "<?php \\1?>", $str );$str = preg_replace ( "/\{if\s+(.+?)\}/", "<?php if(\\1) { ?>", $str );$str = preg_replace ( "/\{else\}/", "<?php } else { ?>", $str );$str = preg_replace ( "/\{elseif\s+(.+?)\}/", "<?php } elseif (\\1) { ?>", $str );$str = preg_replace ( "/\{\/if\}/", "<?php } ?>", $str );//for 循环$str = preg_replace("/\{for\s+(.+?)\}/","<?php for(\\1) { ?>",$str);$str = preg_replace("/\{\/for\}/","<?php } ?>",$str);//++ --$str = preg_replace("/\{\+\+(.+?)\}/","<?php ++\\1; ?>",$str);$str = preg_replace("/\{\-\-(.+?)\}/","<?php ++\\1; ?>",$str);$str = preg_replace("/\{(.+?)\+\+\}/","<?php \\1++; ?>",$str);$str = preg_replace("/\{(.+?)\-\-\}/","<?php \\1--; ?>",$str);$str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\}/", "<?php \$n=1;if(is_array(\\1)) foreach(\\1 AS \\2) { ?>", $str );$str = preg_replace ( "/\{loop\s+(\S+)\s+(\S+)\s+(\S+)\}/", "<?php \$n=1; if(is_array(\\1)) foreach(\\1 AS \\2 => \\3) { ?>", $str );$str = preg_replace ( "/\{\/loop\}/", "<?php \$n++;}unset(\$n); ?>", $str );$str = preg_replace ( "/\{([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str );$str = preg_replace ( "/\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff:]*\(([^{}]*)\))\}/", "<?php echo \\1;?>", $str );$str = preg_replace ( "/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/", "<?php echo \\1;?>", $str );$str = preg_replace("/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\}/es", "\$this->addquote('<?php echo \\1;?>')",$str);$str = preg_replace ( "/\{([A-Z_\x7f-\xff][A-Z0-9_\x7f-\xff]*)\}/s", "<?php echo \\1;?>", $str );$str = preg_replace("/\{pc:(\w+)\s+([^}]+)\}/ie", "self::pc_tag('$1','$2', '$0')", $str);//查看第六步:解析pc标签的开始标签$str = preg_replace("/\{\/pc\}/ie", "self::end_pc_tag()", $str);//查看第六步:解析pc标签的结束标签$str = "<?php defined('IN_PHPCMS') or exit('No permission resources.'); ?>" . $str;return $str;}

第六步:pc标签的解析:

/** * 解析PC标签 * @param string $op 操作方式 * @param string $data 参数 * @param string $html 匹配到的所有的HTML代码 */public static function pc_tag($op, $data, $html) {preg_match_all("/([a-z]+)\=[\"]?([^\"]+)[\"]?/i", stripslashes($data), $matches, PREG_SET_ORDER);$arr = array('action','num','cache','page', 'pagesize', 'urlrule', 'return', 'start');$tools = array('json', 'xml', 'block', 'get');$datas = array();$tag_id = md5(stripslashes($html));//可视化条件$str_datas = 'op='.$op.'&tag_md5='.$tag_id;foreach ($matches as $v) {$str_datas .= $str_datas ? "&$v[1]=".($op == 'block' && strpos($v[2], '$') === 0 ? $v[2] : urlencode($v[2])) : "$v[1]=".(strpos($v[2], '$') === 0 ? $v[2] : urlencode($v[2]));if(in_array($v[1], $arr)) {$$v[1] = $v[2];//如果pc标签中参数在默认参数数组$arr中存在,则将参数转换为变量,如:$page=value等continue;}$datas[$v[1]] = $v[2];//如果pc标签中参数不在默认参数数组$arr中存在,则直接将其放置到$datas[参数名]=value中}$str = '';$num = isset($num) && intval($num) ? intval($num) : 20;$cache = isset($cache) && intval($cache) ? intval($cache) : 0;$return = isset($return) && trim($return) ? trim($return) : 'data';if (!isset($urlrule)) $urlrule = '';if (!empty($cache) && !isset($page)) {$str .= '$tag_cache_name = md5(implode(\'&\','.self::arr_to_html($datas).').\''.$tag_id.'\');if(!$'.$return.' = tpl_cache($tag_cache_name,'.$cache.')){';}if (in_array($op,$tools)) {//pc标签分两大类:工具类和模块类                工具类执行如下代码switch ($op) {case 'json':if (isset($datas['url']) && !empty($datas['url'])) {$str .= '$json = @file_get_contents(\''.$datas['url'].'\');';$str .= '$'.$return.' = json_decode($json, true);';}break;case 'xml':$str .= '$xml = pc_base::load_sys_class(\'xml\');';$str .= '$xml_data = @file_get_contents(\''.$datas['url'].'\');';$str .= '$'.$return.' = $xml->xml_unserialize($xml_data);';break;case 'get':$str .= 'pc_base::load_sys_class("get_model", "model", 0);';if ($datas['dbsource']) {$dbsource = getcache('dbsource', 'commons');if (isset($dbsource[$datas['dbsource']])) {$str .= '$get_db = new get_model('.var_export($dbsource,true).', \''.$datas['dbsource'].'\');';} else {return false;}} else {$str .= '$get_db = new get_model();';}$num = isset($num) && intval($num) > 0 ? intval($num) : 20;if (isset($start) && intval($start)) {$limit = intval($start).','.$num;} else {$limit = $num;}if (isset($page)) {$str .= '$pagesize = '.$num.';';$str .= '$page = intval('.$page.') ? intval('.$page.') : 1;if($page<=0){$page=1;}';$str .= '$offset = ($page - 1) * $pagesize;';$limit = '$offset,$pagesize';if ($sql = preg_replace('/select([^from].*)from/i', "SELECT COUNT(*) as count FROM ", $datas['sql'])) {$str .= '$r = $get_db->sql_query("'.$sql.'");$s = $get_db->fetch_next();$pages=pages($s[\'count\'], $page, $pagesize, $urlrule);';}}$str .= '$r = $get_db->sql_query("'.$datas['sql'].' LIMIT '.$limit.'");while(($s = $get_db->fetch_next()) != false) {$a[] = $s;}$'.$return.' = $a;unset($a);';break;case 'block':$str .= '$block_tag = pc_base::load_app_class(\'block_tag\', \'block\');';$str .= 'echo $block_tag->pc_tag('.self::arr_to_html($datas).');';break;}} else {//pc标签分两大类:工具类和模块类                模块类执行如下代码if (!isset($action) || empty($action)) return false;//content模块:phpcms/modules/content/classes/content_tag.class.phpif (module_exists($op) && file_exists(PC_PATH.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.$op.DIRECTORY_SEPARATOR.'classes'.DIRECTORY_SEPARATOR.$op.'_tag.class.php')) {//content_tag.class.php               检查content_tag类中是否存在的某方法$str .= '$'.$op.'_tag = pc_base::load_app_class("'.$op.'_tag", "'.$op.'");if (method_exists($'.$op.'_tag, \''.$action.'\')) {';if (isset($start) && intval($start)) {$datas['limit'] = intval($start).','.$num;//如:limit 0 , 10} else {$datas['limit'] = $num;//如:limit 10}if (isset($page)) {//分页参数$str .= '$pagesize = '.$num.';';//每页显示数据量$str .= '$page = intval('.$page.') ? intval('.$page.') : 1;if($page<=0){$page=1;}';//当前页码$str .= '$offset = ($page - 1) * $pagesize;';//要查询数据的开始位置$datas['limit'] = '$offset.",".$pagesize';$datas['action'] = $action;//方法,如,content_tag.class.php中的lists方法$str .= '$'.$op.'_total = $'.$op.'_tag->count('.self::arr_to_html($datas).');';//分页方法$str .= '$pages = pages($'.$op.'_total, $page, $pagesize, $urlrule);';}$str .= '$'.$return.' = $'.$op.'_tag->'.$action.'('.self::arr_to_html($datas).');';//查看第七步:content_tag.class.php中方法$str .= '}';} }if (!empty($cache) && !isset($page)) {$str .= 'if(!empty($'.$return.')){setcache($tag_cache_name, $'.$return.', \'tpl_data\');}';$str .= '}';}/** * 解析结果大概如下所示: <?php if(defined('IN_ADMIN')  && !defined('HTML')) {echo "<div class=\"admin_piao\" pc_action=\"content\" data=\"op=content&tag_md5=2d4b9e3c7c2cc4bd0cec8b1fac9ae764&action=position&posid=12&thumb=1&order=listorder+DESC&num=10\"><a href=\"javascript:void(0)\" class=\"admin_piao_edit\">编辑</a>"; } $content_tag = pc_base::load_app_class("content_tag", "content"); if (method_exists($content_tag, 'position')) {$data = $content_tag->position(array('posid'=>'12','thumb'=>'1','order'=>'listorder DESC','limit'=>'10',)); }?> */return "<"."?php if(defined('IN_ADMIN')  && !defined('HTML')) {echo \"<div class=\\\"admin_piao\\\" pc_action=\\\"".$op."\\\" data=\\\"".$str_datas."\\\"><a href=\\\"javascript:void(0)\\\" class=\\\"admin_piao_edit\\\">".($op=='block' ? L('block_add') : L('edit'))."</a>\";}".$str."?".">";}

第七步:pc标签类,路径:phpcms/modules/content/classes/content_tag.class.php

        private $db;public function __construct() {$this->db = pc_base::load_model('content_model');//查看第八步:数据模型,对应数据表news 和 news_data$this->position = pc_base::load_model('position_data_model');//数据模型}/** * 初始化模型 * @param $catid */public function set_modelid($catid) {$siteids = getcache('category_content','commons');//获取所有栏目所属的站点idif(!$siteids[$catid]) return false;//不存在此栏目,返回false$siteid = $siteids[$catid];//当前栏目所属站点id$this->category = getcache('category_content_'.$siteid,'commons');//获取当前站点id下所有栏目的配置信息if($this->category[$catid]['type']!=0) return false;//如果不为内部栏目,返回false  0-内部栏目 1-单网页 2-外部链接$this->modelid = $this->category[$catid]['modelid'];//获取当前栏目所属模型id$this->db->set_model($this->modelid);//查看第八步$this->tablename = $this->db->table_name;//数据表名if(empty($this->category)) {//当前站点id下所有栏目的配置信息return false;} else {return true;}}

        /** * 列表页标签 * @param $data */public function lists($data) {$catid = intval($data['catid']);if(!$this->set_modelid($catid)) return false;if(isset($data['where'])) {//如果pc标签中设置了条件$sql = $data['where'];//pc标签中的条件} else {//如果pc标签中没有设置条件$thumb = intval($data['thumb']) ? " AND thumb != ''" : '';if($this->category[$catid]['child']) {$catids_str = $this->category[$catid]['arrchildid'];$pos = strpos($catids_str,',')+1;$catids_str = substr($catids_str, $pos);$sql = "status=99 AND catid IN ($catids_str)".$thumb;} else {$sql = "status=99 AND catid='$catid'".$thumb;}}$order = $data['order'];//pc标签中排序字段$return = $this->db->select($sql, '*', $data['limit'], $order, '', 'id');//从数据库中获取主表数据,使用的也是sql语句查询//调用副表的数据if (isset($data['moreinfo']) && intval($data['moreinfo']) == 1) {$ids = array();foreach ($return as $v) {if (isset($v['id']) && !empty($v['id'])) {$ids[] = $v['id'];} else {continue;}}if (!empty($ids)) {$this->db->table_name = $this->db->table_name.'_data';//副表名$ids = implode('\',\'', $ids);$r = $this->db->select("`id` IN ('$ids')", '*', '', '', '', 'id');if (!empty($r)) {foreach ($r as $k=>$v) {if (isset($return[$k])) $return[$k] = array_merge($v, $return[$k]);//主表中数据与副表中数据合并}}}}return $return;//返回查询到的数据}


第八步:content_model类,路径:phpcms/model/content_model.class.php

        public $table_name = '';//数据库表名public $category = '';public function __construct() {$this->db_config = pc_base::load_config('database');//加载数据库配置信息$this->db_setting = 'default';//加载数据库默认的配置信息parent::__construct();$this->url = pc_base::load_app_class('url', 'content');$this->siteid = get_siteid();//得到当前站点id}public function set_model($modelid) {$this->model = getcache('model', 'commons');//获取所有模型的配置信息  1-文档模型 2-下载模型 3-图片模型    跟后台设置有关$this->modelid = $modelid;//当前模型id                $this->table_name = $this->db_tablepre.$this->model[$modelid]['tablename'];//模型所对应的数据表 文档模型-news  图片模型-picture 下载模型-download$this->model_tablename = $this->model[$modelid]['tablename'];}

总结:pc标签内部机制也是通过sql语句来返回数据的