Перелопатил много движков и отдельных классов и все ради того чтоб узнать как люди решали проблему парсера BB кодов и толкового ничего не нашел чтоб меня устраивало.
Первая проблема парсера состоит в том что он медленный.
Вторая проблема плохо работает с рекурсией.
Третья проблема это добавление новых BB кодов.
Проблем еще много но это основные.
Я попробовал просто разделить текст на составляющие и поочередно соединить весть текст.
В итоге получился Class парсера:
class bb{
public $descr;
public $bbcode=['center','left','right','justify','b','i','u','s','color','url','img','code','hide','hr','key','audio','spoiler','email','quote','youtube','font','size'];
public $bb=[];
public $code=false;
public $hide=false;
public $config=[
'hide' => true,
'videoH' => '460',
'videoW' => '608',
'size' => [8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40],
'font' => [
'a_BraggaStars'=>'/mods/bb/font/a_BraggaStars.ttf',
'a_FuturaRoundTitulSh'=>'/mods/bb/font/a_FuturaRoundTitulSh.ttf',
'a_MachinaOrtoSht'=>'/mods/bb/font/a_MachinaOrtoSht.ttf',
'Agatha_Modern'=>'/mods/bb/font/Agatha_Modern.ttf',
'Art_Nouveau-Bistro'=>'/mods/bb/font/Art_Nouveau-Bistro.ttf',
'Burlak'=>'/mods/bb/font/Burlak.ttf',
'DSCrystal'=>'/mods/bb/font/DSCrystal.ttf',
'HeinrichScript'=>'/mods/bb/font/HeinrichScript.ttf',
'LehmannC'=>'/mods/bb/font/LehmannC.atf',
'm_Ben_Krush'=>'/mods/bb/font/m_Ben_Krush.ttf',
'WoodenShipDecorated'=>'/mods/bb/font/WoodenShipDecorated.ttf',
'Arial'=>'',
'Arial Black'=>'',
'Century Gothic'=>'',
'Courier New'=>'',
'Georgia'=>'',
'Impac'=>'',
'System'=>'',
'Tahoma'=>'',
'Times New Roman'=>'',
'Verdana'=>'',
'Sans-Serif'=>''
]
];
public function center($val){
return nl2br(($val[1]?'</div>':'<div align="center">').$val[5]);
}
public function left($val){
return nl2br(($val[1]?'</div>':'<div align="left">').$val[5]);
}
public function right($val){
return nl2br(($val[1]?'</div>':'<div align="right">').$val[5]);
}
public function justify($val){
return nl2br(($val[1]?'</div>':'<div align="justify">').$val[5]);
}
public function b($val){
return nl2br('<'.$val[1].'b>'.$val[5]);
}
public function i($val){
return nl2br('<'.$val[1].'i>'.$val[5]);
}
public function u($val){
return nl2br('<'.$val[1].'u>'.$val[5]);
}
public function s($val){
return nl2br('<'.$val[1].'s>'.$val[5]);
}
public function color($val){
return nl2br(($val[1]?'</span>':'<span style="color:'.$val[4].'">').$val[5]);
}
public function url($val){
return nl2br(($val[1]?'</a>':'<a href="'.($val[4]?:$val[5]).'">').$val[5]);
}
public function img($val){
return nl2br($val[1]?$val[5]:'<img alt="'.($val[4]?:$this->descr).'" title="'.($val[4]?:$this->descr).'" src="'.$val[5].'" />');
}
public function code($val){
$this->code=($val[1]?false:true);
return($val[1]?'</code></pre>'.nl2br($val[5]):'<pre class="prettyprint"><code>'.$val[5]);
}
public function hide($val){
if($this->config['hide']){
return nl2br($val[5]);
}else{
$this->hide=($val[1]?false:true);
return nl2br($val[1]?$val[5]:$val[4]);
}
}
public function hr($val){
array_pop($this->bb);
return'<hr/>'.$val[5];
}
public function key($val){
array_pop($this->bb);
return'<span class="bbkey">'.$val[4].'</span>'.$val[5];
}
public function audio($val){
return nl2br(($val[1]?'</div>':'<div class="i-play" data-mp3="'.$val[4].'">').$val[5]);
}
public function spoiler($val){
return nl2br(($val[1]?'</div></div>':'<div class="spoiler"><div class="spoiler_title i-right-hand"> '.($val[4]?:'spoiler').'</div><div class="spoiler_text">').$val[5]);
}
public function email($val){
return nl2br(($val[1]?'</a>':'<a href="mailto:'.$val[4].'">').$val[5]);
}
public function quote($val){
return nl2br(($val[1]?'</quote>':'<quote><h4>Источник/Source: '.$val[4].'</h4>').$val[5]);
}
public function youtube($val){
array_pop($this->bb);
if(preg_match("(v=([w-]{11})|([w-]{11}))",$val[4],$k)){
return '<object><embed src="http://youtube.com/v/'.($k[1]?:$k[2]).'?version=3" type="application/x-shockwave-flash" width="'.$this->config['videoW'].'" height="'.$this->config['videoH'].'"></embed></object>'.nl2br($val[5]);
}else{
return nl2br($val[0]);
}
}
public function font($val){
if($val[1]){
return '</span>'.nl2br($val[5]);
}else{
$font='';
if(isset($this->config['font'][$val[4]])){
$font=$this->config['font'][$val[4]];
return ($font?'<style>@font-face{font-family:"'.$val[4].'";src: url("'.$font.'");}</style>':'').'<span style="font-family:'.$val[4].'">'.nl2br($val[5]);
}else{
return '<span>'.nl2br($val[5]);
}
}
}
public function size($val){
return ($val[1]?'</span>':(in_array($val[4],$this->config['size'])?'<span style="font-size:'.$val[4].'px">':'<span>')).nl2br($val[5]);
}
public function parse($text,$descr=''){
$text=htmlspecialchars(trim($text),ENT_QUOTES);//защита
$this->descr=$descr;//описание для тега картинок
if(preg_match("(^([^[]]*)[(/|)[a-z]+(|=(.*?))])ui",$text,$d1)){//проверяем нужно ли парсить и заодно забираем весь текст до первого бб кода
preg_match_all("([(/|)([a-z]+)(|=(.*?))]([^[]]*))ui",$text,$d2,PREG_SET_ORDER);//разбираем текст на части
$text=nl2br($d1[1]);//вставляем текст который нашли в начале
foreach($d2 as $val){
if(!in_array($val[2],$this->bbcode)){//проверяем разрешон ли бб код
$text.=nl2br($val[0]);
continue;
}
if($this->code){//нужно для бб кода code
if(!$val[1] or $val[2]!='code'){
$text.=$val[0];
continue;
}
}
if($this->hide){//нужно для бб кода hide
if(!$val[1] or $val[2]!='hide'){
continue;
}
}
if($val[1]){//проверка на закрытый бб код
if(!$this->bb or end($this->bb)!=$val[2]){//проверка был ли открыт бб код
$text.=nl2br($val[0]);
continue;
}
array_pop($this->bb);
}else{
$this->bb[]=$val[2];
}
$text.=$this->$val[2]($val);//метод
}
foreach($this->bb as $val){//закрываем не закрытые
$text.=$this->$val(['','/',$val,'','','']);
}
}
return $text;
}
}
Использовать его можно так:
$text = '[center]
[b]Тест текста[/b]
[color=#ff0000]Красный!!![/color]
[/center]';
$bb = new bb();
echo $bb->parse($text,'Тестовый образец');