2011-10-11 62 views
1

我一直在摔跤這一段時間。我知道這是很多代碼,但我不知道問題出在哪裏,似乎無法縮小它的範圍。我會賞賜它。問題與strtok()

我寫了這個類來解析bbcodes。它主要使用strtok(),除非你把兩個標籤緊挨着放在一起,否則這個類很好用,我不能爲了我的生活找出原因。

例如[b] [i]test1[/i] [/b]結果爲<strong> <em>test1</em> </strong>。 還有[b][i]test1[/i][/b]結果爲<strong>i]test1/b]</strong>。 最後一個</strong>標籤僅存在於此處,因爲解析器自動關閉標籤,因此無法在字符串中找到結束標籤。它完全錯過了[i][/b]標籤。

下面是類以及它用於設置各種bb代碼的一個子類。子類基本上只是一個沒有行爲的數據結構。

<?php 
    // beware images can contain any url/any get request. beware of csrf 
    class Lev_TextProcessor_Extension_BbCode { 

     protected $elements = array(); 
     protected $openTags = array(); 

     public function __construct() { 
      $this->elements['b'] = new Lev_TextProcessor_Extension_BbCode_Element('<strong>', '</strong>'); 
      $this->elements['i'] = new Lev_TextProcessor_Extension_BbCode_Element('<em>', '</em>'); 
      $this->elements['u'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="text-decoration: underline;">', '</span>'); 
      $this->elements['s'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="text-decoration: line-through;">', '</span>'); 
      $this->elements['size'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="font-size: ', '</span>', 'px;">'); 
      $this->elements['color'] = new Lev_TextProcessor_Extension_BbCode_Element('<span style="color: ', '</span>', ';">'); 
      $this->elements['center'] = new Lev_TextProcessor_Extension_BbCode_Element('<div style="text-align: center;">', '</div>', '', true, true, false); 
      $this->elements['url'] = new Lev_TextProcessor_Extension_BbCode_Element('<a href="', '</a>', '">'); 
      $this->elements['email'] = new Lev_TextProcessor_Extension_BbCode_Element('<a href="mailto:', '</a>', '">'); 
      $this->elements['img'] = new Lev_TextProcessor_Extension_BbCode_Element('<img src="', '" alt="" />', '', false, false, true); 
      $this->elements['youtube'] = new Lev_TextProcessor_Extension_BbCode_Element('<object width="400" height="325"><param name="movie" value="http://www.youtube.com/v/{param}"></param><embed src="http://www.youtube.com/v/', '" type="application/x-shockwave-flash" width="400" height="325"></embed></object>', '', false, false, false); 
      $this->elements['code'] = new Lev_TextProcessor_Extension_BbCode_Element('<pre><code>', '</code></pre>', '', true, false, false); 
     } 

     public function processText($input) { 
      // pre processing 
      $input = htmlspecialchars($input, ENT_NOQUOTES); 
      $input = nl2br($input); 
      $input = str_replace(array("\n", "\r"), '', $input); 
      // start main processing 
      $output = ''; 
      $allow_child_tags = true; 
      $allow_child_quotes = true; 

      $string_segment = strtok($input, '['); 

      do { 
       // check content for quotes 
       if ($allow_child_quotes === false) { 
        if (strpos($string_segment, '"') === false) { 
         $output .= $string_segment; 
        } 
       } else { 
        // add content to output 
        $output .= $string_segment; 
       } 

       $tag_contents = strtok(']'); 

       if (strpos($tag_contents, '/') === 0) { 
        // closing tag 
        $tag = substr($tag_contents, 1); 
        if (isset($this->elements[$tag]) === true && array_search($tag, $this->openTags) !== false) { 
         // tag found 
         do { 
          // close tags till matching tag found 
          $last_open_tag = array_pop($this->openTags); 
          $output .= $this->elements[$last_open_tag]->htmlAfter; 
         } while ($last_open_tag !== $tag); 
         $allow_child_tags = true; 
         $allow_child_quotes = true; 
        } 
       } else { 
        // opening tag 
        // separate tag name from argument if there is one 
        $equal_pos = strpos($tag_contents, '='); 
        if ($equal_pos === false) { 
         $tag_name = $tag_contents; 
        } else { 
         $tag_name = substr($tag_contents, 0, $equal_pos); 
         $tag_argument = substr($tag_contents, $equal_pos + 1); 
        } 
        if (isset($this->elements[$tag_name]) === true) { 
         // tag found 
         if (($this->elements[$tag_name]->allowParentTags === true || count($this->openTags) === 0) && $allow_child_tags === true) { 
          // add tag to open tag list and set flags 
          $this->openTags[] = $tag_name; 
          $allow_child_tags = $this->elements[$tag_name]->allowChildTags; 
          $allow_child_quotes = $this->elements[$tag_name]->allowChildQuotes; 
          $output .= $this->elements[$tag_name]->htmlBefore; 
          // if argument exists 
          if ($equal_pos !== false) { 
           if (strpos($tag_argument, '"') === false) { 
            $output .= $tag_argument; 
           } 
           $output .= $this->elements[$tag_name]->htmlCenter; 
          } 
         } 
        } 
       } 

       $string_segment = strtok('['); 
      } while ($string_segment !== false); 
      // close left over tags 
      while ($tag = array_pop($this->openTags)) { 
       $output .= $this->elements[$tag]->htmlAfter; 
      } 
      return $output; 
     } 
    } 
?> 

<?php 

    class Lev_TextProcessor_Extension_BbCode_Element { 

     public $htmlBefore; 
     public $htmlAfter; 
     public $htmlCenter; 
     public $allowChildQuotes; 
     public $allowChildTags; 
     public $allowParentTags; 

     public function __construct($html_before, $html_after, $html_center = '', $allow_child_quotes = true, $allow_child_tags = true, $allow_parent_tags = true) { 
      if ($allow_child_quotes === false && $allow_child_tags === true) throw new Lev_TextProcessor_Exception('You may not allow child tags if you do not allow child quotes.'); 
      $this->htmlBefore = $html_before; 
      $this->htmlAfter = $html_after; 
      $this->htmlCenter = $html_center; 
      $this->allowChildQuotes = $allow_child_quotes; 
      $this->allowChildTags = $allow_child_tags; 
      $this->allowParentTags = $allow_parent_tags; 
     } 
    } 
?> 

編輯

通過創建符號化下面的類固定。

<?php 

    // unlike PHP's strtok() function, this class will not skip over empty tokens. 
    class Lev_TextProcessor_Tokenizer { 

     protected $string; 

     public function __construct($string) { 
      $this->string = $string; 
     } 

     public function getToken($token) { 
      $segment_length = strcspn($this->string, $token); 
      $token = substr($this->string, 0, $segment_length); 
      $this->string = substr($this->string, $segment_length + 1); 
      return $token; 
     } 
    } 
?> 
+0

我得到了'b] test1/b]'而不是。 – alex

+0

@alex奇怪。也許這是因爲我在我的實際輸入測試字符串的開頭有一些空格。 – dqhendricks

+0

是的,在開始的空白給你的原始示例它無法正常工作。 – alex

回答

0

雖然我不認爲這是真的,看來這是我要了解我的一點的唯一途徑解決。

這可能是一種方式strtok()的作品,以獲得你想要的結果。

雖然不完美,但我能夠獲得的結果接近你用這個期待:

<? 
$data1 = strtok('[b][i]test1[/i][/b]','['); 
$data2 = strtok(']'); 
$data3 = strtok('['); 
$data4 = strtok(']'); 
$data5 = strtok('['); 
$data6 = strtok(']'); 
var_dump($data1, $data2,$data3, $data4, $data5, $data6); 
/* 
    OUTPUT 
    string(2) "b]" 
    string(1) "i" 
    string(5) "test1" 
    string(2) "/i" 
    string(3) "/b]" 
    bool(false) 
*/
?> 

正如我所說的它並不完美,但也許看到這將幫助你的方式來處理這個解。我個人從來沒有使用preg_match()這種類型的解析來處理BBCode。

+0

preg match的問題是這個[b] [i] [/ b] [/ i],嵌套不當。還有這[b],未封閉的標籤不會自動關閉。那就是速度。 – dqhendricks

+0

您的示例有助於縮小需要重現的代碼。似乎是跳過第一個'[',然後從那裏雪球問題。但爲什麼它跳過這樣的第一個字符? – dqhendricks

+0

我想我開始明白了。它跳過空白的令牌,繼續前進到下一個令牌,同時仍然從空白令牌中刪除分隔符......使得找到解決方案困難。似乎他們在PHP 4.1上改變了這個功能,因爲它用來彈出空白的令牌。 – dqhendricks