- 投稿: 2011年10月22日 20:22
- 更新: 2011年10月23日 00:07
- ソフトウェア
ブログを長いこと書いていると,エントリ数が膨大になるので,過去に書いた記事を探し出してリンクを張ることが面倒になってきます.エントリ名をトリガにして,テキストをハイパーリンクに置換して,オートリンクすることができれば,便利だなぁなんて思うわけです.ですので,それをWordpressで実現してみます.方法はかなり泥臭い手法です.
前提条件と基本戦略
前提条件を以下のように設定します.
- エントリ本文に対してオートリンクを行う.
- エントリ本文は基本的にプレーンテキストである.
- 置換対象文字列は既存のエントリ名とする.
- 置換後は既存エントリにハイパーリンクを張る.
上記条件を前提として,オートリンクを実現します.基本的な戦略は以下の通り.
- 既存エントリ名を全て取得する.
- エントリ本文から既存エントリ名があるかどうかを探す.
- もし見つかれば,オートリンクに置換する.
既存エントリ名を全て取得する
これは別に難しくないですね.Wordpressの関数を使って,サクッと作ると,以下のような感じでしょうか.
$posts = get_posts('numberposts=-1'); foreach($posts as $post) { $links[$post->post_title] = $post->guid; }
こんな感じで,$link[]な連想配列にkeyとしてエントリ名,valueとしてパーマリンクが格納されます.簡単ちんね.この辺りは,頻繁に変わらないだろうから,キャッシュを仕掛けるとか,どっかに定数として準備しておいても良いかも.
エントリ本文に既存エントリ名があるかどうかを探す
では,文字列置換をしましょう.PHPで文字列置換といえば,str_replaceですが,単純に使うとマズイです.以下のような例を想定しましょう.
私はコーヒーよりもコーヒー牛乳が好きです.
ここで,既存エントリとして「コーヒー」および「コーヒー牛乳」があったとします.置換順序が「コーヒー」「コーヒー牛乳」だった場合,以下のように置換されて行きます.ここでは,マッチした部分を強調で示します.
私はコーヒーよりもコーヒー牛乳が好きです.
次に「コーヒー牛乳」で置換したいのですが,既に「コーヒー」で置換されているので,マッチしません.マズイですね.ですので,置換文字列は,文字数が多い順に並んでいる必要性があると思います.そうすることで,最長一致による文字列置換が実現されます.では,それだけでよいでしょうか?置換順序が「コーヒー牛乳」「コーヒー」の順番になっても,次の問題が発生します.
私はコーヒーよりもコーヒー牛乳が好きです.
この文字列に対して,「コーヒー」の置換を行うと,「コーヒー牛乳」の「コーヒー」が再度置換されます.結果として,入れ子のaタグで挟まれる形になります.これはマズイです.つまり,単純な置換はできないので,正規表現を使うことにしましょう.
正規表現による戦略
mb_ereg_replaceを用います.置換文字列が対象文字列内にあり,さらにaタグで挟まれていない場合に,置換を行いましょう.皆さまご存じの通り,私は正規表現を苦手としており,四苦八苦です.ですので,一生懸命探しました.しかし,結構見つからないのです.出てくるのは,HTMLタグの除去とか,URLっぽい文字列を見つけたらオートリンクで置換とかばっかりです.ちょっと違うのです.しかし,需要がないはずはないと思い,頑張って探したところ,見つかりました.ググリ力は大事ですね!
とりあえず、「aタグの内部には他のタグは含まれていない」という前提の元で話を進めます。
$string = "朝一番のコーヒーは<a href='index.html'>3時のコーヒーや</a>食後のコーヒーより旨い"; $pattern = 'コーヒー(?![^<]*</a>)'; $result = mb_ereg_replace($pattern, '紅茶', $string); echo $result;
ktkr!これでバッチリです.
functions.phpでフィルターフックをかける
では上記基本検討に従って,エントリ本文を表示する際に,エントリ本文中に既存エントリ名があった場合,オートリンクを張るような仕掛けを作ります.今回は,テーマで使われるfunctions.phpにフィルターフックを仕掛けてみました.対象とするフィルタフックはthe_contentです.つまり,まとめると,以下のようなコードをfunctions.phpに書きます.
function myautolink($content='') { mb_regex_encoding('UTF-8'); $links = array(); $length = array(); $posts = get_posts('numberposts=-1'); foreach($posts as $post) { $links[$post->post_title] = $post->guid; $length[$post->post_title] = mb_strlen($post->post_title); } arsort($length); foreach($length as $link => $val) { $pattern = $link.'(?![^<]*</a>)'; $replacement = '<a href="'.$links[$link].'">'.$link.'</a>'; $result = mb_ereg_replace($pattern, $replacement, $content); if(false != $result) { $content = $result; } } return $content; } add_filter('the_content', 'myautolink');
簡単ね!
まとめ
エントリ数が膨大だったり,置換対象の文章が長い場合には,性能が低下する可能性が高くなるので,キャッシュを準備するとか,なんかそういう工夫が必要になります.それについて,今回は触れないので,必要があれば工夫して下さい.
- Newer: 心の健康と病理(’08)
- Older: iHerbでお取り寄せしてみた