Class парсера BB кодов

в 1:54, , рубрики: Песочница, метки: , ,

Перелопатил много движков и отдельных классов и все ради того чтоб узнать как люди решали проблему парсера 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,'Тестовый образец');

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js