简介:面向需要快速搭建区块链或数字货币资讯门户的开发者与站长,这套源码基于帝国cms 7.5内核仿制非小号官网风格,完整覆盖PC端与手机端,适合在php+mysql环境中部署运行。压缩包约793.56MB,主要文件类型包括帝国cms模板源码、手机端适配文件、火车头采集规则与采集模块、同步生成插件等,整体目录结构清晰,模板与模块划分明确,易于定位修改。目前已有144人浏览学习。项目完全开源,无授权限制,可自由二次开发和商业使用;内置火车头采集规则,可直接对接非小号官网及相关资讯源,自动抓取区块链、数字货币行业内容,帮助快速积累栏目数据。页面采用非小号典型的简洁大气风格,视觉上符合区块链门户定位,同时后台同步生成插件能简化模板缓存更新与文章发布流程,降低日常运维负担,适合有一定帝国cms基础、希望快速上线行业资讯站的团队使用。
1. 仿非小号最难的从来不是页面,而是帝国CMS 7.5的数据结构
拿到“帝国cms7.5仿非小号区块链门户资讯网站源码 带手机版.zip”这类压缩包,多数人的第一反应是上传解压、改数据库、刷新首页,结果往往出在列表页空白、行情数据不动、手机端点开还是PC版这三件事上。原因不复杂:非小号本质上不是一个“文章资讯站”,而是一个“币种数据库+资讯流”的复合站点。帝国CMS 7.5是为文章而生的系统,要把币种表、快讯、长文、排行聚合到同一套栏目结构里,先做模型规划再写模板,否则后面每一步都是补丁套补丁。这篇文章按我实际做这类站点时的顺序来讲:先拆数据结构,再写列表模板,再处理手机版和性能,最后落到一个能省掉大量重建时间的增量刷新技巧上。适合正在做区块链资讯门户、币种行情页,以及接帝国CMS仿站单子的PHP开发者。
2. 先拆栏目再建表:区块链门户在帝国CMS 7.5里的模型规划
2.1 非小号首页的板块怎么映射到帝国CMS 7.5的栏目
把非小号首页横向切开,通常能看到三类内容:顶部是币种行情表和涨跌榜,中间是快讯时间流,下面是长文资讯和项目库。对应到帝国CMS 7.5,这三类不应该都丢进“文章模型”里。我一般这样规划:
| 非小号首页板块 | 帝国CMS 7.5 实现方式 | 模型类型 | 理由 |
|---|---|---|---|
| 头条资讯/深度文章 | 资讯栏目 | 内置文章模型 | 文章模型自带静态生成、专题、评论,长文场景最合适 |
| 快讯时间流 | 快讯栏目 | 内置新闻模型 | 标题即正文,列表页直接输出,不生成冗余详情页 |
| 行情总览/涨跌榜 | 自定义数据表 pb_coin | 帝国数据表+SQL调用 | 行情数据每分钟变化,不能走文章审核流程 |
| 项目库/币种详情 | 外部数据表栏目 | 数据表联动 | 详情页用ID从pb_coin取字段,与行情表共用数据 |
外部数据表栏目是帝国CMS 7.5一个很实用的机制:栏目不关联内置的文章表,而是关联你自己建的数据表。这样列表页模板里能像普通内容一样用[!--字段名--]输出,后台也能直接管理数据行,不用每次都手写SQL拼页面。但要注意一个前置操作:新建外部数据表栏目后,必须到“系统设置→数据更新→更新缓存”刷新一次,否则模板里新增的字段名会解析不出来。
2.2 币种行情表的设计与批量更新脚本
在动手写模板之前,先把行情表建好。币种表和文章表最大的区别是更新频率高、数据行多,所以字段类型和索引要从一开始就设计对。
-- 币种行情表,按非小号首页的信息架构拆字段 CREATE TABLE `pb_coin` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(30) NOT NULL DEFAULT '' COMMENT '币种名称', `symbol` varchar(10) NOT NULL DEFAULT '' COMMENT '交易符号', `price` decimal(18,8) NOT NULL DEFAULT '0.00000000' COMMENT '最新价格USD', `marketcap` decimal(20,2) NOT NULL DEFAULT '0.00' COMMENT '市值USD', `vol24h` decimal(20,2) NOT NULL DEFAULT '0.00' COMMENT '24小时成交量USD', `change24h` decimal(8,2) NOT NULL DEFAULT '0.00' COMMENT '24小时涨跌幅百分比', `updatetime` int(11) NOT NULL DEFAULT '0' COMMENT '行情时间戳', PRIMARY KEY (`id`), KEY `idx_marketcap` (`marketcap`), KEY `idx_change24h` (`change24h`), KEY `idx_symbol` (`symbol`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;价格字段不用float,原因很简单:PHP浮点数在展示价格时会出现精度问题,而币种价格往往要精确到小数点后8位。decimal(18,8)的18位整数部分足够覆盖主流币种价格,8位小数兼容大部分山寨币。涨跌幅直接用decimal(8,2)存百分比数值,模板里用正负号控制红绿色。每次建表时给涨跌幅和市值建索引是习惯,因为首页涨幅榜、跌幅榜都是ORDER BY这两个字段,没有索引的数据量过2万后会明显变慢。
数据来源通常是行情API,我习惯用一个独立的更新脚本,每5分钟拉一次全量行情,用UPDATE而不是先DELETE再INSERT,避免自增ID频繁变化导致详情页URL失效:
<?php // /e/admin/update_coin.php 通过计划任务每5分钟执行一次 require_once('../class/connect.php'); require_once('../class/db_class.php'); $coinList = json_decode(file_get_contents('https://你的行情数据源地址/coinlist'), true); if (!$coinList) { exit('数据源返回异常 @ ' . date('Y-m-d H:i:s')); } foreach ($coinList as $coin) { $stmt = $link->prepare( "UPDATE pb_coin SET price=?, marketcap=?, vol24h=?, change24h=?, updatetime=? WHERE symbol=?" ); $stmt->bind_param( 'ddddis', $coin['price'], $coin['marketcap'], $coin['vol24h'], $coin['change24h'], time(), $coin['symbol'] ); $stmt->execute(); } echo 'coin updated @ ' . date('Y-m-d H:i:s') . PHP_EOL;这里用预处理语句而不是拼接SQL,是因为symbol字段虽然长度只有10,但来源是外部数据,养成参数绑定的习惯可以避免SQL注入风险。UPDATE按symbol匹配还有一个好处:币种新增或下架时,脚本只更新已有数据,不会误插入脏数据。updatetime字段在模板里可以直接当排序依据,也可以用来判断行情是否过期。
2.3 zip源码包落地前先查这三处
很多带模板的源码包为了体积小,往往去掉了官方安装流程,直接把配置文件和模板打包在一起。拿到zip后不要立刻传服务器,先本地解压做三件事,能省下大量排错时间。
第一是查后门。网上下载的帝国CMS模板是国内源码站后门重灾区,查一下有没有加密函数:
# 在解压目录执行,排除帝国CMS本身的加密类文件 grep -rn --include='*.php' -E 'eval\(|base64_decode\(|gzinflate\(|assert\(' . \ | grep -v 'e/class' | grep -v 'e/data'常见的恶意代码会藏在模板文件里,用eval包裹一段base64字符串,这段字符串decode之后通常是连接远程服务器的代码。看到可疑的包含函数和加密函数组合,直接删掉对应文件,不要尝试修复。
第二是改数据库配置。帝国CMS 7.5的数据库配置集中在/e/class/config.php,打开后检查表前缀、数据库名、密码这三项。表前缀填错会报“数据表不存在”,而帝国CMS 7.5的模板标签里往往硬编码了表前缀变更逻辑,改错可能出现部分栏目有数据、部分空白。
第三是删/e/install安装目录。很多源码包把install目录保留了,上线初期访问者一旦触发安装流程,可能导致数据表被重建,这个风险比模板出问题严重得多。
3. 币种列表页的灵动标签参数与排序字段的白名单处理
3.1 灵动标签10个参数位,写错一位列表就空白
帝国CMS 7.5模板里最常写的调用是[e:loop],它的参数有10个,顺序是:栏目ID、显示条数、操作类型、是否只显示有标题图片、附加SQL条件、显示排序、操作分类、标题截取、操作时间、是否按模板输出。这个顺序我在实际项目里隔三差五就要回去对照一次,因为它跟很多其他CMS的标签参数顺序完全不同,很容易把“排序”和“条数”互换。写错了不报错,只是列表内容不对,排查时比报错更隐蔽。
在栏目列表模板里,典型写法是这样:
[e:loop={14,20,0,0,0,'',1,0,30,0}] <li> <a href="[!--titleurl--]" title="[!--fulltitle--]">[!--title--]</a> <span>[!--newstime--]</span> </li> [/e:loop]| 参数位 | 示例值 | 含义 | 常见坑 |
|---|---|---|---|
| 1 栏目ID | 14 | 栏目ID,外部数据表栏目同样适用 | 填成栏目名称会静默失败 |
| 2 显示条数 | 20 | 列表最多返回行数 | 行情榜单不要超过50条 |
| 3 操作类型 | 0 | 0默认列表,1推荐,2头条 | 外部数据表栏目用0 |
| 4 标题图片 | 0 | 1时只取有标题图的条目 | 快讯栏目必须写0 |
| 5 附加SQL | '' | 额外条件,如"isgood=1" | 单引号要注意转义 |
| 6 显示排序 | 1 | 0按时间,1按ID倒序 | 币价表要按市值排,这里用不了字段名 |
| 7 操作分类 | 0 | 子栏目操作 | 父栏目ID要确认子栏目是否继承 |
| 8 标题截取 | 0 | 0不截取,数字为字数 | 中文按字数截取,不是字节 |
| 9 操作时间 | 30 | 按时间范围过滤,0不限制 | 快讯流建议0 |
| 10 按模板 | 0 | 0不按模板输出,1按列表模板 | 外部数据表一般写0 |
对行情首页来说,第6位参数是最大的限制:灵动标签的排序只支持内置的几种排序规则,做不到按自定义数据表的marketcap排序。所以行情榜单我很少用[e:loop],直接在列表内容模板里写PHP。
3.2 外部数据表栏目在模板里直接跑PHP排序
在模板文件里可以直接操作帝国CMS 7.5的全局数据库对象$empire,做动态列表页时这是最直接的方式。下面这段代码就是一个支持排序参数的行情榜:
<?php // 列表内容模板:行情榜 $order = isset($_GET['order']) ? trim($_GET['order']) : 'marketcap'; $allow = array('marketcap', 'change24h', 'vol24h', 'price'); if (!in_array($order, $allow)) { $order = 'marketcap'; // 白名单过滤,防止ORDER BY注入 } $limit = 30; $sql = "SELECT name, symbol, price, marketcap, change24h FROM pb_coin ORDER BY {$order} DESC LIMIT {$limit}"; $query = $empire->query($sql); while ($r = $empire->fetch($query)) { $class = $r['change24h'] >= 0 ? 'up' : 'down'; echo '<li class="' . $class . '">' . '<span class="symbol">' . $r['symbol'] . '</span>' . '<span class="price">$' . number_format($r['price'], 8, '.', '') . '</span>' . '<span class="change">' . $r['change24h'] . '%</span>' . '</li>'; } ?>这里有两个关键点。第一,$order必须走白名单,因为ORDER BY后面的字段名无法用预处理参数绑定,直接拼接$_GET['order']等于把排序逻辑暴露给外部,虽然不算严重漏洞,但会被接口抓取方利用。第二,number_format的第4个参数传空字符串,意思是不用千分位逗号,因为价格和小数点后面8位数字一旦加上逗号,在手机端窄屏下会换行错位。
动态列表模板还有一个特性:每次访问都会实时查库,所以行情页天然是实时的,代价是数据库压力大。如果只是做一个静态化的栏目列表页,[e:loop]会在生成HTML时被解析一次,之后访问的是静态文件,适合快讯列表。
3.3 榜单页排序参数的校验与可缓存性
展示层不直接暴露原始参数名是另一个常见做法。URL里的order参数我用数字代替字段名,比如?order=1表示市值,?order=2表示涨跌幅:
$orderMap = array( 1 => 'marketcap', 2 => 'change24h', 3 => 'vol24h' ); $key = isset($_GET['order']) ? intval($_GET['order']) : 1; $order = isset($orderMap[$key]) ? $orderMap[$key] : 'marketcap';这样做的好处是URL语义更短,而且intval之后不存在任何注入可能。三个榜单tab在页面上分别链接到?order=1、?order=2、?order=3,切换时浏览器直接刷新整页。如果希望切换不刷新页面,可以做Ajax请求同一个列表页,返回一段HTML插到容器里,后端代码完全不用改。
榜单页适合做文件缓存:把每个order对应的HTML片段各自存成一个shtml文件,由计划任务每1分钟刷新一次。时间间隔不要太短,行情数据源的更新频率一般也就是分钟级,60秒的缓存不会让用户觉得数据滞后。
4. 手机版模板组与移动端同步生成的落地配置
4.1 帝国CMS 7.5多模板开关和模板组绑定
帝国CMS 7.5的移动端方案是模板组级别的“多模板”,不是单独一个WAP插件。后台“系统设置→参数设置”里有一个多模板开关,打开后每个栏目和首页都可以指定PC模板组和手机模板组。这样做的收益很明显:PC和WAP共用一套数据库,栏目、会员、评论数据天然同步,不需要双份内容。缺点是要维护两套模板变量,新增栏目时得同时指定两个模板组的列表模板和内容模板,漏一个手机端就会跑到PC样式上。
我实际操作时按这四步走:
- 后台“模板管理→模板组管理”新建一个模板组,名称写“手机版”,绑定域名
m.xxx.com。 - 在template目录新建手机版模板目录,把PC端首页模板复制过去改成移动端布局。
- 每个栏目编辑时,在“模板组”设置里分别指定PC端和手机端的列表模板、内容模板。
- 系统设置→参数设置开启多模板,并把“手机版访问模板组”设为刚才新建的那个。
访问入口同样不能忽略。很多源码包把手机端跳转逻辑写在首页模板里,用PHP判断UA再header跳转。这种做法的缺点是:手机浏览器先下载了PC首页HTML,再执行跳转,首屏被白浪费一次。我一般不用PHP或JS跳转,直接在Nginx层用map加rewrite。
map $http_user_agent $is_mobile { default 0; ~*mobile|android|iphone|ipad 1; } server { listen 80; server_name www.xxx.com; if ($is_mobile = 1) { rewrite ^/(.*)$ https://m.xxx.com/$1 redirect; } }map指令写在http块里定义变量,server块引用。注意rewrite目标必须写完整域名,否则会被当前server块再次匹配,造成重定向循环。同时手机版server块里要判断host已经是m.xxx.com就停止跳转,防止PC模板和手机模板互相踢。
4.2 移动端同步生成不是双写数据,而是双模板组刷新
所谓“移动端同步生成”,很多刚接触帝国CMS 7.5的人会误以为要配置两套数据,实际上完全是同一批内容,只是生成HTML时要分别按PC模板组和WAP模板组跑一遍。这里有个容易踩的坑:后台“刷新首页”只会按当前访问域名所属的模板组生成。也就是说,用 www.xxx.com 打开后台点刷新,生成的是PC首页,WAP首页还是旧的。
常见做法是写一个命令行脚本,分别处理两个模板组。帝国CMS 7.5自带的后台生成功能本质上是带登录态的长请求,生产环境我习惯用curl按模板组触发:
#!/bin/bash # 全站生成脚本:PC与WAP分开执行,避免模板组串掉 BASE=/www/wwwroot/xxx # PC端批量更新首页和栏目页 curl -s -b /tmp/ecms_cookie.txt \ "https://www.xxx.com/e/admin/ecmsinfo.php?enews=UpdateAll" > /tmp/make_pc.log 2>&1 # WAP端按手机模板组生成,先从后台取一份登录cookie curl -s -c /tmp/ecms_cookie.txt \ -d "username=admin&password=你的密码" \ "https://www.xxx.com/e/admin/login.php" > /dev/null 2>&1 curl -s -b /tmp/ecms_cookie.txt \ "https://m.xxx.com/e/admin/ecmsinfo.php?enews=UpdateAll" > /tmp/make_wap.log 2>&1cookie处理和登录模拟在不同版本里细节有差异,所以脚本里要加结果校验,单纯依赖curl的返回值不靠谱。因为我判断WAP首页是否真的生成成功,看HTML特征比看日志更直接:
# 抓取WAP首页,检查是否包含手机模板标志,比如mobile.css特征字符 if curl -s https://m.xxx.com/ | grep -q 'assets/wap/'; then echo "WAP home generated OK @ $(date)" else echo "WAP home FAILED @ $(date)" >> /tmp/make_wap.err fi这里的关键点是,手机模板目录和PC模板目录的文件路径要有区分度,比如模板里引用/assets/wap/,校验时就grep这个特征。不要grep一个PC端也存在的通用关键词,那样校验结果没有意义。
4.3 手机版行情卡片流和JSON刷新接口
手机版行情榜不适合用PC那种宽表格,一般用卡片流,每行两到三个币种,适合拇指滑动。模板里复用第三段的PHP循环,输出时把表格标签换成卡片容器就行。这里有一个提速点:模板本身只是骨架,行情数字最好由前端每秒或每5秒拉一次JSON再渲染,不然页面被shtml缓存后价格就变成死数据了。
async function refreshPrice(symbol) { const resp = await fetch('/e/action/coinjson.php?symbol=' + symbol); const data = await resp.json(); document.getElementById('price-' + symbol).textContent = '$' + data.price; } // 每5秒刷新一次页面内所有币价 setInterval(() => document.querySelectorAll('[data-symbol]') .forEach(el => refreshPrice(el.dataset.symbol)), 5000);对应的接口文件要做两件事:参数白名单和Referer校验:
<?php // /e/action/coinjson.php if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'xxx.com') === false) { exit(json_encode(array('error' => 'forbidden'))); } $symbol = isset($_GET['symbol']) ? preg_replace('/[^A-Za-z0-9]/', '', $_GET['symbol']) : ''; // 数据量不大,这里直接查库 $sql = "SELECT price, change24h, marketcap FROM pb_coin WHERE symbol='{$symbol}' LIMIT 1"; $r = $empire->fetch($empire->query($sql)); echo json_encode($r);接口不做缓存、不返回列表,每次都只查一行数据,数据库压力几乎可以忽略。preg_replace白名单把symbol限制在字母数字范围内,配合拼接SQL也安全。
5. 首页聚合性能优化与Nginx伪静态改造
5.1 一次内存排序顶掉80次SQL查询
完全用灵动标签堆出来的首页,每个[e:loop]发起一次查询。非小号首页板块多,行情、快讯、资讯、推荐位加起来二十个标签很正常,首页总查询数轻松到60到80次。放在文章站还能忍,放在币价站上就很尴尬——币价数据每分钟在变,用户每次刷新都触发整页动态生成,数据库连接数一高,MySQL的processlist里全是SELECT。
我一般做两层缓存。第一层是PHP变量级缓存,适合同一个请求里多个模板片段查同一张表的情况。比如首页需要按市值排行、按涨跌幅排行、按成交额排行,三次查询共用一个SQL结果集:
function get_coin_list($order, $limit) { static $all = null; // 当前请求内只查一次全量,之后做内存排序 if ($all === null) { $all = $empire->query("SELECT * FROM pb_coin ORDER BY marketcap DESC LIMIT 100") ->fetch_all(MYSQLI_ASSOC); } usort($all, function ($a, $b) use ($order) { return $a[$order] <=> $b[$order]; }); return array_slice($all, 0, $limit); } // 首页三个榜单这样调用 $marketRank = get_coin_list('marketcap', 10); $changeRank = get_coin_list('change24h', 10); $volRank = get_coin_list('vol24h', 10);这里有一个取舍要讲清楚:先取市值前100,再在内存里按涨跌幅重新排序,拿到的“涨幅榜”并不完全准确,因为真正涨幅最大的币可能根本没进市值前100。要严谨,得让SQL直接按change24h排序后再取100条。我的取舍是考虑到首页三个榜单更看重“主流币种”,实际业务口径由项目自己定。如果要求全量准确性,把LIMIT 100改大即可,但内存排序消耗会上升。
第二层是文件级缓存。帝国CMS 7.5文章列表本身可以做静态化,但币价表没有内置静态生成逻辑。常见做法是让计划任务定时curl一个刷新缓存接口,生成三个shtml文件,首页通过include方式引入。这样首页HTML被帝国CMS正常静态化后,行情区块依然能每分钟更新。
排查排序慢的常规手段是EXPLAIN,看字段是否走了索引:
EXPLAIN SELECT * FROM pb_coin ORDER BY change24h DESC LIMIT 20;如果Extra列出现Using filesort,就是索引没生效,回到建表语句补KEY idx_change24h。这一条在数据量增大后尤其重要,别等线上报警了再去补。
5.2 Nginx rewrite生成可收录的列表和详情URL
帝国CMS 7.5支持两种URL形态:后台“参数设置”里的页面伪静态,以及Nginx层面的rewrite。两者同时开容易出现重复内容。我一般只开Nginx层rewrite,后台保持动态URL,这样栏目首页是classid/index.html,翻页是listinfo-14-2.html,文章页是showinfo-14-123.html:
location / { if (!-f $request_filename) { rewrite ^/listinfo-([0-9]+)-([0-9]+)\.html$ /e/action/ListInfo.php?classid=$1&page=$2 last; rewrite ^/showinfo-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /e/action/ShowInfo.php?classid=$1&id=$2&page=$3 last; rewrite ^/tags/([a-z0-9\-]+)\.html$ /e/tags/?tag=$1 last; } }两个易错点值得单独说。第一,if (!-f $request_filename)必须写。否则当后台生成过静态HTML后,Nginx不会把请求交给PHP动态输出,就会出现“后台删了文章,前台还能访问旧页面”的缓存残留。第二,rewrite用last不要用break。last会重新匹配location,而帝国CMS的/e/action/路径通常还有自己的location规则,用break可能导致重写后的URL在另一个location里再次被处理,最终404。
动态伪静态页在并发高时同样撑不住,所以线上规范做法是:列表页和详情页都开启帝国CMS后台的静态生成,伪静态规则只作为兜底。
5.3 内容静态化、行情走JSON接口的动静结合
行情数据的实时刷新增量小但频率高,用JS异步拉取最划算。内容页模板里不直接输出价格,而是输出一个容器,再通过JS从 coinjson.php 拉取最新价格并渲染。这样做的好处是内容页可以放心静态化,无论缓存多久,价格总是新的。
具体实现上,静态化的资讯页里嵌这样一段HTML:
<span id="price-BTC">// coinjson.php 支持批量: ?symbol=BTC,ETH $symbols = array_map('trim', explode(',', $_GET['symbol'])); $allowed = array(); foreach ($symbols as $s) { if (preg_match('/^[A-Za-z0-9]{1,10}$/', $s)) { $allowed[] = "'" . $s . "'"; } } $sql = "SELECT symbol, price, change24h FROM pb_coin WHERE symbol IN (" . implode(',', $allowed) . ")";接口要做好防盗链,至少校验Referer是否为本站域名,否则别人可以直接抓你的行情接口刷流量。批量模式下前端一次性传入页面内所有symbol即可。
6. 一个shtml增量刷新接口解决手机端快讯延迟
6.1 快讯板块不能等整站生成
PC和WAP首页都静态化之后,行情区块可以走JSON保持实时,但快讯列表依然是静态HTML。每发一条新快讯,得等下一次批量生成脚本跑完才会出现在首页,运气不好延迟可能有几十分钟。对普通资讯站可以接受,对区块链快讯站来说,十分钟可能已经是另一条新闻了。解决方案不是缩短全站生成周期,而是把快讯区块单独抽出来做增量刷新,其余静态页面保持不变。
6.2 局部shtml、cron与flock的最小实现
这里给出一个最小可用的实现,把首页快讯区块渲染到独立shtml文件,首页模板include它。
<?php // /e/wap/refresh_quicknews.php —— 只刷新手机版首页的快讯区块 require_once('../class/connect.php'); require_once('../class/db_class.php'); // 取最近8条已审核快讯,checked=1这个条件不能省,预览态稿子会被刷到线上 $sql = "SELECT id, title, addtime FROM {$dbtbpre}ecms_news WHERE classid=你的快讯栏目ID AND checked=1 ORDER BY id DESC LIMIT 8"; $query = $empire->query($sql); $html = '<section class="quick-news">'; while ($r = $empire->fetch($query)) { $time = date('H:i', $r['addtime']); $html .= '<a class="news-item" href="https://m.xxx.com/showinfo-你的快讯栏目ID-' . $r['id'] . '.html"><span class="time">' . $time . '</span><span>' . $r['title'] . '</span></a>'; } $html .= '</section>'; // 只写快讯局部片段,不要覆盖整个首页 file_put_contents(ECMS_PATH . 'e/wap/tpl_quicknews.shtml', $html); echo 'quicknews refreshed @ ' . date('Y-m-d H:i:s') . PHP_EOL;WAP首页模板里的快讯区域这样引入:
<!--#include virtual="/e/wap/tpl_quicknews.shtml" -->如果Nginx没有开启SSI,就用PHP的include引入shtml文件,效果相同:
<?php include(ECMS_PATH . 'e/wap/tpl_quicknews.shtml'); ?>最后加一条cron任务,每分钟执行一次:
* * * * * /usr/bin/flock -x /tmp/quicknews.lock -c "/usr/bin/php /www/wwwroot/xxx/e/wap/refresh_quicknews.php" > /tmp/quicknews.log 2>&1flock的作用是防止脚本上一次还没跑完、下一次又启动,两个进程同时写一个shtml文件可能出现空白文件。因为脚本很快,但谁也无法保证某次文件锁或数据库查询不卡住,加一把flock没有额外成本,却能把极端情况兜住。刷新WAP首页快讯区块的同时,PC端首页如果也引用了同一个shtml,等于一次脚本同时维护了手机版和PC版的快讯板块,移动端同步生成的核心就在这个共享片段上。
本文还有配套的精品资源,点击获取