一个用PHP实现的UBB类

  <?php
  /*
  如有转载,请注明作者
  作者:何志强
  文件:ubb.php
  备注:说是改进,其实核心函数parse()已经完全重写了,而且思路也是不一样的。
  不过仍是受何志强的例子的启发,而且测试的例子还有URLCHECK等几个函数也是沿用的何志强的程序,谢谢何志强。
  目前还没有颜色的功能,但我会加入的。
  如果在程序上有什么BUG或不便的地方,请给我MAIL。
  谢谢!
  改进功能:
  对字符串进行UBB编码,该类目前只支持下列几个简单且实用的编码:
  1.URL裢接
  http://www.phpexe.com/
  http://头可以不需要
  如phpexe.com也是可以的。
  2.Email裢接
  demo 163.net
  3.图片裢接
  同URL链接一样,前面的http也可以不要。
  4.文字方面
  粗体字
  斜体字
  加下划线
  1号标题字
  ...
  6号标题字
  [tt][/tt]
  [s][/s]
  [em][/em]
  [strong][/strong]
  [samp][/samp]
  [kbd][/kbd]
  [var][/var]
  [dfn][/dfn]
  [cite][/cite]
  注意以下几点:
  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));
  }
  ?>