Code Snippets の処理をテーマに移す:プラグインが有効なあいだは読まない形で、止めずに切り替える

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

このサイトでは、ちょっとした PHP の処理を、Code Snippets というプラグインに入れて動かしていました。

有効なスニペットは 13 個ありました。

これを、テーマの中のファイル(functions/site-snippets.php)に、中身のまま移しました。

この記事では、止めずに切り替えるための形と、作業環境で確かめた結果をまとめます。

なぜテーマに移すのか

Code Snippets は、管理画面から PHP を書いて動かせる、便利なプラグインです。

ただ、このサイトのテーマは git で管理していて、push すると自動で公開される流れにしています。

スニペットだけが DB の中にあると、そこだけ流れから外れます。

Code Snippets テーマのファイル
置き場所 DB(スニペットの表) テーマのフォルダ
変えた記録 残りにくい git に残る。前に戻せる
公開のしかた 管理画面で保存すると、すぐ本番で動く push すると自動で公開
管理画面から PHP を書き換えられるか 書き換えられる 書き換えられない

git で管理できることと、管理画面から PHP を書き換える仕組みを減らすことが、移した理由です。

テーマを git などで管理していないサイトや、FTP でファイルを直すのが手間なサイトでは、Code Snippets は今でも便利でおすすめです。

このサイトは、テーマを git で管理しているので、そちらにまとめました。

そのまま移すと止まる

スニペットの中には、関数を作るものがあります。

WordPress は、プラグインを先に読み込み、そのあとでテーマの functions.php を読みます。

プラグインが有効なまま、テーマにも同じ関数を書くと、同じ名前の関数が 2 回作られます。

PHP は、同じ名前の関数を作り直せません。

「Cannot redeclare」という Fatal error になり、サイトが止まります。

テーマを公開してからプラグインを止めるまでのあいだも、サイトは動いていてほしいです。

そこで、「プラグインが有効なあいだは、テーマ側のファイルを読まない」形にしました。

移し方

1. 有効なスニペットを、順番どおりに書き出す

Code Snippets は、優先度の小さい順に、同じ優先度なら id の順にスニペットを動かします。

テーマのファイルにも、同じ順で並べました。

有効なものと順番は、作業環境の DB で次のように出しました(表の頭の wp_ は、サイトの設定に合わせて変えます)。

有効なスニペットを、動く順に出す
SELECT id, name, scope, priority
FROM wp_snippets
WHERE active = 1
ORDER BY priority, id;
書き方 役目
active = 1 有効なスニペットだけ
scope どこで動かすか(どこでも・管理画面だけ・表のページだけ など)
ORDER BY priority, id Code Snippets が動かす順に並べる

このサイトの 13 個は、どれも「どこでも動かす」(global)で、優先度も全部 10 でした。

そのため、id の順に並べるだけで済みました。

「管理画面だけ」などのスニペットがあるときは、テーマ側で is_admin() などを使って分ける必要があります(このサイトにはなかったので、試していません)。

テーマのファイルは、次の形です(関数の名前は例です)。

functions/site-snippets.php(形の例)
<?php
// Code Snippets のプラグインに入れていた処理を、テーマに移したもの。
// 並びは Code Snippets と同じ(優先度 → id)。中身は元のまま。
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/* ---- id 8:(スニペットの名前)(優先度 10) ---- */
function my_example_head() {
	// スニペットの中身をそのまま
}
add_action( 'wp_head', 'my_example_head' );

/* ---- id 9:(スニペットの名前)(優先度 10) ---- */
if ( ! function_exists( 'my_example_cache' ) ) {
	function my_example_cache() {
		// スニペットの中身をそのまま
	}
}

// …(有効なスニペットの数だけ続く)
書き方 役目
if ( ! defined( 'ABSPATH' ) ) exit; このファイルを URL で直接開かれたときに、何もしないで終わる
/* ---- id 8:… ---- */ Code Snippets の一覧の id と名前を見出しにする。あとで元と見比べられる
中身 スニペットのコードを、変えずにそのままコピーする

中身は変えずにコピーしました。

直したいところがあっても、移すのとは別にして、あとで 1 つずつ直します。

2. functions.php で、読むかどうかを分ける

Code Snippets は、読み込まれると CODE_SNIPPETS_FILE という定数を作ります。

この定数があるかどうかで、プラグインが有効かを見分けます。

functions.php(いちばん上)
<?php

// Code Snippets から移した処理。
// Code Snippets のプラグインが有効なあいだは読まない(同じ関数が二重に作られて止まるため)。
// プラグインを止めると、こちらに切り替わる。
if ( ! defined( 'CODE_SNIPPETS_FILE' ) ) {
	require_once ( dirname(__FILE__) . '/functions/site-snippets.php' );
}
書き方 役目
defined( 'CODE_SNIPPETS_FILE' ) Code Snippets が読み込まれていれば true
if ( ! defined( … ) ) プラグインがないときだけ、中に進む
require_once テーマ側のファイルを 1 回だけ読む
いちばん上に書く テーマのほかの処理が、移した関数を使う前に読んでおく

プラグインは、テーマより先に読み込まれます。

そのため、functions.php が動くときには、定数はもうできています。

3. 公開してから、プラグインを止める

順 すること 動いている処理
1 テーマの変更を push する(自動で公開) プラグインのスニペット(テーマ側は読まない)
2 Code Snippets のプラグインを止める テーマ側のファイル
3 しばらく様子を見て、問題がなければ、プラグインを消す テーマ側のファイル

どの順のときも、同じ関数は 1 回しか作られません。

もし何かおかしければ、プラグインを有効に戻すだけで、元の動きに戻せます。

確かめた結果

このサイトの作業環境(PHP 8.3.32)で確かめました。

検証用スクリプトは、作業環境のテーマとは別の場所に置いて動かしました。

定数がどこで作られるか

Code Snippets の本体(3.10.2)を grep で探しました。

定数を作っているのは、本体のファイル code-snippets.php の 1 か所だけでした。

code-snippets.php(一部)
// Code Snippets の本体(code-snippets.php・3.10.2)の一部
if ( ! defined( 'CODE_SNIPPETS_FILE' ) && version_compare( phpversion(), '7.4', '>=' ) ) {
	// …
	define( 'CODE_SNIPPETS_FILE', __FILE__ );
	// …
	require_once dirname( __FILE__ ) . '/php/Core/load.php';
}

まだ定数がなく、PHP が 7.4 以上のときに、定数を作ってプラグインの本体を読み込みます。

プラグインを止めていれば、このファイルは読まれないので、定数もできません。

同じ関数を 2 回作ると止まるか

小さな試しのファイルで確かめました。

検証用スクリプト(同じ関数を 2 回作る)
<?php
// first.php
function my_hello() { return 'first'; }

// second.php
function my_hello() { return 'second'; }

// run1.php:2 つとも読む
require __DIR__ . '/first.php';
require __DIR__ . '/second.php';
echo "not reached";
試したこと 結果
同じ名前の関数を、2 つのファイルで作る Fatal error: Cannot redeclare my_hello()(終了コード 255)。そのあとの行は動かない
2 つ目を if ( ! function_exists( 'my_hello' ) ) で包む 止まらない。1 つ目の関数が使われる(first)

定数があるとき/ないときに、どちらが読まれるか

検証用スクリプト(functions.php と同じ分け方)
<?php
// run3.php:定数があるとき/ないときに、2 つ目のファイルを読むか
$mode = $argv[1] ?? 'off';
if ( 'on' === $mode ) {
	define( 'CODE_SNIPPETS_FILE', __FILE__ ); // プラグインが有効なとき
	require __DIR__ . '/first.php';           // プラグイン側で関数ができている
}
if ( ! defined( 'CODE_SNIPPETS_FILE' ) ) {
	require_once __DIR__ . '/second.php';     // テーマ側
}
echo $mode, ': ', my_hello();
試したこと 結果
定数なし(プラグインを止めたとき) off: second(テーマ側のファイルが読まれた)
定数あり(プラグインが有効なとき) on: first(テーマ側は読まれず、エラーもなし)

作業環境の WordPress で

次に、作業環境の WordPress を読み込む検証用スクリプトで、本物のテーマとプラグインで確かめました。

作業環境では、Code Snippets のプラグインは止めてあり、スニペットの表には有効なものが 13 個残っています。

試したこと 定数 テーマ側のファイル 結果
プラグインなし(今の作業環境) なし 読まれた 移した関数はテーマ側にできた
プラグインあり(読み込むプラグインの一覧に、検証用スクリプトの中だけで足した) あり 読まれない 移した関数は、プラグイン側(スニペット)にできた。エラーなし
プラグインありで、分けずにテーマ側のファイルも読む あり 読んだ Cannot redeclare の Fatal error。WordPress の「重大なエラー」の画面になった

3 つ目は、分ける形がないとどうなるかを見るために、わざと読ませたものです。

ファイルのいちばん初めに出てくる関数の所で止まりました。

画面で見た

最後に、作業環境の DB で、プラグインを一時的に有効にして、トップのページを開きました。

見たあとは、有効なプラグインの一覧を、メモしておいた元の値に戻しました。

プラグイン トップのページ
止めたとき(テーマ側が動く) 200。フッターまで出た。エラーの画面なし
有効なとき(スニペットが動く) 200。フッターまで出た。エラーの画面なし

戻したあとに、プラグインの一覧と Code Snippets の設定・スニペットの表を、試す前と比べ、同じであることを確かめました。

気をつけること

  • 全部の関数を function_exists() で包めば、Fatal error は避けられます。ただ、それでは中身を書き換えることになるので、ファイルごと読まない形にしました
  • プラグインを止めても、スニペットは DB に残ります。すぐに消さず、様子を見てから片付けます
  • Code Snippets には、スニペットを動かさない「セーフモード」があります。コードを読むかぎり、セーフモードでも定数はできるので、そのときはテーマ側も読まれないはずです(試していません)
  • 本番の PHP のバージョンに合わせて、移すときに新しいバージョンだけの書き方を足さないようにしました

まとめ

  • Code Snippets のスニペットは、テーマのファイルに中身のまま移せる。並びは優先度 → id の順にそろえる
  • プラグインが有効なまま同じ関数をテーマに書くと、Cannot redeclare で止まる
  • functions.php で CODE_SNIPPETS_FILE があるかを見て、ないときだけテーマ側を読めば、公開 → プラグインを止める、の順で止めずに切り替えられる
  • おかしいときは、プラグインを有効に戻せば元に戻る

参考

関連機能

  1. WordPress 記事データの複製 Yoast Duplicate Post プラグイン 作業時のバックアップとしても活用

  2. WordPress Duplicatorプラグインを使ったバックアップ&復旧対応手順

  3. サイトマップエラー error on line 2 at column 6: XML declaration allowed only at the start of the document の修正…

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP