一个改进的UBB类


Posted in PHP onOctober 09, 2006

<?php
/*
如有转载,请注明作者

原作者: 何志强
改进: SonyMusic[ sonymusic@163.net ]
文件: ubb.php
备注: 说是改进,其实核心函数parse()已经完全重写了,而且思路也是不一样的。
不过仍是受何志强的例子的启发,而且测试的例子还有URLCHECK等几个函数也是沿用的何志强的程序,谢谢何志强。
目前还没有颜色的功能,但我会加入的。
如果在程序上有什么BUG或不便的地方,请给我MAIL。
谢谢!
改进功能:
对字符串进行UBB编码,该类目前只支持下列几个简单且实用的编码:
1. URL裢接
[url] http://phpuser.com/ [/url]
http://头可以不需要
如[url]phpuser.com[/url]也是可以的。
2. Email裢接
[email] sonymusic@163.net [/email]
3. 图片裢接
[img] http://www.phpchina.com/images/logo.gif [/img]
同URL链接一样,前面的http也可以不要。
4. 文字方面
[b]粗体字[/b]
[i]斜体字[/i]
[u]加下划线[/u]
[h1]1号标题字[/h1] ... [h6]6号标题字[/h6]
[sup][/sup]
[sub][/sub]
[tt][/tt]
[s][/s]
[strike][/strike]
[em][/em]
[strong][/strong]
[code][/code]
[samp][/samp]
[kbd][/kbd]
[var][/var]
[dfn][/dfn]
[cite][/cite]
[small][/small]
[big][/big]
[blink][/blink]
注意以下几点:
1. url,email,img等标签是不分大小写的.
2. 在标签中不允许有TAB键出现,但空格允许。
3. 该类要调用htmlencode,htmlencode4textarea,emailcheck函数和urlcheck类.
4. 修改后支持嵌套,但url,email,img这三个标签不是允许嵌套的。
技术资料:
Ultimate Bulletin Board
http://www.ultimatebb.com/  
What is UBB Code
http://www.scriptkeeper.com/ubb/ubbcode.html  
*/

include("urlcheck.php");
include("otherfunc.php"); //这两个文件的内容,附在最后。

//ubbcode类
class ubbcode{
var $call_time=0;
//可处理标签及处理函数对应表
var $tags = array( //小写的标签 => 对应的处理函数
'url' => '$this->url',
'email' => '$this->email',
'img' => '$this->img',
'b' => '$this->simple',
'i' => '$this->simple',
'u' => '$this->simple',
'tt' => '$this->simple',
's' => '$this->simple',
'strike' => '$this->simple',
'h1' => '$this->simple',
'h2' => '$this->simple',
'h3' => '$this->simple',
'h4' => '$this->simple',
'h5' => '$this->simple',
'h6' => '$this->simple',
'sup' => '$this->simple',
'sub' => '$this->simple',
'em' => '$this->simple',
'strong' => '$this->simple',
'code' => '$this->simple',
'samp' => '$this->simple',
'kbd' => '$this->simple',
'var' => '$this->simple',
'dfn' => '$this->simple',
'cite' => '$this->simple',
'small' => '$this->simple',
'big' => '$this->simple',
'blink' => '$this->simple'
);
//url裢接属性
var $attr_url;
//url合法性检查对象
var $urlcheck;

function ubbcode($attr_url){
$this->attr_url = ''.$attr_url;
$this->urlcheck = new urlcheck();
}

//对$str进行UBB编码解析
function parse($str){
$this->call_time++;
$parse = ''.htmlencode($str);

$ret = '';
while(true){
$eregi_ret=eregi("[[#]{0,1}[[:alnum:]]{1,7}]",$parse,$eregi_arr); //查找[xx]
if(!$eregi_ret){
$ret .= $parse;
break; //如果没有,返回
}
$pos = @strpos ($parse,$eregi_arr[0]);
$tag_len=strlen($eregi_arr[0])-2;//标记长度
$tag_start=substr($eregi_arr[0],1,$tag_len);
$tag=strtolower($tag_start);

if((($tag=="url") or ($tag=="email") or ($tag=="img")) and ($this->call_time>1)){
echo $this->call_time."<br>";
return $parse;//如果不能是不能嵌套的标记,直接返回
}

$parse2 = substr($parse,0,$pos);//标记之前
$parse = substr($parse,$pos+$tag_len+2);//标记之后
if(!isset($this->tags[$tag])){
echo "$tag_start<br>";
$ret .= $parse2.'['.$tag_start.']';
continue;//如果是不支持的标记
}

//查找对对应的结束标记
$eregi_ret=eregi("[/".$tag."]",$parse,$eregi_arr);
if(!$eregi_ret){
$ret .= $parse2.'['.$tag_start.']';
continue;//如果没有对应该的结束标记
}
$pos=strpos($parse,$eregi_arr[0]);
$value=substr($parse,0,$pos);//这是起止标记之间的内容
$tag_end=substr($parse,$pos+2,$tag_len);
$parse=substr($parse,$pos+$tag_len+3);//结束标记之后的内容

if(($tag!="url") and ($tag!="email") and ($tag!="img")){
$value=$this->parse($value);
}

$ret .= $parse2;
eval('$ret .= '.$this->tags[$tag].'("'.$tag_start.'","'.$tag_end.'","'.$value.'");');
}
$this->call_time--;
return $ret;
}

function simple($start,$end,$value){
return '<'.$start.'>'.$value.'</'.$end.'>';
}

function url($start,$end,$value){
$trim_value=trim($value);
if (strtolower(substr($trim_value,0,7))!="http://")
$trim_value="http://".$trim_value;
if($this->urlcheck->check($trim_value)) return '<a href="'.$trim_value.'" '.$this->attr_url.'>'.$value.'</a>';
else return '['.$start.']'.$value.'[/'.$end.']';
}

function email($start,$end,$value){
if(emailcheck($value)) return '<a href="mailto:'.$value.'">'.$value.'</a>';
else return '['.$start.']'.$value.'[/'.$end.']';
}

function img($start,$end,$value){
$trim_value=trim($value);
if ((strtolower(substr($trim_value,0,7))!="http://") or ($this->urlcheck->check($trim_value)))
return '<img src="'.$trim_value.'"></img>';
else return '['.$start.']'.$value.'[/'.$end.']';
}
}

//测试
echo '<html>';
echo '<head><title>测试</title></head>';
echo '<body>';
echo '<form action="'.str2url($PATH_INFO).'" method="post">';
echo '<textarea cols="100" rows="10" name="ubb">'.htmlencode4textarea($ubb).'</textarea><br>';
echo '<input type="submit" value="转换">';
echo '</form>';

if(isset($ubb)){
$ubbcode = new ubbcode('target="_blank"');
echo '<hr>'.$ubbcode->parse($ubb);
}

echo '</body>';
echo '</html>';

?>

文件urlcheck.php的内容:
<?php
//urlcheck.php
class urlcheck{
var $regex = array(//协议名(注意在这里必须写成小写) => 对应的正则表达式
'ftp' => '$this->ftpurl',
'file' => '$this->fileurl',
'http' => '$this->httpurl',
'https' => '$this->httpurl',
'gopher' => '$this->gopherurl',
'news' => '$this->newsurl',
'nntp' => '$this->nntpurl',
'telnet' => '$this->telneturl',
'wais' => '$this->waisurl'
);

var $lowalpha;
var $hialpha;
var $alpha;
var $digit;
var $safe;
var $extra;
var $national;
var $punctuation;
var $reserved;
var $hex;
var $escape;
var $unreserved;
var $uchar;
var $xchar;
var $digits;

var $urlpath;
var $password;
var $user;
var $port;
var $hostnumber;
var $alphadigit;
var $toplabel;
var $domainlabel;
var $hostname;
var $host;
var $hostport;
var $login;

//ftp
var $ftptype;
var $fsegment;
var $fpath;
var $ftpurl;

//file
var $fileurl;

//http,https
var $search;
var $hsegment;
var $hpath;
var $httpurl;

//gopher
var $gopher_string;
var $selector;
var $gtype;
var $gopherurl;

//news
var $article;
var $group;
var $grouppart;
var $newsurl;

//nntp
var $nntpurl;

//telnet
var $telneturl;

//wais
var $wpath;
var $wtype;
var $database;
var $waisdoc;
var $waisindex;
var $waisdatabase;
var $waisurl;

function check($url){
$pos = @strpos ($url,':',1);
if($pos<1) return false;
$prot = substr($url,0,$pos);
if(!isset($this->regex[$prot])) return false;
eval('$regex = '.$this->regex[$prot].';');
return ereg('^'.$regex.'$',$url);
}

function urlcheck(){
$this->lowalpha = '[a-z]';
$this->hialpha = '[A-Z]';
$this->alpha = '('.$this->lowalpha.'|'.$this->hialpha.')';
$this->digit = '[0-9]';
$this->safe = '[$.+_-]';
$this->extra = '[*()'!,]';
$this->national = '([{}|^~`]|\[|\])';
$this->punctuation = '[<>#%"]';
$this->reserved = '[?;/: @&= ]';
$this->hex = '('.$this->digit.'|[a-fA-F])';
$this->escape = '(%'.$this->hex.'{2})';
$this->unreserved = '('.$this->alpha.'|'.$this->digit.'|'.$this->safe.'|'.$this->extra.')';
$this->uchar = '('.$this->unreserved.'|'.$this->escape.')';
$this->xchar = '('.$this->unreserved.'|'.$this->reserved.'|'.$this->escape.')';
$this->digits = '('.$this->digit.'+)';

$this->urlpath = '('.$this->xchar.'*)';
$this->password = '(('.$this->uchar.'|[?;&=]'.')*)';
$this->user = '(('.$this->uchar.'|[?;&=]'.')*)';
$this->port = $this->digits;
$this->hostnumber = '('.$this->digits.'.'.$this->digits.'.'.$this->digits.'.'.$this->digits.')';
$this->alphadigit = '('.$this->alpha.'|'.$this->digit.')';
$this->toplabel = '('.$this->alpha.'|('.$this->alpha.'('.$this->alphadigit.'|-)*'.$this->alphadigit.'))';
$this->domainlabel = '('.$this->alphadigit.'|('.$this->alphadigit.'('.$this->alphadigit.'|-)*'.$this->alphadigit.'))';
$this->hostname = '(('.$this->domainlabel.'\.)*'.$this->toplabel.')';
$this->host = '('.$this->hostname.'|'.$this->hostnumber.')';
$this->hostport = '('.$this->host.'(:'.$this->port.')?)';
$this->login = '(('.$this->user.'(:'.$this->password.')?@)?'.$this->hostport.')';

$this->ftptype = '[aidAID]';
$this->fsegment = '(('.$this->uchar.'|[?: @&= ])*)';
$this->fpath = '('.$this->fsegment.'(/'.$this->fsegment.')*)';
$this->ftpurl = '([fF][tT][pP]://'.$this->login.'(/'.$this->fpath.'(;[tT][yY][pP][eE]='.$this->ftptype.')?)?)';

$this->fileurl = '([fF][iI][lL][eE]://('.$this->host.'|[lL][oO][cC][aA][lL][hH][oO][sS][tT])?/'.$this->fpath.')';

$this->search = '(('.$this->uchar.'|[;: @&= ])*)';
$this->hsegment = '(('.$this->uchar.'|[;: @&= ])*)';
$this->hpath = '('.$this->hsegment.'(/'.$this->hsegment.')*)';
$this->httpurl = '([hH][tT][tT][pP][sS]?://'.$this->hostport.'(/'.$this->hpath.'([?]'.$this->search.')?)?)';

$this->gopher_string = '('.$this->xchar.'*)';
$this->selector = '('.$this->xchar.'*)';
$this->gtype = $this->xchar;
$this->gopherurl = '([gG][oO][pP][hH][eE][rR]://'.$this->hostport.'(/('.$this->gtype.'('.$this->selector.'(%09'.$this->search.'(%09'.$this->gopher_string.')?)?)?)?)?)';

$this->article = '(('.$this->uchar.'|[;/?:&=]) +@'.$this- >host.')';
$this->group = '('.$this->alpha.'('.$this->alpha.'|'.$this->digit.'|[-.+_])*)';
$this->grouppart = '([*]|'.$this->group.'|'.$this->article.')';
$this->newsurl = '([nN][eE][wW][sS]:'.$this->grouppart.')';

$this->nntpurl = '([nN][nN][tT][pP]://'.$this->hostport.'/'.$this->group.'(/'.$this->digits.')?)';

$this->telneturl = '([tT][eE][lL][nN][eE][tT]://'.$this->login.'/?)';

$this->wpath = '('.$this->uchar.'*)';
$this->wtype = '('.$this->uchar.'*)';
$this->database = '('.$this->uchar.'*)';
$this->waisdoc = '([wW][aA][iI][sS]://'.$this->hostport.'/'.$this->database.'/'.$this->wtype.'/'.$this->wpath.')';
$this->waisindex = '([wW][aA][iI][sS]://'.$this->hostport.'/'.$this->database.'[?]'$this->search.')';
$this->waisdatabase = '([wW][aA][iI][sS]://'.$this->hostport.'/'.$this->database.')';
$this->waisurl = '('.$this->waisdatabase.'|'.$this->waisindex.'|'.$this->waisdoc.')';
}
}

?>

文件otherfunc.php的内容:
<?php
//otherfunc.php
function htmlencode($str){
$str = (string)$str;

$ret = '';
$len = strlen($str);
$nl = false;
for($i=0;$i<$len;$i++){
$chr = $str[$i];
switch($chr){
case '<':
$ret .= '<';
$nl = false;
break;
case '>':
$ret .= '>';
$nl = false;
break;
case '"':
$ret .= '"';
$nl = false;
break;
case '&':
$ret .= '&';
$nl = false;
break;
/*
case ' ':
$ret .= ' ';
$nl = false;
break;
*/  
case chr(9):
$ret .= '    ';
$nl = false;
break;
case chr(10):
if($nl) $nl = false;
else{
$ret .= '<br>';
$nl = true;
}
break;
case chr(13):
if($nl) $nl = false;
else{
$ret .= '<br>';
$nl = true;
}
break;
default:
$ret .= $chr;
$nl = false;
break;
}
}

return $ret;
}

function htmlencode4textarea($str){
$str = (string)$str;

$ret = '';
$len = strlen($str);
for($i=0;$i<$len;$i++){
$chr = $str[$i];
switch($chr){
case '<':
$ret .= '<';
break;
case '>':
$ret .= '>';
break;
case '"':
$ret .= '"';
break;
case '&':
$ret .= '&';
break;
case ' ':
$ret .= ' ';
break;
case chr(9):
$ret .= '    ';
break;
default:
$ret .= $chr;
break;
}
}

return $ret;
}

function emailcheck($email){
$ret=false;
if(strstr($email, '@' ) && strstr($email, '.')){
if(eregi("^([_a-z0-9]+([\._a-z0-9-]+)*)@([a-z0-9]{2,}(\.[a-z0-9-]{2,})*\.[a-z]{2,3})$", $email)){
$ret=true;
}
}
return $ret;
}

function str2url($path){
return eregi_replace("%2f","/",urlencode($path));
}
?>

PHP 相关文章推荐
PHP中GET变量的使用
Oct 09 PHP
PHP中的类-什么叫类
Nov 20 PHP
PHP学习 运算符与运算符优先级
Jun 15 PHP
PHP 开发环境配置(Zend Server安装)
Apr 28 PHP
php新浪微博登录接口用法实例
Dec 23 PHP
php生成PDF格式文件并且加密
Jun 22 PHP
php简单的上传类分享
May 15 PHP
Yii实现文章列表置顶功能示例
Oct 18 PHP
PHP自定义多进制的方法
Nov 03 PHP
微信公众号OAuth2.0网页授权问题浅析
Jan 21 PHP
PHP实现文件下载【实例分享】
Apr 28 PHP
laravel 配置路由 api和web定义的路由的区别详解
Sep 03 PHP
PHP3 safe_mode 失效漏洞
Oct 09 #PHP
vBulletin HACK----关于排版的两个HACK
Oct 09 #PHP
PHP+APACHE实现用户论证的方法
Oct 09 #PHP
从C/C++迁移到PHP——判断字符类型的函数
Oct 09 #PHP
PHP实现图片简单上传
Oct 09 #PHP
在线增减.htpasswd内的用户
Oct 09 #PHP
将OICQ数据转成MYSQL数据
Oct 09 #PHP
You might like
PHP4(windows版本)中的COM函数
2006/10/09 PHP
计算php页面运行时间的函数介绍
2013/07/01 PHP
yii2.0之GridView自定义按钮和链接用法
2014/12/15 PHP
PHP设计模式之简单投诉页面实例
2016/02/24 PHP
php单元测试phpunit入门实例教程
2017/11/17 PHP
Apache站点配置SSL强制跳转443
2021/03/09 Servers
JS图片浏览组件PhotoLook的公开属性方法介绍和进阶实例代码
2010/11/09 Javascript
javascript判断用户浏览器插件安装情况的代码
2011/01/01 Javascript
jquery重新播放css动画所遇问题解决
2013/08/21 Javascript
JavaScript在IE和FF下的兼容性问题
2014/05/19 Javascript
jQuery 复合选择器应用的几个例子
2014/09/11 Javascript
Javascript中arguments和arguments.callee的区别浅析
2015/04/24 Javascript
理解javascript中的with关键字
2016/02/15 Javascript
Web性能优化系列 10个提升JavaScript性能的技巧
2016/09/27 Javascript
jQuery焦点图左右转换效果
2016/12/12 Javascript
Node.js中文件操作模块File System的详细介绍
2017/01/05 Javascript
JS仿QQ好友列表展开、收缩功能(第一篇)
2017/07/07 Javascript
详解react使用react-bootstrap当轮子造车
2017/08/15 Javascript
基于ES6作用域和解构赋值详解
2017/11/03 Javascript
JS实现图片放大镜插件详解
2017/11/06 Javascript
vue-cli3.0 脚手架搭建项目的过程详解
2018/10/19 Javascript
JavaScript中break、continue和return的用法区别实例分析
2020/03/02 Javascript
JavaScript动画实例之粒子文本的实现方法详解
2020/07/28 Javascript
vue实现拖拽进度条
2021/03/01 Vue.js
python读取Android permission文件
2013/11/01 Python
用实例分析Python中method的参数传递过程
2015/04/02 Python
详解如何在Apache中运行Python WSGI应用
2019/01/02 Python
Python中的asyncio代码详解
2019/06/10 Python
python 安装库几种方法之cmd,anaconda,pycharm详解
2020/04/08 Python
Python之字典添加元素的几种方法
2020/09/30 Python
MCAKE蛋糕官方网站:一直都是巴黎的味道
2018/02/06 全球购物
捷克家电和家具购物网站:OKAY.cz
2020/07/23 全球购物
商务英语专业应届毕业生求职信
2013/10/28 职场文书
2020年基层司法所建设情况调研报告
2019/11/30 职场文书
MySQL实现配置主从复制项目实践
2022/03/31 MySQL
Python识别花卉种类鉴定网络热门植物并自动整理分类
2022/04/08 Python