Wikiエンジン、リスト処理まで完成
[ Category : CMS開発プロジェクト ] 2010年01月26日 02:25
Wikiエンジン自作中です。見出しや文字の修飾、リンク、画像の表示等に加え、リストの処理まで完成しました。
現在の状況
コード
とりあえずはコードから(汚いですけど)
- cording.php
<?php class cording{ public $enable_nesting_tags = array( 'table' => 'tr', 'tr' => array('td', 'th'), 'td' => array('ul', 'ol'), 'th' => array('ul', 'ol'), 'ul' => array('ul', 'ol', 'li'), 'ol' => array('ul', 'ol', 'li'), 'div' => array('p', 'table', 'ul', 'ol', 'h2', 'h3', 'h4', 'h5'), 'p' => array('ul', 'ol') ); public $cording_open_tags = array(); public $cording_now_tag; public $cording_mode; public $cording_list_array; public $cording_last_list_level; public $cording_last_list_type; function cording_tag_close_check($tag){ global $_debug; $result = FALSE; if (count($this->cording_open_tags) > 0){ if (is_array($this->enable_nesting_tags[$this->cording_open_tags[0]])){ if (!in_array($tag, $this->enable_nesting_tags[$this->cording_open_tags[0]])){ $result = TRUE; } }else if ($tag != $this->enable_nesting_tags[$this->cording_open_tags[0]]){ $result = TRUE; } } return $result; } function cording_tag_close($tag, $unshift_flug=TRUE, $reset_list_level=TRUE){ if ($this->cording_tag_close_check($tag)){ while($this->cording_tag_close_check($tag)){ $close_tag = array_shift($this->cording_open_tags); echo '</'.$close_tag.">\n"; } } if ($unshift_flug){ array_unshift($this->cording_open_tags, $tag); } if ($reset_list_level){ $this->cording_last_list_level = 0; } } function cording_list($new_level, $type, $body){ if (!in_array("p", $this->cording_open_tags)){ $this->cording_tag_close("p", TRUE, TRUE); echo "<p>\n"; } if ($new_level > $this->cording_last_list_level){ $this->cording_tag_close($type, TRUE, FALSE); $str = '<'.$type.' class=level'.$new_level.'">'."\n".'<li class="level_'.$new_level.'">'.$this->cording_text($body)."</li>\n"; }else if ($new_level < $this->cording_last_list_level){ $str = '<'.$this->cording_last_list_type.">\n".'<li class="level_'.$new_level.'">'.$this->cording_text($body)."</li>\n"; }else{ $str = '<li class="level_'.$new_level.'">'.$this->cording_text($body)."</li>\n"; } $this->cording_last_list_level = $new_level; $this->cording_last_list_type = $new_type; return $str; } function cording_text($str){ global $a_target; $str = preg_replace("/<([^\.=]*)>/", "<$1>", $str); $str = preg_replace("/\*\*([^\*]*)\*\*/", "<b>$1</b>", $str); $str = preg_replace("/\/\/([^\/\[\]\{\}]*)\/\//", "<i>$1</i>", $str); $str = preg_replace("/__([^_]*)__/", "<u>$1</u>", $str); $str = preg_replace("/--([^-]*)--/", "<del>$1</del>", $str); $str = preg_replace("/\[\[http:\/\/([^\|\]]*)\|([^\]]*)\]\]/", '<a href="http://$1"'.$a_target.'>$2</a>', $str); $str = preg_replace("/\[\[http:\/\/([^\|\]]*)\]\]/", '<a href="http://$1"'.$a_target.'>$1</a>', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*)\}\}/", '<img src="http://$1" alt="$1" />', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*)\?([0-9]*)\}\}/", '<img src="http://$1" width="$2" alt="$1" />', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*)\?([0-9]*)x([0-9]*)\}\}/", '<img src="http://$1" width="$2" height="$3" alt="$1" />', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*) *\}\}/", '<img src="http://$1" alt="$1" class="img_left" align="left" />', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*)\?([0-9]*) *\}\}/", '<img src="http://$1" width="$2" alt="$1" class="img_left" align="left"/>', $str); $str = preg_replace("/\{\{http:\/\/([^\|\}\? ]*)\?([0-9]*)x([0-9]*) *\}\}/", '<img src="http://$1" width="$2" height="$3" alt="$1" class="img_left" align="left"/>', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*)\}\}/", '<img src="http://$1" alt="$1" class="img_right" align="rignt" />', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*)\?([0-9]*)\}\}/", '<img src="http://$1" width="$2" alt="$1" class="img_right" align="right" />', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*)\?([0-9]*)x([0-9]*)\}\}/", '<img src="http://$1" width="$2" height="$3" alt="$1" class="img_right" align="right" />', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*) +\}\}/", '<img src="http://$1" alt="$1" class="img_center" />', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*)\?([0-9]*) +\}\}/", '<img src="http://$1" width="$2" alt="$1" class="img_center" />', $str); $str = preg_replace("/\{\{ +http:\/\/([^\|\}\? ]*)\?([0-9]*)x([0-9]*) +\}\}/", '<img src="http://$1" width="$2" height="$3" alt="$1" class="img_center" />', $str); $str = preg_replace("/< ?\.([^\| ]*) ?\|([^>]*)>/", '<span class="$1">$2</span>', $str); $str = preg_replace("/< ?= ?([^\| ]*) ?\|([^>]*)>/", '<span style="$1">$2</span>', $str); return $str; } function cording_start($str){ $array = preg_split("/[\r\n]/", $str, -1, PREG_SPLIT_NO_EMPTY); $end = count($array); $result = ''; for ($i=0; $i<$end; $i++){ $line = $array[$i]; if (preg_match("/^(\={2}) ?([^=]*)$/", $line, $data)){ $this->cording_tag_close("h2", FALSE); echo "<h2>".$data[2]."</h2>\n"; }else if (preg_match("/^(\={3}) ?([^=]*)$/", $line, $data)){ $this->cording_tag_close("h3", FALSE); echo "<h3>".$data[2]."</h3>\n"; }else if (preg_match("/^(\={4}) ?([^=]*)$/", $line, $data)){ $this->cording_tag_close("h4", FALSE); echo "<h3>".$data[2]."</h4>\n"; }else if (preg_match("/^(\={5}) ?([^=]*)$/", $line, $data)){ $this->cording_tag_close("h5", FALSE); echo "<h3>".$data[2]."</h5>\n"; }else if (preg_match("/^( {2,3}-) ?(.*)$/", $line, $data)){ echo $this->cording_list(1, 'ol', $data[2]); }else if (preg_match("/^( {4,5}-) ?(.*)$/", $line, $data)){ echo $this->cording_list(2, 'ol', $data[2]); }else if (preg_match("/^( {6,7}-) ?(.*)$/", $line, $data)){ echo $this->cording_list(3, 'ol', $data[2]); }else if (preg_match("/^( {8,9}-) ?(.*)$/", $line, $data)){ echo $this->cording_list(4, 'ol', $data[2]); }else if (preg_match("/^( {2,3}\*) ?(.*)$/", $line, $data)){ echo $this->cording_list(1, 'ul', $data[2]); }else if (preg_match("/^( {4,5}\*) ?(.*)$/", $line, $data)){ echo $this->cording_list(2, 'ul', $data[2]); }else if (preg_match("/^( {6,7}\*) ?(.*)$/", $line, $data)){ echo $this->cording_list(3, 'ul', $data[2]); }else if (preg_match("/^( {8,9}\*) ?(.*)$/", $line, $data)){ echo $this->cording_list(4, 'ul', $data[2]); }else if (preg_match("/^\#([^\.\#]*) ?\.([^\.\#\{]*)\{$/", $line, $data)){ $this->cording_tag_close("div", TRUE); echo '<div id="'.$data[1].'" class="'.$data[2].'">'."\n"; }else if (preg_match("/^\.([^\.\#]*) ?\#([^\.\#\{]*)\{$/", $line, $data)){ $this->cording_tag_close("div", TRUE); echo '<div class="'.$data[1].'" id="'.$data[2].'">'."\n"; }else if (preg_match("/^\.([^\.\#\{]*)\{$/", $line, $data)){ $this->cording_tag_close("div", TRUE); echo '<div class="'.$data[1].'">'."\n"; }else if (preg_match("/^\#([^\.\#\{]*)\{$/", $line, $data)){ $this->cording_tag_close("div", TRUE); echo '<div id="'.$data[1].'">'."\n"; }else if ($line == "}" && in_array("div", $this->cording_open_tags)){ while ($this->cording_open_tags[0] != "div"){ echo '</'.array_shift($this->cording_open_tags).">\n"; } echo '</'.array_shift($this->cording_open_tags).">\n"; }else{ $this->cording_tag_close("p", TRUE); echo "<p>\n"; echo $this->cording_text($line)."\n"; } } $this->cording_tag_close("", FALSE); } } ?>
file_get_contents()関数等でテキストファイルを読み込み、そのデータをそのままcording_start()メソッドに投げてやることでHTMLに変換します。
例えば、こんな感じです。
- cording_test.php
<? // 読み込ませるファイルのパスを指定 $file = 'path/to/file'; require_once("cording.php"); // デバッグのため「text/plain」で吐き出します header("Content-Type: text/plain; charset=UTF-8"); $obj = new cording(); $obj->cording_start(file_get_contents($file)); ?>
ちなみに、変換させるテキストファイルはこちらのページに書いてある文法を解読します。
ここがムズい!
HTMLタグは、入れ子にしてどんどん開いていくことができます。例えば…
<div> <p> <ul> <li>aaaaa</li> <ul> <li>bbbbb</li>
といった感じです。開いていくのは楽なんですが、次に来るタグによって「どこまで閉じるか」を判断するロジックが難しいです。
例えば、上のHTMLコードの次に「<h2>」なんてタグを入れようと思ったら、<ul>は2つとも閉じるとして、<p>は閉じるのか? <div>は閉じるのか?? <h2>ではなく「<p>テキスト</p>」が来たらどこまで閉じるのか?? このあたりの判断がWikiエンジンをつくるにあたっての1つの山ではないかと思います。
ボクのコード内では「cording_tag_close()」と「cording_tag_close_check()」とでタグを閉じるか閉じないか(閉じるとしたらどこまで閉じるのか)を判断させています。
次はテーブルの処理
Discussion
- 投稿する前に「Discussionのガイドライン」をご一読くださいませ。