どうもどうも。今年はそこそこブログを書いています。体調が戻ってきました。よかったよかった。決して万全ではないのですが、1年近く体調がわるかったので、まぁそのいろいろアレです。
ところで、PHPerのみなさん。
たとえば、管理画面でメールテンプレートを管理するような場合、
そのテンプレートをどう処理するか、結構悩みますよね。
同じようなかんじで、WordPressなんかで、囲みタグ型のショートコードでループをさせたいときとかもかなり悩みます。
よくある例だと、
- 管理画面からテンプレート編集
- POSTされたデータをDBに保存しつつ、テンプレートエンジン用に、ファイルにも保存
- 出力時に保存されたファイルをテンプレートエンジンで処理して出力
というような手順でしょうか。
まぁ悪くはないですが、セキュリティに気をつけなければいけないとか、結構手間取りますし、ファイル出力だと手軽さがなくなります。
今回はWordPressのショートコードでどうしようかなーと思ったときの対処をメモ代わりに。
やりたいこと
前提条件です。
- WordPressの囲みタグ型ショートコードをテンプレートとして扱いたい
- テンプレートエンジンとして動かしたいが、PHPを書くのや、Smartyのようにファイル処理を前提としない
- 条件分岐はとりあえずいらないがループはある
という前提条件で、具体的には
[get_product_categories category="カテゴリ名"] <ul> {categories} <li> <a href="{category_url}"> {category_name} </a> </li> {/categories} </ul> [/get_product_categories]
こんなショートコードを書いてもらってテンプレート処理をしたいわけです。
囲みタグ型のショートコードについてはみなさまが書いているので、そちらをご参考に
まぁ普通にやれば
このような条件を満たそうと思うと、難しいコードでもないので、自分で簡単なテンプレートエンジンを書いてもいいとは思うのですが、いまいち気乗りがしません。自分で書くとすれば、正規表現を書いて、ゴニョゴニョとするところではありますよね。
そこで!Codeigniterの簡易パーサークラスをぱくってきます
Codeigniterには条件分岐はないですがループに対応して、ファイル処理なしで実行できる簡易テンプレートンジンがついています。これが使えないかと思ってソースを見たところ、Codeigniter本体にはほとんど依存していないことがわかり、少しの書き換えで対応ができることがわかりました。
四の五の言わずにコードを晒せ
はい。
<?php class CI_Parser { /** * Left delimiter character for pseudo vars * * @var string */ public $l_delim = '{'; /** * Right delimiter character for pseudo vars * * @var string */ public $r_delim = '}'; // -------------------------------------------------------------------- /** * Class constructor * * @return void */ public function __construct(){ } // -------------------------------------------------------------------- /** * Parse a template * * @param string * @param array * @param bool * @return string */ public function parse( $template, $data, $return = FALSE ){ return $this->_parse($template, $data, $return); } // -------------------------------------------------------------------- /** * Parse a template * * Parses pseudo-variables contained in the specified template, * replacing them with the data in the second param * * @param string * @param array * @param bool * @return string */ protected function _parse( $template, $data ) { if ($template === '') { return FALSE; } $replace = array(); foreach ($data as $key => $val) { $replace = array_merge( $replace, is_array($val) ? $this->_parse_pair($key, $val, $template) : $this->_parse_single($key, (string) $val, $template) ); } unset($data); $template = strtr($template, $replace); return $template; } // -------------------------------------------------------------------- /** * Set the left/right variable delimiters * * @param string * @param string * @return void */ public function set_delimiters($l = '{', $r = '}') { $this->l_delim = $l; $this->r_delim = $r; } // -------------------------------------------------------------------- /** * Parse a single key/value * * @param string * @param string * @param string * @return string */ protected function _parse_single($key, $val, $string) { return array($this->l_delim.$key.$this->r_delim => (string) $val); } // -------------------------------------------------------------------- /** * Parse a tag pair * * Parses tag pairs: {some_tag} string... {/some_tag} * * @param string * @param array * @param string * @return string */ protected function _parse_pair($variable, $data, $string) { $replace = array(); preg_match_all( '#'.preg_quote($this->l_delim.$variable.$this->r_delim).'(.+?)'.preg_quote($this->l_delim.'/'.$variable.$this->r_delim).'#s', $string, $matches, PREG_SET_ORDER ); foreach ($matches as $match) { $str = ''; foreach ($data as $row) { $temp = array(); foreach ($row as $key => $val) { if (is_array($val)) { $pair = $this->_parse_pair($key, $val, $match[1]); if ( ! empty($pair)) { $temp = array_merge($temp, $pair); } continue; } $temp[$this->l_delim.$key.$this->r_delim] = $val; } $str .= strtr($match[1], $temp); } $replace[$match[0]] = $str; } return $replace; } }
Codeigniterのコントローラーオブジェクトに依存しているところなどを削って純粋にclassとして動くようになっています。
じゃWPで動かしてみよう
functions.phpあたりで、上記ファイルをrequireして
add_shortcode( 'get_product_categories', 'get_product_categories'); functionget_product_categories( $atts, $content = null ){ //このへんで適当に引数処理してね $parser = new CI_Parser(); $args = array( ここに適当に条件かいてね ); $the_query = new WP_Query($args); if (!$the_query->have_posts()) { return ''; } $data = array(); $data['categories'] = array();//ショートコードのループタグと名前を合わせる while ( $the_query->have_posts() ) { $the_query->the_post(); $set = array( 'category_name' => get_the_title(), 'category_url' => get_the_permalink(), ); $data['categories'][] = $set; } wp_reset_postdata(); $content = $parser->parse( $content, $data );//最後にパースする。 return $content; }
とでもしておけば、簡単ね。
多分改造でifくらいはいける
上記の通りCodeigniterのパーサーはシンプルにできてます。
たぶん改造すればifくらいは余裕でかけると思います。
が、他方、毎回実行時にパーサーが走るわけなので、決して軽くはありません。
使い所見極めていきましょう。
なお、Codeigniter3.xはMITライセンスなのでOKですが、2.xまでのコードを使う場合は独自ライセンスなので、扱いに気をつけてください。
現場からは以上です。
コメントを残す