正規表現では文字列に最短一致でマッチさせるために *? や +? など ? を使いますが、preg_replace を使って文字列を置き換えようとした際にミスったケースの話です。
01はてなブログインポートツール bugfix
このブログもそうですが、1年半ほど前に、はてなブログで運用していた 3つのブログを WordPress に移しています。その際、ワンクリック(とはいかないが…)で簡単に移行できる「はてなブログインポートツール」というプラグインを作っています。そのプログラムに正規表現の最短一致についてバグがあったということです。
上記リンク記事がそのツールの説明で GitHub からダウンロードできるようになっています。すでにバグフィックス済みです。
02preg_replace での最短一致
移行には、はてなブログのデータを MT(MovableType)形式でエクスポートしたファイルを使うわけですが、取り込む前にいくつか WordPress 用に整形する必要があります。
整形する項目は
- 画像の URL 変更、alt, title 等不要属性削除
- 見出しの変更 h3->h2, h4->h3, h5->h4
- キーワードリンク削除
- Youtube リンクを https に変更
で、その部分のコードは、
$hatenablog_path = はてなブログのエクスポートファイル
$hatena_text = file_get_contents($hatenablog_path);
$patterns = array (
'/https?:\/\/cdn-ak\.f\.st-hatena.com\/images\/fotolife\/.+?\/.+?\/(\d{4})(\d{2})\d{2}\/(.+\.(jpg|gif|png))/',
'/alt="f:id:.+?"/',
'/title="f:id:.+?"/',
'/ figure-image-fotolife mceNonEditable/',
'/ class="mceEditable"/',
'/<h3 /',
'/<\/h3>/',
'/<h4 /',
'/<\/h4>/',
'/<h5 /',
'/<\/h5>/',
'/<a(?: class="keyword" | )href="http:\/\/d\.hatena\.ne\.jp\/keyword\/.+?".*?>(.+?)<\/a>/',
'/<iframe.+?youtube\.com\/embed\/(.+)?\?.+?<\/iframe>/'
);
$replace = array (
$wp_path . '/wp-content/uploads/$1/$2/$3',
'alt=""',
'title=""',
'',
'',
'<h2 ',
'</h2>',
'<h3 ',
'</h3>',
'<h4 ',
'</h4>',
'$1',
'<iframe src="https://www.youtube.com/embed/$1?enablejsapi=1" width="560" height="315" frameborder="0" allowfullscreen></iframe>'
);
$new_file = preg_replace($patterns, $replace, $hatena_text);
となっています。
その15行目です。
赤のアンダーラインの部分です。やろうとしていることは、<a …>文字列</a> のリンクを削除して文字列だけにしようということです。これを最初は (.+)?<\/a> とやっていたものですから最短一致にならなかったということです。単純に無知だったということです(涙)。
なお、このリンクが何かといいますと、はてなに登録されている文字列に自動的にキーワードリンクがつくというものです。はてなブログPro を契約しますとリンクを貼らないオプションがつきます。