ITDumka
BB parser или парсер bb-кодов

Когда-то я хотел написать блог и выложить его исходы. Блог я написал, а исходы - зажал. Так вот, это всё давно устарело и можно спокойно открыть карты. Интересующиеся наконец увидят на каком говнокоде работает это блог, но тем не менее это работает и будет работать, если никто не будет ничего ломать.

Сегодня выложу свой парсер bb-кодов, который конечно же не совершенен и, я думаю, многим это будет заметно. Но в мире еще много людей, которым это может пригодиться.

Итак, тупо кидаю исходы обычного bb парсера. Это почти стандартное решение на конечном автомате, который я тоже кидаю. Заметьте, кто знает, что FSM очень похож на пировский ;). Конечно этот парсер не xbb или еще там что, но он тоже может работать. Кому особо интересно, можно протестить скорость по сравнению с другими парсерами. Еще хочется сказать, что весь html код сделан тупо под убогую вёрстку этого блога, так что не надо меня сильно пилить за эти, да и за все остальные бяки ибо я уже не тот.

Вот сам парсер. Он расширяется объектами, каждый из который обрабатывает конкретный bb-код. Эти объекты должны быть способны описать интерфейс BBDecoder, что и не особо то проверяется даже, но всё же зачатки можно наблюдать.


Listing №1 (PHP)
  1. <?php
  2. /**
  3.  *
  4.  * @package BBParser.php
  5.  * @version 2.4.0
  6.  * @author Gerasimov Konstantin
  7.  * @copyright Copyright (c) 2008-2010, Gerasimov Konstantin
  8.  * @license LGPL
  9.  */
  10. interface BBDecoder {
  11.   /**
  12.    * Возвращает начало обрамления
  13.    * @return string
  14.    */
  15.   public function GetOpenHTML();
  16.   /**
  17.    * Возвращает конец обрамления
  18.    * @return string
  19.    */
  20.   public function GetCloseHTML();
  21.   /**
  22.    * Возвращает TRUE если желает владеть кодом, иначе FALSE
  23.    * @return bool
  24.    */
  25.   public function NeedContainCode();
  26.   /**
  27.    * Добавляет код во владение
  28.    */
  29.   public function AddCode($sCodeString);
  30.   /**
  31.    * Возвращает TRUE если BB-код корректен, иначе FALSE
  32.    */
  33.   public function Validate();
  34. }
  35. /**
  36.  * Обработчик исключений при работе с BBParser
  37.  */
  38. class BBParserException extends Exception {}
  39. /**
  40.  * Класс парсинга
  41.  */
  42. class BBParser {
  43.   /**
  44.    * @var FSM конечный автомат
  45.    */
  46.   private $_FSM;
  47.   /**
  48.    * @var string входной текст
  49.    */
  50.   private $_sInText;
  51.   /**
  52.    * @var int длинна входного текста
  53.    */
  54.   private $_iInTextCount;
  55.   /**
  56.    * @var string выходной текст
  57.    */
  58.   private $_sOutText;
  59.   /**
  60.    * @var string
  61.    */
  62.   private $_sCurrBB;
  63.   /**
  64.    * @var array
  65.    */
  66.   private $_arBBStack;
  67.   /**
  68.    * @var int
  69.    */
  70.   private $_iCurrBBContainStackIndex;
  71.   /**
  72.    * @var array
  73.    */
  74.   private $_arBBCodes;
  75.   /**
  76.    * @var string
  77.    */
  78.   private $_sCharset;
  79.   /**
  80.    * @var bool
  81.    */
  82.   private $_Deb;
  83.   /**
  84.    * @var string
  85.    */
  86.   private $_DebS;
  87.   /**
  88.    * @param array $arAvailableBBCodes
  89.    */
  90.   public function __construct($arAvailableBBCodes = array('B', 'I', 'IMG', 'URL', 'QUOTE', 'CODE', 'ALIGN', 'SIZE', 'COLOR', 'LABEL', 'LIST', 'ENDOFNODEPREVIEW'))
  91.   {
  92.     $this->_sCharset = 'UTF-8';
  93.     /*Иницилизируем FSM*/
  94.     $this->_FSM = new FSM('0');
  95.     /**
  96.      * Состояния:
  97.      * 0 - Ожидаем что угодно.
  98.      * 1 - Ожидаем символ BB-кода или "/" (только что начался BB-код символом "[")
  99.      * 2 - Ожидаем символ BB-кода
  100.      * 3 - Ожидаем "]"
  101.      */
  102.     //Вход по умолчанию
  103.     $this->_FSM->SetDefaultTransition(array($this, 'AddTextChar'), '0');
  104.     //Вход: [
  105.     $this->_FSM->AddTransition('[', '0', array($this, 'StartBB'), '1');
  106.     $this->_FSM->AddTransition('[', '1', array($this, 'BreakBB'), '0');
  107.     $this->_FSM->AddTransition('[', '2', array($this, 'BreakBB'), '0');
  108.     $this->_FSM->AddTransition('[', '3', array($this, 'BreakBBThenStartBB'), '1');
  109.     //Вход: /
  110.     $this->_FSM->AddTransition('/', '0', array($this, 'AddTextChar'), '0');
  111.     $this->_FSM->AddTransition('/', '1', array($this, 'AddBBChar'), '2');
  112.     $this->_FSM->AddTransition('/', '2', array($this, 'AddBBChar'), '2');
  113.     $this->_FSM->AddTransition('/', '3', array($this, 'AddBBChar'), '3');
  114.     //Вход: ]
  115.     $this->_FSM->AddTransition(']', '0', array($this, 'AddTextChar'), '0');
  116.     $this->_FSM->AddTransition(']', '1', array($this, 'BreakBB'), '0');
  117.     $this->_FSM->AddTransition(']', '2', array($this, 'BreakBB'), '0');
  118.     $this->_FSM->AddTransition(']', '3', array($this, 'EndBB'), '0');
  119.     //Вход: любой символ кроме "[", "]" и "/"
  120.     $this->_FSM->AddDefaultTransition('0', array($this, 'AddTextChar'), '0');  
  121.     $this->_FSM->AddDefaultTransition('1', array($this, 'AddBBChar'), '3');  
  122.     $this->_FSM->AddDefaultTransition('2', array($this, 'AddBBChar'), '3');  
  123.     $this->_FSM->AddDefaultTransition('3', array($this, 'AddBBChar'), '3');
  124.    
  125.     //Доступные BB-коды
  126.     $this->_arBBCodes = $arAvailableBBCodes;
  127.     //debug
  128.     $this->_Deb = FALSE;
  129.     $this->_DebS = '';
  130.   }
  131.   /**
  132.    * Устанавливает кодировку
  133.    * @param string $sNewCharset
  134.    */
  135.   public function SetCharset($sNewCharset)
  136.   {
  137.     if ($sNewCharset != '') {
  138.       $this->_sCharset = $sNewCharset;
  139.     }
  140.   }
  141.   /**
  142.    * Добавляет символ текста
  143.    * @param string $sChar
  144.    */
  145.   public function AddTextChar($sChar)
  146.   {
  147.     if ($this->_iCurrBBContainStackIndex === -1) {
  148.       $this->_sOutText .= htmlspecialchars($sChar, ENT_QUOTES, $this->_sCharset);
  149.     }
  150.     else {
  151.       $this->_arBBStack[$this->_iCurrBBContainStackIndex]['BBDecoder']->AddCode($sChar);
  152.     }
  153.   }
  154.   /**
  155.    * Стартует формирвание BB-кода
  156.    * @param  string $sChar
  157.    */
  158.   public function StartBB($sChar)
  159.   {
  160.     $this->_sCurrBB = $sChar;
  161.   }
  162.   /**
  163.    * Добавляет символ BB-кода
  164.    * @param string $sChar
  165.    */
  166.   public function AddBBChar($sChar)
  167.   {
  168.     $this->_sCurrBB .= $sChar;
  169.   }
  170.   /**
  171.    * Ошибка синтаксиса BB-кода
  172.    * @param string $sChar
  173.    */
  174.   public function BreakBB($sChar)
  175.   {
  176.     $this->AddTextChar($this->_sCurrBB . $sChar);
  177.     $this->_sCurrBB = '';
  178.   }
  179.   /**
  180.    * Завершение формирования BB-кода
  181.    * @param string $sChar
  182.    */
  183.   public function EndBB($sChar)
  184.   {
  185.     $this->_sCurrBB .= $sChar;
  186.     $this->_ProcessBB($this->_sCurrBB);
  187.   }
  188.   /**
  189.    * Ошибка синтаксиса и инициализация начала BB-кода
  190.    * @param string $sChar
  191.    */
  192.   public function BreakBBThenStartBB($sChar)
  193.   {
  194.     $this->BreakBB('');
  195.     $this->StartBB($sChar);
  196.   }
  197.   /**
  198.    * Возвращает декодированный HTML текст
  199.    * @param string $sInText исходный текст
  200.    * @return string HTML текст
  201.    */
  202.   public function GetHTML($sInText)
  203.   {
  204.     BB_CODE::ClearListingNumbers();
  205.     $this->_sOutText = '';
  206.     $this->ParseText($sInText);
  207.     $this->_sOutText = '<div class="node_block">' . $this->_UpdateEndsOfLines($this->_sOutText) . '</div>';
  208.     return  $this->_sOutText;
  209.   }
  210.   /**
  211.    * @param string $sInText
  212.    * @return string
  213.    */
  214.   public function ParseText($sInText) {
  215.     if ($sInText == '') {
  216.       $this->_sOutText = ' ';
  217.       return $this->_sOutText;
  218.     }
  219.     $this->_ClearVars();
  220.     $this->_sInText = $sInText;
  221.     $this->_iInTextCount = Str::Strlen($this->_sInText);
  222.     for($i = 0; $i < $this->_iInTextCount; $i++) {
  223.       $this->_FSM->GoStep(Str::Substr($this->_sInText, $i, 1));
  224.     }
  225.     $this->_CheckNotClosedBB();
  226.     return $this->_sOutText;
  227.   }
  228.   /**
  229.    * @param stirn $sText
  230.    * @return string
  231.    */
  232.   private function _UpdateEndsOfLines($sText)
  233.   {
  234.     $sText = str_replace("\r\n", "\n", $sText);
  235.     $sText = '<p>' . str_replace("\n", '</p><p>', $sText) . '</p>';
  236.     $sText = str_replace('<p></p>', '<br>', $sText);
  237.     $sText = str_replace('<p></div>', '</div><p>', $sText);
  238.     $sText = str_replace('<div class="node_block"></p>', '</p><div class="node_block">', $sText);
  239.     return $sText;
  240.   }
  241.   /**
  242.    * Очищает переменные для следующего преобразования
  243.    */
  244.   private function _ClearVars()
  245.   {
  246.     $this->_FSM->SetState('0');
  247.     $this->_Deb = FALSE;
  248.     $this->_DebS = '';
  249.     $this->_iInTextCount = 0;
  250.     $this->_sInText = '';
  251.     //$this->_sOutText = '';
  252.     $this->_sCurrBB = '';
  253.     $this->_arBBStack = array();
  254.     $this->_iCurrBBContainStackIndex = -1;
  255.   }
  256.   /**
  257.    * Вызывает соответсвтвующи функции обработки
  258.    * @param string $sBB
  259.    */
  260.   private function _ProcessBB($sBB)
  261.   {
  262.     if (Str::Substr($sBB, 1, 1) === '/') {
  263.       $this->_CloseBB(Str::Substr($sBB, 2, Str::Strlen($sBB) - 3));
  264.     }
  265.     else {
  266.       $this->_OpenBB(Str::Substr($sBB, 1, Str::Strlen($sBB) - 2));
  267.     }
  268.   }
  269.   /**
  270.    * Открывает BB-код
  271.    * @param string $sCleanBB
  272.    */
  273.   private function _OpenBB($sCleanBB)
  274.   {
  275.     if ($this->_iCurrBBContainStackIndex !== -1) {
  276.       $this->BreakBB('');
  277.       return;
  278.     }
  279.     $arBBHash = $this->_GetBBHash($sCleanBB);
  280.     if ($arBBHash['BBName'] === '') {
  281.       $this->BreakBB('');
  282.       return;
  283.     }
  284.     $BBName = 'BB_' . $arBBHash['BBName'];
  285.     $BB = new $BBName($arBBHash['BBParams']);
  286.     if (!$BB->Validate()) {
  287.       $this->BreakBB('');
  288.       return;
  289.     }
  290.     $BBIndex = array_push($this->_arBBStack, array('BBName'=>$arBBHash['BBName'], 'BBDecoder'=>$BB)) - 1;  
  291.     if ($BB->NeedContainCode()) {
  292.       $this->_StartBBContaining($BBIndex);
  293.     }
  294.     $this->_sOutText .= $BB->GetOpenHTML();
  295.   }
  296.   /**
  297.    * Закрывает BB-кодs
  298.    * @param string $sCleanBB
  299.    */
  300.   private function _CloseBB($sCleanBB)
  301.   {
  302.     $arBBHash = $this->_GetBBHash($sCleanBB);
  303.     if ($arBBHash['BBName'] === '') {
  304.       $this->BreakBB('');
  305.       return;
  306.     }
  307.     $arLastOpenBB  = array_pop($this->_arBBStack);
  308.     if (NULL !== $arLastOpenBB) {
  309.       if ($arLastOpenBB['BBName'] === $arBBHash['BBName']) {
  310.         $this->_sOutText .= $arLastOpenBB['BBDecoder']->GetCloseHTML();
  311.         $this->_EndBBContaining();
  312.         $arLastOpenBB['BBDecoder'] = NULL;
  313.       }
  314.       else {
  315.         array_push($this->_arBBStack, $arLastOpenBB);
  316.         $this->BreakBB('');
  317.       }
  318.     }
  319.   }
  320.   /**
  321.    * Возвращает хэш параметров BB-кода
  322.    * @param string BB-код
  323.    */
  324.   private function _GetBBHash($sCleanBB)
  325.   {
  326.     $arHash = array();
  327.     $arHash['BBName'] = $this->_GetBBName($sCleanBB);
  328.     $arHash['BBParams'] = $this->_GetBBParams($sCleanBB, Str::Strlen($arHash['BBName']));
  329.     return $arHash;
  330.   }
  331.   /**
  332.    * Возвращает имя BB-кода или пустое значени если такой BB-код не известен
  333.    * @param string $sCleanBB
  334.    * @return string
  335.    */
  336.   private function _GetBBName($sCleanBB)
  337.   {
  338.     $iCount = count($this->_arBBCodes);
  339.     for($i = 0; $i < $iCount; $i++) {
  340.       if (preg_match('/^('. $this->_arBBCodes[$i] . ')(?(?=[=]{1})(.*)|($))/ui', $sCleanBB)) {
  341.         return $this->_arBBCodes[$i];
  342.       }
  343.     }
  344.     return '';
  345.   }
  346.   /**
  347.    * Возваращет параметры BB-кода
  348.    * @param string $sCleanBB
  349.    * @param ing $iBBNameLength
  350.    */
  351.   private function _GetBBParams($sCleanBB, $iBBNameLength)
  352.   {
  353.     if (($iBBNameLength > 0) &&
  354.         (Str::Substr($sCleanBB, $iBBNameLength, 1) === '=') &&
  355.         (Str::Strlen(Str::Substr($sCleanBB, $iBBNameLength + 1)) != FALSE)) {
  356.       return Str::Substr($sCleanBB, $iBBNameLength + 1);
  357.     }
  358.     else {
  359.       return '';
  360.     }
  361.   }
  362.   /**
  363.    * Закрывает открытые BB-коды
  364.    */
  365.   private function _CheckNotClosedBB()
  366.   {
  367.     if (count($this->_arBBStack)) {
  368.       while (NULL !== ($arNeedCloseBB = array_pop($this->_arBBStack))) {
  369.         $this->_sOutText .= $arNeedCloseBB['BBDecoder']->GetCloseHTML();
  370.         $this->_EndBBContaining();
  371.         $arNeedCloseBB['BBDecoder'] = NULL;
  372.       }
  373.     }
  374.   }
  375.   /**
  376.    * Включает режим владения кодом BB-кода с индексом в стэке
  377.    * @param int $iBBStackIndex
  378.    */
  379.   private function _StartBBContaining($iBBStackIndex)
  380.   {
  381.     $this->_iCurrBBContainStackIndex = $iBBStackIndex;
  382.   }
  383.   /**
  384.    * Выключает режим владения кодом BB-кода
  385.    */
  386.   private function _EndBBContaining()
  387.   {
  388.     $this->_iCurrBBContainStackIndex = -1;   
  389.   }
  390.  
  391. }
  392. /**
  393.  * Класс обработки BB-кода [B] [/B]
  394.  * Без параметров
  395.  */
  396. class BB_B implements BBDecoder {
  397.   /**
  398.    *@see interface BBDecoder
  399.    *@return string
  400.    */
  401.   public function GetOpenHTML()
  402.   {
  403.     return '<b>';
  404.   }
  405.   /**
  406.    *@see interface BBDecoder
  407.    *@return string
  408.    */
  409.   public function GetCloseHTML()
  410.   {
  411.     return '</b>';
  412.   }
  413.   /**
  414.    * @see interface BBDecoder
  415.    * @return bool
  416.    */
  417.   public function NeedContainCode()
  418.   {
  419.     return FALSE;
  420.   }
  421.   /**
  422.    * @see interface BBDecoder
  423.    */
  424.   public function AddCode($sCodeString)
  425.   {
  426.   }
  427.   /**
  428.    * @see interface BBDecoder
  429.    * @return bool
  430.    */
  431.   public function Validate()
  432.   {
  433.     return TRUE;
  434.   }
  435. }
  436. /**
  437.  * Класс обработки BB-кода [I] [/I]
  438.  * Без параметров
  439.  */
  440. class BB_I implements BBDecoder {
  441.   /**
  442.    *@see interface BBDecoder
  443.    *@return string
  444.    */
  445.   public function GetOpenHTML()
  446.   {
  447.     return '<em>';
  448.   }
  449.   /**
  450.    *@see interface BBDecoder
  451.    *@return string
  452.    */
  453.   public function GetCloseHTML()
  454.   {
  455.     return '</em>';
  456.   }
  457.   /**
  458.    * @see interface BBDecoder
  459.    * @return bool
  460.    */
  461.   public function NeedContainCode()
  462.   {
  463.     return FALSE;
  464.   }
  465.   /**
  466.    * @see interface BBDecoder
  467.    */
  468.   public function AddCode($sCodeString)
  469.   {
  470.   }
  471.   /**
  472.    * @see interface BBDecoder
  473.    * @return bool
  474.    */
  475.   public function Validate()
  476.   {
  477.     return TRUE;
  478.   }
  479. }
  480.  
  481. /**
  482.  * Класс обработки BB-кода [IMG] [/IMG]
  483.  * Параметры: [IMG=ссылка_на_рисунок]подпись_рисунка[/IMG]
  484.  */
  485. class BB_IMG implements BBDecoder {
  486.   /**
  487.    * @var string
  488.    */
  489.   private $_sSrc;
  490.   /**
  491.    * @param string $sParams
  492.    */
  493.   public function __construct($sParams)
  494.   {
  495.     $this->_sSrc = $sParams;
  496.   }
  497.   /**
  498.    *@see interface BBDecoder
  499.    *@return string
  500.    */
  501.   public function GetOpenHTML()
  502.   {
  503.     return '<img src="' . $this->_sSrc . '" alt="';
  504.   }
  505.   /**
  506.    *@see interface BBDecoder
  507.    *@return string
  508.    */
  509.   public function GetCloseHTML()
  510.   {
  511.     return '">';
  512.   }
  513.   /**
  514.    * @see interface BBDecoder
  515.    * @return bool
  516.    */
  517.   public function NeedContainCode()
  518.   {
  519.     return FALSE;
  520.   }
  521.   /**
  522.    * @see interface BBDecoder
  523.    * @return bool
  524.    */
  525.   public function Validate()
  526.   {
  527.     return (preg_match('/^[^<>"]+$/iu', $this->_sSrc));
  528.   }
  529.   /**
  530.    * @see interface BBDecoder
  531.    */
  532.   public function AddCode($sCodeString)
  533.   {
  534.   }
  535. }
  536. /**
  537.  * Класс обработки BB-кода [URL] [/URL]
  538.  * Параметры [URL=ссылка]надпись_ссылки[/URL]
  539.  */
  540. class BB_URL implements BBDecoder {
  541.   /**
  542.    * @var string
  543.    */
  544.   private $_sParams;
  545.   /**
  546.    * @var string
  547.    */
  548.   private $_sURL;
  549.   /**
  550.    * @param string $sParams
  551.    */
  552.   public function __construct($sParams)
  553.   {
  554.     $this->_sParams = $sParams;
  555.     $AncorStr    = '';
  556.     $UrlStr      = $this->_sParams;
  557.     if(FALSE !== $AncorPos = Str::Strpos($this->_sParams, '#')) {
  558.       $AncorStr = Str::Substr($this->_sParams, $AncorPos + 1, Str::Strlen($this->_sParams) - $AncorPos);
  559.       $UrlStr   = Str::Substr($this->_sParams, 0, $AncorPos);
  560.     }
  561.     $this->_sURL = (($UrlStr) ? urldecode($UrlStr) : '') . (($AncorStr) ? '#' . urldecode(Str::Strtolower($AncorStr)) : '');
  562.   }
  563.   /**
  564.    *@see interface BBDecoder
  565.    *@return string
  566.    */
  567.   public function GetOpenHTML()
  568.   {
  569.     return '<a href="' . $this->_sURL  . '"' . ((Str::Substr($this->_sURL, 0, 1) == '#') ? ' target="_self"' : ' target="_blank"') . '>';
  570.   }
  571.   /**
  572.    *@see interface BBDecoder
  573.    *@return string
  574.    */
  575.   public function GetCloseHTML()
  576.   {
  577.     return '</a>';
  578.   }
  579.   /**
  580.    * @see interface BBDecoder
  581.    * @return bool
  582.    */
  583.   public function NeedContainCode()
  584.   {
  585.     return FALSE;
  586.   }
  587.   /**
  588.    * @see interface BBDecoder
  589.    * @return bool
  590.    */
  591.   public function Validate()
  592.   {
  593.     return (preg_match('#^[^<>"]+$#iu', $this->_sParams));
  594.   }
  595.   /**
  596.    * @see interface BBDecoder
  597.    */
  598.   public function AddCode($sCodeString)
  599.   {
  600.   }
  601. }
  602. /**
  603.  * Класс обработки BB-кода [QUOTE] [/QUOTE]
  604.  * Параметры: [QUOTE=имя_комментриуемого]текст_комментария[/QUOTE]
  605.  */
  606. class BB_QUOTE implements BBDecoder {
  607.   /**
  608.    * @var string
  609.    */
  610.   private $_sName;
  611.   /**
  612.    * @param string $sParams
  613.    */
  614.   public function  __construct($sParams)
  615.   {
  616.     $this->_sName = $sParams;
  617.   }
  618.   /**
  619.    *@see interface BBDecoder
  620.    *@return string
  621.    */
  622.   public function GetOpenHTML()
  623.   {
  624.     return '<div class="quote"><div class="quote_title">' . htmlspecialchars($this->_sName , ENT_QUOTES, 'UTF-8') . '</div>';
  625.   }
  626.   /**
  627.    *@see interface BBDecoder
  628.    *@return string
  629.    */
  630.   public function GetCloseHTML()
  631.   {
  632.     return '</div>';
  633.   }
  634.   /**
  635.    * @see interface BBDecoder
  636.    * @return bool
  637.    */
  638.   public function NeedContainCode()
  639.   {
  640.     return FALSE;
  641.   }
  642.   /**
  643.    * @see interface BBDecoder
  644.    * @return bool
  645.    */
  646.   public function Validate()
  647.   {
  648.     if(trim($this->_sName) !== '') {
  649.       return TRUE;
  650.     }
  651.     else {
  652.       return FALSE;
  653.     }
  654.   }
  655.   /**
  656.    * @see interface BBDecoder
  657.    */
  658.   public function AddCode($sCodeString)
  659.   {
  660.   }
  661. }
  662. /**
  663.  * Класс обработки BB-кода [CODE] [слешьCODE]
  664.  * Параметры: [CODE=имя_языка]текст_программы[слешьCODE]
  665.  */
  666. class BB_CODE implements BBDecoder {
  667.   /**
  668.    * @var int
  669.    */
  670.   private static $_iListingNumber = 0;
  671.   /**
  672.    * @var string
  673.    */
  674.   private $_sCodeName;
  675.   /**
  676.    * @var string
  677.    */
  678.   private $_sCode;
  679.   /**
  680.    * @var array
  681.    */
  682.   private $_arAvailableCodes;
  683.   /**
  684.    * @param string $sParams
  685.    */
  686.   public function  __construct($sParams)
  687.   {
  688.     $this->_sCodeName = $sParams;
  689.     $this->_sCode = '';
  690.     $this->_arAvailableCodes = array('unknown'       => 'unknown',
  691.                                      'php'           => 'PHP',
  692.                                      'python'        => 'Python',
  693.                                      'perl'          => 'Perl',
  694.                                      'javascript'    => 'JavaScript',
  695.                                      'actionscript3' => 'ActionScript',
  696.                                      'html4strict'   => 'HTML',
  697.                                      'css'           => 'CSS',
  698.                                      'xml'           => 'XML',
  699.                                      'apache'        => 'Apache',
  700.                                      'sql'           => 'SQL',
  701.                                      'c'             => 'C',
  702.                                      'cpp'           => 'C++',
  703.                                      'csharp'        => 'C#',
  704.                                      'delphi'        => 'Delphi',
  705.                                      'pascal'        => 'Pascal',
  706.                                      'ruby'          => 'Ruby',
  707.                                      'rails'         => 'Rails',
  708.                                      'java5'         => 'Java5',
  709.                                      'java'          => 'Java',
  710.                                      'smalltalk'     => 'Smalltalk',
  711.                                      'vb'            => 'VisualBasic',
  712.                                      'vbnet'         => 'VisualBasic(.Net)',
  713.                                      'visualfoxpro'  => 'VisualFoxPro',
  714.                                      'asm'           => 'Asm'
  715.                                     );
  716.   }
  717.   /**
  718.    *@see interface BBDecoder
  719.    *@return string
  720.    */
  721.   public function GetOpenHTML()
  722.   {
  723.     self::$_iListingNumber++;
  724.     return '<div class="code"><a name="listing' .
  725.                self::$_iListingNumber .
  726.              '"></a><div class="code_title">Listing №' .
  727.            self::$_iListingNumber .
  728.              ' (' .
  729.            htmlspecialchars($this->_arAvailableCodes[$this->_sCodeName], ENT_QUOTES, 'UTF-8') .
  730.              ')</div>';
  731.   }
  732.   /**
  733.    *@see interface BBDecoder
  734.    *@return string
  735.    */
  736.   public function GetCloseHTML()
  737.   {
  738.     return $this->_GetHighlightCode() . '</div>';
  739.   }
  740.   /**
  741.    * @see interface BBDecoder
  742.    * @return bool
  743.    */
  744.   public function NeedContainCode()
  745.   {
  746.     return TRUE;
  747.   }
  748.   /**
  749.    * @see interface BBDecoder
  750.    */
  751.   public function AddCode($sCodeString)
  752.   {
  753.     $this->_sCode .= $sCodeString;
  754.   }
  755.   /**
  756.    * @see interface BBDecoder
  757.    * @return bool
  758.    */
  759.   public function Validate()
  760.   {
  761.     if (preg_match('/^[0-9A-Z]+$/ui', $this->_sCodeName)) {
  762.       return (array_key_exists(Str::Strtolower($this->_sCodeName), $this->_arAvailableCodes));
  763.     }
  764.     return FALSE;
  765.   }
  766.   /**
  767.    * Выполняет подсветку синтаксиса
  768.    * @return string
  769.    */
  770.   private function _GetHighlightCode()
  771.   {
  772.     if ((Str::Strlen($this->_sCode) < MAX_HIGHLIGHT_BUFFER)) {
  773.       require_once GESHI_ROOT . 'geshi' . PHP_EXT;
  774.       $Highlighter = new GeSHi($this->_sCode, $this->_sCodeName);
  775.       $Highlighter->set_header_type(GESHI_HEADER_DIV);
  776.       $Highlighter->enable_line_numbers(GESHI_NORMAL_LINE_NUMBERS);
  777.       $Highlighter->set_tab_width(2);
  778.       $Highlighter->set_encoding('UTF-8');
  779.       $sCode = $Highlighter->parse_code();
  780.       unset($Highlighter);
  781.       return $sCode;
  782.     }
  783.     else {
  784.       return htmlspecialchars($this->_sCode, ENT_QUOTES, 'UTF-8');
  785.     }
  786.    
  787.   }
  788.   /**
  789.    * Очищает счетчик листингов
  790.    */
  791.   public static function ClearListingNumbers()
  792.   {
  793.     self::$_iListingNumber = 0;
  794.   }
  795. }
  796. /**
  797.  * Класс обработки BB-кода [ALIGN] [/ALIGN]
  798.  * Параметры: [ALIGN=выравнивание]текст[/ALIGN]
  799.  */
  800. class BB_ALIGN implements BBDecoder {
  801.   /**
  802.    * @var string
  803.    */
  804.   private $_sAligment;
  805.   /**
  806.    * @param string $sParams
  807.    */
  808.   public function  __construct($sParams)
  809.   {
  810.     $this->_sAligment = $sParams;
  811.   }
  812.   /**
  813.    *@see interface BBDecoder
  814.    *@return string
  815.    */
  816.   public function GetOpenHTML()
  817.   {
  818.     if (strtolower($this->_sAligment) === 'center') {
  819.       return '</div><div style="text-align: ' . $this->_sAligment . '; width: auto;   border: 0;">';
  820.     }
  821.     else {
  822.       return '</div><div style="text-align: ' . $this->_sAligment . ';float: ' . $this->_sAligment . ';">';
  823.     }
  824.   }
  825.   /**
  826.    *@see interface BBDecoder
  827.    *@return string
  828.    */
  829.   public function GetCloseHTML()
  830.   {
  831.     return '</div><div class="node_block">';
  832.   }
  833.   /**
  834.    * @see interface BBDecoder
  835.    * @return bool
  836.    */
  837.   public function NeedContainCode()
  838.   {
  839.     return FALSE;
  840.   }
  841.   /**
  842.    * @see interface BBDecoder
  843.    * @return bool
  844.    */
  845.   public function Validate()
  846.   {
  847.     if((Str::Strtolower($this->_sAligment) === 'right') ||
  848.        (Str::Strtolower($this->_sAligment) === 'left')  ||
  849.        (Str::Strtolower($this->_sAligment) === 'center')) {
  850.       return TRUE;
  851.     }
  852.     else {
  853.       return FALSE;
  854.     }
  855.   }
  856.   /**
  857.    * @see interface BBDecoder
  858.    */
  859.   public function AddCode($sCodeString)
  860.   {
  861.   }
  862. }
  863. /**
  864.  * Класс обработки BB-кода [SIZE] [/SIZE]
  865.  * Параметры: [SIZE=размер]текст[/SIZE]
  866.  */
  867. class BB_SIZE implements BBDecoder {
  868.   /**
  869.    * @var string
  870.    */
  871.   private $_fHeight;
  872.   /**
  873.    * @param string $sParams
  874.    */
  875.   public function  __construct($sParams)
  876.   {
  877.     $this->_fHeight = $sParams;
  878.   }
  879.   /**
  880.    *@see interface BBDecoder
  881.    *@return string
  882.    */
  883.   public function GetOpenHTML()
  884.   {
  885.     return '<div style="font-size: ' . $this->_fHeight . 'em;">';
  886.   }
  887.   /**
  888.    *@see interface BBDecoder
  889.    *@return string
  890.    */
  891.   public function GetCloseHTML()
  892.   {
  893.     return '</div>';
  894.   }
  895.   /**
  896.    * @see interface BBDecoder
  897.    * @return bool
  898.    */
  899.   public function NeedContainCode()
  900.   {
  901.     return FALSE;
  902.   }
  903.   /**
  904.    * @see interface BBDecoder
  905.    * @return bool
  906.    */
  907.   public function Validate()
  908.   {
  909.     if (is_numeric($this->_fHeight) && ($this->_fHeight > 0) && ($this->_fHeight < 5)) {
  910.       return TRUE;
  911.     }
  912.     else {
  913.       return FALSE;
  914.     }
  915.   }
  916.   /**
  917.    * @see interface BBDecoder
  918.    */
  919.   public function AddCode($sCodeString)
  920.   {
  921.   }
  922. }
  923.  
  924. /**
  925.  * Класс обработки BB-кода [COLOR] [/COLOR]
  926.  * Параметры: [COLOR=#00fa000]текст[/COLOR]
  927.  */
  928. class BB_COLOR implements BBDecoder {
  929.   /**
  930.    * @var array
  931.    */
  932.   private $_arColors;
  933.   /**
  934.    * @var string
  935.    */
  936.   private $_sColor;
  937.   /**
  938.    * @param string $sParams
  939.    */
  940.   public function  __construct($sParams)
  941.   {
  942.     $this->_sColor = $sParams;
  943.     $this->_arColors = array('red'   => '#FF0000',
  944.                              'green' => '#00FF00',
  945.                              'blue'  => '#0000FF',
  946.                              'black' => '#000000',
  947.                              'yellow'=> '#FFFF00',
  948.                              'gray'  => '#808080',
  949.                              'white' => '#FFFFFF');
  950.   }
  951.   /**
  952.    *@see interface BBDecoder
  953.    *@return string
  954.    */
  955.   public function GetOpenHTML()
  956.   {
  957.     if (array_key_exists(Str::Strtolower($this->_sColor), $this->_arColors)) {
  958.       return '<span style="color: ' . $this->_arColors[$this->_sColor] . ';">';
  959.     }
  960.     else {
  961.       return '<span style="color: ' . $this->_sColor . ';">';
  962.     }
  963.   }
  964.   /**
  965.    *@see interface BBDecoder
  966.    *@return string
  967.    */
  968.   public function GetCloseHTML()
  969.   {
  970.     return '</span>';
  971.   }
  972.   /**
  973.    * @see interface BBDecoder
  974.    * @return bool
  975.    */
  976.   public function NeedContainCode()
  977.   {
  978.     return FALSE;
  979.   }
  980.   /**
  981.    * @see interface BBDecoder
  982.    * @return bool
  983.    */
  984.   public function Validate()
  985.   {
  986.     if (array_key_exists(Str::Strtolower($this->_sColor), $this->_arColors) || (preg_match('/[#][0-9a-f]{1,6}/iu', $this->_sColor))) {
  987.       return TRUE;
  988.     }
  989.     else {
  990.       return FALSE;
  991.     }
  992.   }
  993.   /**
  994.    * @see interface BBDecoder
  995.    */
  996.   public function AddCode($sCodeString)
  997.   {
  998.   }
  999. }
  1000. /**
  1001.  * Класс обработки BB-кода [LABEL] [/LABEL]
  1002.  * Параметры: [LABEL=имя метки][/LABEL]
  1003.  */
  1004. class BB_LABEL implements BBDecoder {
  1005.   /**
  1006.    * @var string
  1007.    */
  1008.   private $_LabelName;
  1009.  
  1010.   public function __construct($Params)
  1011.   {
  1012.     $this->_LabelName = $Params;
  1013.   }
  1014.   /**
  1015.    * @see interface BBDecoder
  1016.    */
  1017.   public function GetOpenHTML()
  1018.   {
  1019.     return '<a name="' . Str::Strtolower($this->_LabelName) . '"></a>';
  1020.   }
  1021.   /**
  1022.    * @see interface BBDecoder
  1023.    */
  1024.   public function GetCloseHTML()
  1025.   {
  1026.     return '';
  1027.   }
  1028.   /**
  1029.    * @see interface BBDecoder
  1030.    */
  1031.   public function NeedContainCode()
  1032.   {
  1033.     return FALSE;
  1034.   }
  1035.   /**
  1036.    * @see interface BBDecoder
  1037.    */
  1038.   public function AddCode($sCodeString)
  1039.   {
  1040.  
  1041.   }
  1042.   /**
  1043.    * @see interface BBDecoder
  1044.    */
  1045.   public function Validate()
  1046.   {
  1047.     return (preg_match('#^[_a-z0-9-]+(\s{1}[_a-z0-9-]+)*$#iu', $this->_LabelName));
  1048.   }
  1049.    
  1050. }
  1051. /**
  1052.  * Класс обработки BB-кода [LIST] [/LIST]
  1053.  * Параметры: [LIST=тип списка][/LIST]
  1054.  */
  1055. class BB_LIST implements BBDecoder {
  1056.   /**
  1057.    * @var strign
  1058.    */
  1059.   private $_Type;
  1060.   /**
  1061.    * @var strings
  1062.    */
  1063.   private $_sCode;
  1064.   /**
  1065.    * @params $sParams
  1066.    */
  1067.   public function __construct($sParams)
  1068.   {
  1069.     $this->_Type = $sParams;
  1070.     $this->_sCode = '';
  1071.   }
  1072.   /**
  1073.    * @see interface BBDecoder
  1074.    */
  1075.   public function GetOpenHTML()
  1076.   {
  1077.     return '<'. $this->_GetListType() .  ' class="list">';
  1078.   }
  1079.   /**
  1080.    * @see interface BBDecoder
  1081.    */
  1082.   public function GetCloseHTML()
  1083.   {
  1084.     $BBSubParser = new BBParser(array('B', 'I', 'URL', 'SIZE', 'COLOR'));
  1085.     $sParsedCode = $BBSubParser->ParseText($this->_sCode);
  1086.     unset($BBSubParser);
  1087.     $sParsedCode = preg_replace("/\r\n|\n/u", "\n", $sParsedCode);
  1088.     $sParsedCode = preg_replace("/\n/u", '</li><li class="list_item">', '<li class="list_item">' . $sParsedCode . '</li>');
  1089.     $sParsedCode = preg_replace('#<li class="list_item"></li>#u', '', $sParsedCode) . '</' . $this->_GetListType(). '>';
  1090.     return $sParsedCode . "\n";
  1091.   }
  1092.   /**
  1093.    * @see interface BBDecoder
  1094.    */
  1095.   public function NeedContainCode()
  1096.   {
  1097.     return TRUE;
  1098.   }
  1099.   /**
  1100.    * @see interface BBDecoder
  1101.    */
  1102.   public function AddCode($sCodeString)
  1103.   {
  1104.     $this->_sCode .= $sCodeString;
  1105.   }
  1106.   /**
  1107.    * @see interface BBDecoder
  1108.    */
  1109.   public function Validate()
  1110.   {
  1111.     return ((Str::Strtolower($this->_Type) === 'numbers') || (Str::Strtolower($this->_Type) === 'points'));
  1112.   }
  1113.   /**
  1114.    * @return string
  1115.    */
  1116.   private function _GetListType()
  1117.   {
  1118.     if(Str::Strtolower($this->_Type) === 'numbers') {
  1119.       return 'ol';
  1120.     }
  1121.     else {
  1122.       return 'ul';
  1123.     }
  1124.   }
  1125. }
  1126. /**
  1127.  * Класс обработки BB-кода [ENDOFNODEPREVIEW] [/ENDOFNODEPREVIEW]
  1128.  * Параметры(нету пока параметров): [ENDOFNODEPREVIEW][/ENDOFNODEPREVIEW]
  1129.  */
  1130. class BB_ENDOFNODEPREVIEW implements BBDecoder {
  1131.   /**
  1132.    * @see interface BBDecoder
  1133.    */
  1134.   public function __construct($sParams)
  1135.   {
  1136.   }
  1137.   /**
  1138.    * @see interface BBDecoder
  1139.    */
  1140.   public function GetOpenHTML()
  1141.   {
  1142.     return '';
  1143.   }
  1144.   /**
  1145.    * @see interface BBDecoder
  1146.    */
  1147.   public function GetCloseHTML()
  1148.   {
  1149.     return '';
  1150.   }
  1151.   /**
  1152.    * @see interface BBDecoder
  1153.    */
  1154.   public function NeedContainCode()
  1155.   {
  1156.     return FALSE;
  1157.   }
  1158.   /**
  1159.    * @see interface BBDecoder
  1160.    */
  1161.   public function AddCode($sCodeString)
  1162.   {
  1163.    
  1164.   }
  1165.   /**
  1166.    * @see interface BBDecoder
  1167.    */
  1168.   public function Validate()
  1169.   {
  1170.     return TRUE;
  1171.   }
  1172. }
  1173. ?>


А вот и сам конечный автомат:


Listing №2 (PHP)
  1. <?php
  2. /**
  3.  *
  4.  * @package FSM.php
  5.  * @version 1.1.0
  6.  * @author Gerasimov Konstantin
  7.  * @copyright Copyright (c) 2008-2010, Gerasimov Konstantin
  8.  * @license LGPL
  9.  *
  10.  */
  11. /**
  12.  * Обработчие ошибок при работе с FSM
  13.  */
  14. class FSMException extends Exception {}
  15. /**
  16.  * Класс описывает конечный автомат
  17.  * В качестве действий вызывает внешние функции
  18.  */
  19. class FSM {
  20.   /**
  21.    * @var array карта переходов
  22.    */
  23.   private $_arMap;
  24.   /**
  25.    * @var string исходное состояние
  26.    */
  27.   private $_sCurrState;
  28.   /**
  29.    * @var array переход по умолчанию
  30.    */
  31.   private $_arDefTransition;
  32.  
  33.   /**
  34.    * Конструктор
  35.    * @param string $sCurrState исходное состояние
  36.    */
  37.   public function __construct($sCurrState)
  38.   {
  39.     $this->_arMap = array();
  40.     $this->_arDefTransition = array();
  41.     $this->_sCurrState = $sCurrState;
  42.   }
  43.   /**
  44.    * Добавляет переход
  45.    * @param string $sInput вход
  46.    * @param string $sCurrState текущее состояние
  47.    * @param string $sAction имя вызываемой фукнции
  48.    * @param string $sNextState следующее состояние
  49.    */
  50.   public function AddTransition($sInput, $sCurrState, $sAction, $sNextState)
  51.   {
  52.     $this->_arMap[$sInput . ' ' . $sCurrState] = array($sAction, $sNextState);
  53.   }
  54.   /**
  55.    * Добавляет набор переходов
  56.    * @param array $sInput входы
  57.    * @param string $sCurrState текущее состояние
  58.    * @param string $sAction имя вызываемой фукнции
  59.    * @param string $sNextState следующее состояние
  60.    */
  61.   public function AddTransitions($arInputs, $sCurrState, $sAction, $sNextState)
  62.   {
  63.     foreach ($arInputs as $sInput) {
  64.       $this->AddTransition($sInput, $sCurrState, $sAction, $sNextState);
  65.     }
  66.   }
  67.   /**
  68.    * Устанавливает переход по умолчанию при известных состояниях
  69.    * @param string $sCurrState текущее состояние
  70.    * @param string $sAction имя вызываемой фукнции
  71.    * @param string $sNextState следующее состояние
  72.    *
  73.    */
  74.   public function AddDefaultTransition($sCurrState, $sAction, $sNextState)
  75.   {
  76.     $this->_arMap['' . $sCurrState] = array($sAction, $sNextState);
  77.   }
  78.   /**
  79.    * Устанавливет переход по умолчанию
  80.    */
  81.   public function SetDefaultTransition($sAction, $sNextState)
  82.   {
  83.     $this->_arDefTransition = array($sAction, $sNextState);
  84.   }
  85.   /**
  86.    * Шаг перехода
  87.    * @param string $sChar
  88.    */
  89.   public function GoStep($sChar)
  90.   {
  91.     $this->_Transit($sChar);
  92.   }
  93.   /**
  94.    * @param string $sState
  95.    */
  96.   public function SetState($sState)
  97.   {
  98.     $this->_sCurrState = $sState;
  99.   }
  100.   /**
  101.    * Производит переход из одного состояния в другое,
  102.    * вызывая соответсвующую пользовательскую функцию
  103.    * @param string $sInput
  104.    */
  105.   private function _Transit($sInput)
  106.   {
  107.     if(!empty($this->_arMap[$sInput . ' ' . $this->_sCurrState])) {
  108.       call_user_func($this->_arMap[$sInput . ' ' . $this->_sCurrState][0], $sInput);
  109.       $this->_sCurrState = $this->_arMap[$sInput . ' ' . $this->_sCurrState][1];
  110.     }
  111.     else {
  112.       if(!empty($this->_arMap['' . $this->_sCurrState])) {
  113.         call_user_func($this->_arMap['' . $this->_sCurrState][0], $sInput);
  114.         $this->_sCurrState = $this->_arMap['' . $this->_sCurrState][1];
  115.       }
  116.       else {
  117.         if(count($this->_arDefTransition) != 2) {
  118.           throw new FSMException(__METHOD__ . '=>Default transition is not set');
  119.         }
  120.         call_user_func($this->_arDefTransition[0], $sInput);
  121.         $this->_sCurrState = $this->_arDefTransition[1];
  122.       }
  123.     }
  124.   }
  125. }
  126. ?>


Пример использования довольно банален:


Listing №3 (PHP)
  1. <?php
  2. $sText = 'Привет мир!';
  3. $BBParser = new BBParser();
  4. echo $BBParser->GetHTML($sText);


Вообщем, если хотите - комментируйте, предлагайте свои варианты, что хотите, то и делайте. Любой комментарий парсится именно этим кодом.


Comments
By   555
Published   09.06.2010

ужасный говногодище

Published   09.06.2010

555
ужасный говногодище

а то, два года назад я только начинал. Зато концепт ничего так, мне и сейчас нравится!

By   555
Published   09.06.2010

это не два года назад. судя по названию методов и концепции, это максимум 3/4 года

два года назад ты только echo писал :)

Published   09.06.2010

Этот парсер написан до того, как появился блог в сети. Судя из даты первой статьи этому парсеру больше года. А начал изучать пых я года два назад.

Published   09.06.2010

Что написано пером, не вырубишь топором: уже тогда вроде всё было, кроме GESHI

By   555
Published   09.06.2010

ну год. но не два.

Published   09.06.2010

Ладно, давай сойдемся на год и два месяца ;)

Create comment
 
Formatting
Comment can not be edited. Please, use the button "Preview"
By
  (Enter prev char)
Comment
Categories
PHP
12
articles
Прочее
4
articles
Delphi
0
article
C/C++
0
article
C#
0
article
Java
0
article
Perl
0
article
Python
0
article
Enter
Cookie must be "ON"
Login
Password
 
Popular tags
PHP
9
articles
паттерн
5
articles
framework
5
articles
шаблон
5
articles
Template View
3
articles
Facade
3
articles
Service Stub
3
articles
Page Controller
2
articles
Singleton
2
articles
Gateway
2
articles
MySQL
2
articles
Registry
2
articles
Command
2
articles
Front Controller
2
articles
Action
2
articles
Abstract Factory
2
articles
типовое решение
2
articles
шаблоны проектирования
2
articles
Iterator
2
articles
Transaction Script
2
articles
Rambler's Top100 Правильный CSS!