本日もお疲れ様です。タカフです。
最近WordPressの開発案件を担当しているのですが、久々目からウロコのコードだったので、本日はそれを記事にしたいと思います。
それは何かというと、WordPress で一番最後にhtmlの特定文字列を置換する方法です。
どういうこと?
どういうことかと言うと、例えばですがテーマディレクトリに設置されたページテンプレートphpファイルの中で、imgタグのsrcに指定された相対パスのurlを、html側に手を入れることなく絶対パスのurlに置換したいというような時に役立つ技です。
何故こういうことをしたいかというと、WordPressに限らずの話ですがWeb開発の仕事をしていてコーダーによってhtml/cssのコーディングをして納品されたものってimgのsrcのurlなどは相対パスで書かれてあったりしますよね。
でも最近のWebシステムは、フロントコントローラーでルーティング処理をしているものが多く、相対パスがそのまま使えないことってあるんですよね。
けど全てのコーディングファイルのhtmlを編集したくない。。imgタグがたくさんあるなら尚更ですよね。
しかもデザイン修正入ってまたコーディングファイルが変わったら、手を入れたコーディングファイルにまたその差分だけマージするような作業もしたくないですし。
作業したくない病ですみません(´・ω・`)
でも、みなさんも作業を楽に出来るならその方がいいに決まってますよね。
解決コード
さて、結論の解決コードから示すと下記のようになります。
function output_callback($buffer) {
// テーマディレクトリをURL形式で取得
$theme_url = get_template_directory_uri();
// imgタグのsrc属性に記述されたimg相対パスをテーマディレクトリのパスに一括置換する(i:大文字小文字無視する)
$buffer = preg_replace('/(<img .*src=")img\//i', '$1' . $theme_url . '/img/', $buffer);
return $buffer;
}
function buf_start() {
// テーマの始まりで呼ばれるので、ここからブラウザに出力する文字を内部バッファリングする
// 引数はコールバック関数を渡すと、出力バッファがフラッシュされた時に呼び出される
ob_start('output_callback');
}
function buf_end() {
// 出力バッファリングがある時はフラッシュするが、ここの中は通ることはない。
// 何故ならWordPressの shutdownアクションの中で、先にすべての出力バッファをフラッシュしているところがある為。
// なので念の為バッファがあるかを確認してフラッシュにする。これを入れないと以下のエラーが出る
// ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush
if (ob_get_length()) {
ob_end_flush();
}
}
add_action('after_setup_theme', 'buf_start'); // 本テーマのfunctions.phpが読み込まれた直後のフック
add_action('shutdown', 'buf_end'); // PHP の実行が終わりかけの時点
コードの解説
何やら色々やってますが、重要なのは、after_setup_theme のフックで ob_startによってバッファリングを開始して、フラッシュされた時によばれるのが output_callback でこの中で特定文字列を置換して返却すると、置換した文字列として出力する、ということをやっています。
ネットで参考になった記事があったのですが、そのサンプルコードは以下のような感じで、
function output_callback($buffer) {
$buffer = str_replace('hoge','hogehoge',$buffer);
return $buffer;
}
function buf_start() { ob_start("output_callback"); }
function buf_end() { ob_end_flush(); }
add_action('after_setup_theme', 'buf_start');
add_action('shutdown', 'buf_end');
これで組み込んでWP_DEBUG を trueにしていると、以下のようなエラーが出ることがわかりました。
ob_end_flush(): failed to delete and flush buffer. No buffer to delete or flush
どうやら「もうフラッシュする内容が無いよう」(は!)って事のようです。
これは、僕の方で調査したところ、WordPress側でshutdownアクションの中で、先に全ての出力バッファをフラッシュしている処理があるために、実はあえて shutdown フックで、ob_end_flushする必要がないんですね。
本当は明示的にフラッシュしてshutdown内で置換してやりたいけど、フック優先度変えても出来ないので諦めました。
ということで、ob_startの第二引数にコールバックを渡すと、出力バッファがフラッシュされた時に自動で呼び出されるので、WordPress側の wp_ob_end_flush_all 関数に委ねてコールバックされて置換します。
なので、それに委ねてしまえばあえてshuddownをフックする必要はないのですが、今回の解決コードでは一応 ob_get_length で一度バッファリングがあるか確かめてから ob_end_flush する処理にしています。まぁ保険の意味での処理になりますね。
これを調べるためにob_startに始まるバッファリングについても詳しく調べて何かと勉強になりました。
PHPの出力バッファリング、使いこなすと結構便利なんですよね。
現場からは以上です!