[WordPress][プラグイン開発] 記事中のショートコードを保存時に書き換える
記事に挿入したショートコードを保存した時に書き換えてしまうプラグインサンプルを書きました。
[foo bar="qux"] → 保存すると [foo bar="qux" baz="quux"] というような感じ。
なぜそんなことする必要があるのかというと、ショートコードの処理は毎回記事を表示するときに実行されるので、重たい処理はショートコードに含ませておこうというものです。
具体的には、Webサイトのスクリーンショットを表示 で作成したプラグインのショートコードが、Web サイトのタイトルを指定していないと毎回 Web サイトにアクセスするという動作をしているのに、今頃気付いたので、その解決策です。このプラグインは保存時にタイトルを取得するようアップデートしています。既存の記事は一度更新しないと毎回タイトル取得してしまいますが、プラグイン単体で良い感じにするのは思いつかなかったのでそのままです。
コード全体は jz5/wp-rewrite-shortcode-sample に置いてます。
ショートコード プラグインの作成
ショートコードのプラグイン作成は簡単です。add_shortcode でショートコードタグのフックを追加します。
[foo bar="qux" baz="quux"] というショートコードを書くと、(bar = qux, baz = quux) と表示するだけの簡単なものです。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// shortcode 処理 | |
// [foo bar="qux" baz="quux"] | |
add_shortcode('foo', 'rewrite_shortcode_func'); | |
function rewrite_shortcode_func($atts) { | |
// shortcode のパラメータ取得 | |
extract(shortcode_atts(array( | |
'bar' => 'qux', // bar 既定値 | |
'baz' => null // baz 既定値 | |
), $atts)); | |
return "(bar = $bar, baz = $baz)"; // shortcode の結果 | |
} | |
?> |
保存時に書き換える
サンプルとして、ショートコードの baz 属性が指定されていなかった場合は、プラグインで値を決め本文中に書き換えてから保存するようにします。
新規作成と更新時時に処理するため wp_insert_post_data というフィルターをフックします。
$data['post_content']
の内容を書き換えます。サンプルでは baz 属性を指定していなければ uniqid() の結果が付けたショートコードに書き換えられます。
ショートコード部分の取得などは、Shortcode API を使います。リファレンスだと詳細不明で実際の shortcodes.php のコードも読んでました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// 保存時 shortcode の書き換え | |
add_filter('wp_insert_post_data', 'rewrite_shortcode_insert_post_data'); | |
function rewrite_shortcode_insert_post_data($data) { | |
// foo shortcode がなければ書き換えなし | |
if (false === has_shortcode($data['post_content'], 'foo')) { | |
return $data; | |
} | |
// foo shortcode 部分の書き換え | |
$pattern = get_shortcode_regex(); | |
// shortcode 部分を繰り返し取得して置換 | |
$data['post_content'] = preg_replace_callback('/'. $pattern .'/s', function ($matches) { | |
// foo 以外の shortcode は書き換えなし | |
if ($matches[2] !== 'foo') { | |
return $matches[0]; | |
} | |
// エスケープされた shortcode は書き換えなし | |
if ($matches[1] == '[' && $matches[6] == ']') { | |
return $matches[0]; | |
} | |
// shortcode のパラメータ取得 | |
$atts = shortcode_parse_atts(stripslashes($matches[3])); | |
// baz !== null (規定値以外)の場合も書き換えなし | |
if ($atts['baz'] !== null) { | |
return $matches[0]; | |
} | |
// baz が既定値以外の場合 shortcode を書き換え | |
$atts['baz'] = uniqid(); | |
$params = array(); | |
foreach ($atts as $key => $val) { | |
$params[] = "$key=\"$val\""; | |
} | |
return '[foo ' . implode(' ', $params) . ']'; // 書き換えた shortcode | |
}, $data['post_content']); | |
return $data; | |
} | |
?> |
has_shortcode
Function Reference/has shortcode « WordPress Codex
まず対象のショートコードが本文に含まれるか確認に使っています。
get_shortcode_regex
Function Reference/get shortcode regex « WordPress Codex
ショートコードを表す正規表現を返す関数。この正規表現自体は [foo] のような閉じタグ([/foo])がない場合はうまくショートコード部分を取れません(が、フィルターに渡される本文データの場合上記コードで意図した動作になります)。
下記の結果を使っています。
- $matches[0]: ショートコード全体([foo bar="qux" baz="quux"])
- $matches[2]: ショートコードタグ(foo)
- $matches[3]: ショートコード属性部分(bar="qux" baz="quux")
- $matches[1], $matches[6]: ショートコードのエスケープ記述([[foo]])の場合は [ と ] が入る
shortcode_parse_atts
Function Reference/shortcode parse atts « WordPress Codex
ショートコード属性部分(bar="qux" baz="quux")を連想配列に変換します。上記で得られた結果は stripslashes 関数を通してから指定します。