管理画面から PHP を足せる「Code Snippets」は便利ですが、スニペットが増えてくると、どこで何をしているのか分かりにくくなり、変更の履歴も残りません。そこで、スニペットのコードをテーマの functions.php に移し、git で管理するようにしました。
移すときに一度「Cannot redeclare」でサイトが止まりかけたので、原因と、止めずに切り替える方法をまとめます。
移す前に考えたこと
- 中身は変えずに、そのまま移す:移し替えと直しを同時にすると、何かあったときにどちらが原因か分からなくなります。
- 同じ順番で動かす:Code Snippets は「優先度 → id」の順に実行します。移した先でも同じ順に並べます。
- パスワードなどは git に入れない:別の DB につなぐスニペットなどに接続情報が入っていたら、wp-config.php の定数に移します。
- いつでも戻せるようにする:Code Snippets のプラグインを有効に戻せば、元どおりになる形にします。
有効なスニペットを書き出す
スニペットは DB の wp_snippets テーブルに入っています。有効なもの(active = 1)を、実行される順に取り出します。
SELECT id, name, priority, code FROM wp_snippets WHERE active = 1 ORDER BY priority, id;
これを 1 つのファイル(例:テーマの functions/site-snippets.php)に、上から順に並べます。どのスニペットから来たか分かるように、見出しに id と名前を書いておくと、あとで探しやすくなります。
並べ方の例
<?php
// Code Snippets から移した処理。中身は元のまま、Code Snippets と同じ順(優先度 → id)に並べた
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/* ---- id 23:ショートコード : パーマリンク取得(優先度 10) ---- */
function my_short_code_get_permalink( $atts ) {
// …(スニペットの中身をそのまま)
}
add_shortcode( 'permalink', 'my_short_code_get_permalink' );
/* ---- id 24:…… ---- */
「Cannot redeclare」でサイトが止まる
移したファイルを functions.php から読み込むと、Code Snippets のプラグインが有効なあいだは、同じ関数が 2 回作られます。PHP では同じ名前の関数を 2 回作れないので、次のエラーでサイトが止まります。
PHP Fatal error: Cannot redeclare my_short_code_get_permalink() (previously declared in …/code-snippets/php/snippet-ops.php(822) : eval()'d code:4)
そこで、最初はファイルの先頭に「Code Snippets が有効なら何もしない」と書きました。
うまくいかなかった書き方
<?php
// functions/site-snippets.php の先頭
if ( defined( 'CODE_SNIPPETS_FILE' ) ) {
return; // Code Snippets が有効なら、ここで読むのをやめる…つもり
}
function my_short_code_get_permalink( $atts ) {
// …
}
ところが、これでも同じエラーで止まります。
原因:PHP は、ファイルの一番外の関数を先に作る
PHP は、ファイルを読み込むとき、ファイルの一番外(if や関数の中ではない所)に書いた関数を、中の処理を動かす前に作ります。そのため、先頭の return まで処理が進む前に、関数を作ろうとしてエラーになります。
if の中に書いた関数は、その if が動いたときに作られます。Code Snippets の多くのスニペットが「if ( ! function_exists( ‘…’ ) ) { function …」と書かれているのは、このためです。ただ、すべてのスニペットがその書き方になっているとは限りません。
直し方:読み込む前に、functions.php で分ける
ファイルの中で止めるのではなく、読み込むかどうかを functions.php の側で決めます。
// Code Snippets から移した処理。Code Snippets のプラグインが有効なあいだは読まない
// (同じ関数が二重に作られて止まるため。プラグインを止めると、こちらに切り替わる)
if ( ! defined( 'CODE_SNIPPETS_FILE' ) ) {
require_once __DIR__ . '/functions/site-snippets.php';
}
こうすると、ファイルそのものを読まないので、関数が二重に作られることはありません。
- Code Snippets が有効なあいだ:今までどおりスニペットが動き、テーマのファイルは読まれない
- Code Snippets のプラグインを止めた瞬間:テーマのファイルが読まれて、同じ処理が動く
両方が同時に動く時間がないので、出力の置き換えのような「二重に動くとおかしくなる」処理も安全です。
Code Snippets の code_snippets() 関数は名前空間(Code_Snippets)の中にあるため、function_exists( 'code_snippets' ) は false になります。プラグインのメインファイルで定義される定数 CODE_SNIPPETS_FILE で判定します。
パスワードの入ったスニペットは wp-config.php の定数へ
別の WordPress の DB につないで記事数を数えるスニペットのように、接続情報(パスワード)が入っているものは、そのまま git に入れないようにします。接続情報を wp-config.php に定数で書き、スニペット側はそれを読むだけにします。
wp-config.php に接続情報を書く(git に入れない)
define( 'MY_REMOTE_DBS', array(
'blog' => array(
'host' => 'localhost',
'database' => 'blog_db',
'user' => 'blog_user',
'pass' => '(パスワード)',
'prefix' => 'wp_',
),
) );
スニペット側は定数を読むだけにする(git に入れる)
$remote_wp = defined( 'MY_REMOTE_DBS' ) ? MY_REMOTE_DBS : array();
if ( ! isset( $remote_wp[ $db_key ] ) ) {
return '';
}
$remote_db = $remote_wp[ $db_key ];
定数に配列を入れられるのは PHP 7.0 からです。
切り替えの手順
- 本番のファイルとデータベースのバックアップを取る。
- パスワードなどを wp-config.php の定数に移す(スニペット側はまだ元のまま)。
- テーマの functions.php と functions/site-snippets.php を本番に上げる。Code Snippets が有効なので、この時点では何も変わらない。
- 主なページの HTML を保存しておく(切り替えの前と比べるため)。
- Code Snippets のプラグインを停止する。ここでテーマ側に切り替わる。
- 同じページを開いて、前と比べる。ショートコードが文字のまま出ていないか、管理画面の追加した機能(ダッシュボードのウィジェットなど)が出るかも見る。
- おかしければ、Code Snippets のプラグインを有効に戻す。すぐに元どおりになる。
実際に切り替えたときは、前と後の HTML の違いが、ランダムに表示している部分だけでした。
まとめ
- ファイルの一番外に書いた関数は、return より先に作られる。「途中で return して読むのをやめる」では二重定義を防げない。
- 読み込むかどうかを functions.php で決める(if の中で require する)。
- Code Snippets の判定は、定数 CODE_SNIPPETS_FILE で行う。
- パスワードは wp-config.php の定数に移して、git に入れない。
- プラグインを止めるだけで切り替わり、有効に戻すだけで元に戻せる。


