这个链接可以更好的展示虎绿林怎么存储帖子数据,它几乎展示了每种UBB类型的数组结构:
虎绿林的UBB解析器是一个使用正则表达式实现的递归下降解析器:
https://github.com/hu60t/hu60wap6/blob/master/src/class/xubbp.php
https://github.com/hu60t/hu60wap6/blob/master/src/class/ubbparser.php
解析过程:
type=>"text"
(正文)的数组,结束递归。 /*code 代码高亮*/
'!^(^|.*?[\r\n]+)\[code(?:=(\w+))?\]([\r\n]+.*?[\r\n]+)\[/code\]([\r\n]+.*|$)$!is' => array(array(1, 4), 'code', array(2, 3)),
'!^(.*?)\[code(?:=(\w+))?\](.*?)\[/code\](.*)$!is' => array(array(1, 4), 'code', array(2, 3)),
/*time 时间*/
'!^(.*?)\[time(?:=(.*?))?\](.*)$!is' => array(array(1, 3), 'time', array(2)),
/*link 链接*/
'!^(.*?)\[url(?:=(.*?))?\](.*?)\[/url\](.*)$!is' => array(array(1, 4), 'link', array('url', 2, 3)),
'!^(.*?)《(链接|外链|锚):(.*?)》(.*)$!is' => array(array(1, 4), 'link', array(2, 3)),
/*img 图片*/
'!^(.*?)\[img(?:=(.*?))?\](.*?)\[/img\](.*)$!is' => array(array(1, 4), 'img', array('img', 2, 3)),
'!^(.*?)《(图片|缩略图):(.*?)》(.*)$!is' => array(array(1, 4), 'img', array(2, 3)),
'!^(.*?)《表情(?::|:)(.*?)》(.*)$!uis' => array(array(1, 3), 'face', array(2)),
/*video 视频*/
'!^(.*?)《视频:(.*?)》(.*)$!is' => array(array(1, 3), 'video', array(2)),
/*videoStream 视频*/
'!^(.*?)《视频流:(.*?)》(.*)$!is' => array(array(1, 3), 'videoStream', array(2)),
/*audio 音频*/
'!^(.*?)《音频:(.*?)》(.*)$!is' => array(array(1, 3), 'audio', array(2)),
/*audioStream 视频*/
'!^(.*?)《音频流:(.*?)》(.*)$!is' => array(array(1, 3), 'audioStream', array(2)),
/*at @消息*/
'!^(.*?)[@@]([@@##a-zA-Z0-9\x{4e00}-\x{9fa5}_-]+)(.*)$!uis' => array(array(1, 3), 'at', array(2)),
及其对应的处理函数:
/**
* @brief at消息
*/
function at($tag)
{
$tag = str_replace('@', '@', $tag);
$arr = explode('@', $tag);
if (count($arr) > 1) {
$result = array();
foreach ($arr as $v) {
$res = $this->at($v);
$result = array_merge($result, $res);
}
return $result;
}
global $USER;
//user的at方法产生at消息并返回at对象的uid
//会生成at信息的页面须用regAt()方法注册at消息
//若未注册,则不产生at消息,但uid正常返回
if (!is_object($USER)) {
$user = new user;
} else {
$user = $USER;
}
$uid = $user->at($tag);
return array(array(
'type' => 'at',
'tag' => trim($tag),
'uid' => $uid,
));
}
可以看到,最后转化为了这样的数组:
array(array(
'type' => 'at',
'tag' => trim($tag),
'uid' => $uid,
));
一旦内容变成了数组,显示的时候就能想怎么操作就怎么操作了:
https://github.com/hu60t/hu60wap6/blob/master/src/class/ubbdisplay.php
/*at用户名*/
public function at($data)
{
global $PAGE;
$jsFunc = $this->getOpt('at.jsFunc');
$uid = (int)$data['uid'];
$uinfo = new UserInfo();
$uinfo->uid($uid);
$name = $uinfo->name === null ? $data['tag'] : $uinfo->name;
if ($jsFunc) {
return '<a class="userat" href="#" onclick="' . $jsFunc . '(\'' . $name . '\',this);return false">@</a><a class="userinfo" href="user.info.' . $uid . '.' . $PAGE->bid . '">' . code::html($name) . '</a>';
} else {
return '<a class="userinfo" href="user.info.' . $uid . '.' . $PAGE->bid . '">@' . code::html($name) . '</a>';
}
}
@suishifanli001,每个UBB标签(包括正文,正文是text
标签)都是一个JSON对象,这些对象罗列成一个JSON数组,数组是有序的,数组的顺序就是标签出现在帖子里的顺序:
所以只要从头遍历这个JSON数组,把里面的每个JSON对象都转换成HTML然后相连,就能得到帖子的显示结果。
如果遍历数组时把每个JSON对象都转换成UBB文本然后相连,就能得到帖子的原始输入。
@suishifanli001,那么在解析时是怎么存储顺序的呢?
正则表达式将内容分成3部分,前文、UBB标记、后文。
UBB标记被当场转换为数组。
前文和后文通过递归调用随后被转换为数组。
然后将这些数组按[前文数组, UBB标记数组, 后文数组]
的顺序合并。
这样最终得到的结果就是有序的,标签对象的顺序就是帖子内容的顺序。
https://github.com/hu60t/hu60wap6/blob/master/src/class/xubbp.php#L226
/**
* 递归解析文本
*/
protected function parser($text)
{
if ($text == '') return array();
foreach ($this->parse as $k => $v) {
$arr = array();
$this->tmp_parse_result = &$arr;
$this->tmp_parse_param = $v;
$ok = preg_replace_callback($k, array($this, 'parseExec'), $text);
if ($ok === NULL) throw new xubbpException("正则表达式 '$k' 错误,匹配失败或达到PHP上限!\n内容过长可能会引发该问题。如果缩减内容后依然无法发送,请联系站长解决。"/*."\n引起错误的文本:$text"*/, 500);
if ($arr === NULL) throw new xubbpException("正则表达式 '$k' 的回调函数 '$v[1]' 返回值错误,应该返回二维数组!\n请联系站长修复该问题", 501);
if ($ok == '') return $arr;
}
return $this->parseText($text);
}
/**
* 使用回调函数执行递归解析
*/
protected function parseExec($argv)
{
$arr = &$this->tmp_parse_result;
$v = $this->tmp_parse_param;
$func = $v[1];
$param = $v[2];
foreach ($param as &$tmpV) {
if (is_int($tmpV)) $tmpV = $argv[$tmpV];
}
$arr = array_merge($this->parser($argv[$v[0][0]]), call_user_func_array(array($this, $func), $param), $this->parser($argv[$v[0][1]]));
return '';
}
把目录中的每一个内容都看一遍,就会对Perl兼容正则表达式(PCRE)有完整充分的认识。
备注:从User Contributed Notes开始是用户留言,看到它就说明这篇内容结束了,可以回到目录看下一篇了。
@suishifanli001,访问这个链接你就能发现,虎绿林的帖子内容不是纯文本的,而是JSON数组。虎绿林通过UBB解析,将帖子内容转换成PHP数组存储在数据库,对于
@
标记会存储uid,在显示时遍历数组动态生成内容,此时就能查询uid最新的用户名了。https://hu60.cn/q.php/bbs.topic.101538.json?_content=json