WordPressカスタムプラグイン開発:軽量プラグインの構造、フック、公開
1. いつ自分でプラグインを書くべきか
WordPressのカスタマイズはすべてフックに登録します。送信元メールアドレスの変更からカスタムフィールドの追加まで、数十行のPHPで解決できます。問題は、多くの人がまずプラグイン検索をしてしまうことです。
プラグインを入れるか自分で書くかの判断:要件が「決まった動作を1つ変える」だけなら、例えばデフォルトの送信元変更、カスタムJSの追加、抜粋の長さ変更などは、自分でプラグインを書くほうがサードパーティ製よりクリーンです。プラグイン選定の一般的な考え方はWordPress独立サイトの技術スタック。
直感に反するのは、functions.phpが長くなるほど熟練しているのではなく、次にテーマを変えるときの地雷を埋めているということです。テーマの更新や変更でfunctions.phpは上書きされ、コードは全部消えます。正しいやり方はカスタムロジックを独立したプラグインに移し、テーマは見た目だけを担当させることです。
命名にもルールがあります。プラグインディレクトリ名はプレフィックスと機能を組み合わせ、例えばcrater-inquiry-sourceのようにして、公式プラグインと重複しないようにします。関数名も同様にプレフィックスを付け、crater_register_sourceとして、他のプラグインの関数と衝突しないようにします。
関数名の衝突はWordPressプラグインで最もよくある致命的なミスです。2つのプラグインがどちらもfunction register_source()と書くと、後から読み込まれたほうが真っ白な画面になります。すべてのカスタム関数には一意のプレフィックスを付ける必要があります。
| 要件の規模 | 推奨する形態 | ファイル数 |
|---|---|---|
| 小さな変更が1〜3個 | 単一ファイルプラグイン | php 1個 |
| 関連機能が3〜5個 | 単一ファイルプラグイン+定数 | php 1個 |
| 独立した機能が5個以上 | ディレクトリ構造プラグイン | メインファイル+includes+assets |
| 管理画面の設定ページが必要 | ディレクトリ構造プラグイン | admin-page.phpを追加 |
2. 単一ファイルプラグインの最小構造
動作するWordPressプラグインは、最低限phpファイル1つと、冒頭にこれがプラグインであることをWPに伝えるプラグインヘッダーコメントがあれば十分です。ファイルをwp-content/plugins/に置けば、管理画面に表示されます。
プラグインヘッダーコメントの固定フォーマット:Plugin Name、Description、Version、Authorの各項目です。このコメントがないと、WPはプラグインとして認識しません。
シナリオ例1:デフォルトの送信元メールアドレスを変更する。プラグイン全体でわずか10行だ。wp_mail_fromとwp_mail_from_nameの2つのfilterをフックして、デフォルトのwordpress@をnoreply@example.comに変える。設定ページは一切不要。
こうした純粋なfilter型プラグインは入門に最適だ。データベースも管理画面も定期実行タスクもなく、問題が起きたらプラグインを無効化すれば元に戻る。
設定を保存する必要があるなら、WordPress標準のoptionsテーブルを使い、1レコードに1つの配列を保存する。自分でテーブルを作らないこと。簡単な設定ならget_optionとupdate_optionで十分だ。
| フックの種類 | よく使うフック | 用途 |
|---|---|---|
| filterフィルター | wp_mail_from、the_content | 出力内容を変更する |
| actionアクション | wp_head、admin_init | コードを挿入する、または処理を実行する |
| 管理画面での操作 | admin_menu、admin_init | 設定ページを追加し、管理画面でのみ実行する |
| フロント側のアクション | wp_enqueue_scripts | CSS/JSを読み込み、フロント側でのみ実行する |
フックをどこに掛けるかはパフォーマンスの要だ。admin_initは管理画面の読み込み時のみ発火し、wp_headはフロント側のページ読み込み時のみ発火する。管理画面専用の処理をinitに掛けてはいけない。フロント側のリクエストのたびに無駄に実行されてしまう。
自前のCSSとJSを読み込むにはwp_enqueue_scriptsフックを使う。wp_headで直接echoしてはいけない。直接echoするとスクリプトが重複して読み込まれる。
シナリオ例3:フォームページで検証スクリプトを読み込みたい。wp_enqueue_scriptで登録し、依存にjqueryを指定、5番目の引数をtrueにしてフッターで読み込ませ、初回表示をブロックしないようにする。
三、有効化、アンインストール、データのクリーンアップ
プラグインがデータテーブルを作成したりoptionを書き込んだりする場合、有効化とアンインストールを必ず処理する。有効化時にテーブルを作り、アンインストール時に削除する。そうしないとプラグインを削除してもデータベースにゴミが残る。
register_activation_hookはプラグインの有効化時に発火し、テーブル作成やデフォルトoptionの書き込みに適している。uninstall.phpはプラグイン削除時に発火し、データのクリーンアップに適している。deactivateとuninstallは別物だ。無効化ではデータは消えず、削除で初めて消える。
シナリオ例2:プラグインが問い合わせの流入元をカスタムテーブルに記録する。有効化時にdbDeltaでテーブルを作成し、アンインストール時にDROP TABLEする。ユーザーが無効化して再度有効化してもデータは残る。プラグインを削除するとテーブルも一緒に消える。
| ライフサイクル | フック | やるべきこと |
|---|---|---|
| プラグインを有効化 | register_activation_hook | テーブル作成、デフォルトoptionの書き込み |
| プラグインを無効化 | register_deactivation_hook | 定期実行タスクを削除、データは触らない |
| プラグインを削除 | uninstall.php | テーブル削除、option削除 |
| 毎回の読み込み時 | plugins_loaded | 定数の定義、テキストドメインの読み込み |
セキュリティ面の注意:自作プラグインでユーザー入力を扱う場合は、必ず$wpdb->prepareで前処理すること。SQLを直接連結してはいけない。ユーザー権限と管理画面権限のベースライン設定については、次を参照。WordPressセキュリティベースライン。PHPバージョンの互換性については、次を参照。独立サイトのPHPバージョンアップ。
四、公開と保守:プライベートプラグインは公式ディレクトリに公開しない
特定のクライアントサイト向けに書く非公開プラグインなので、wordpress.org への申請は不要です。zip にまとめて管理画面からアップロードするか、wp-content/mu-plugins ディレクトリに直接 git clone します。MU プラグインはデフォルトで有効になり、誤って停止できません。
バージョン管理は必須です。プラグインヘッダーの Version 番号を変更のたびに上げれば、管理画面に更新が表示されます。本番サイトでプラグインファイルを直接編集せず、ローカルで直してからアップロードしてください。
crater チームの自社開発プラグインも同じ考え方です。詳しくは環形山テックスタックをご覧ください。どれも具体的な小さな課題を解決するもので、ファイルは 2 つ以内に収まっています。
| 公開方法 | 適用シーン | メリット | リスク |
|---|---|---|---|
| zip を管理画面からアップロード | 非公開プラグインをクライアントへ納品 | クライアントが自分で停止できる | 誤って削除されるおそれ |
| mu-plugins ディレクトリ | 中核ロジックを止めさせない | デフォルトで有効、誤って停止できない | 更新は手動 |
| wordpress.org への申請 | 汎用の公開プラグイン | 自動更新 | 審査を通し、ドキュメントを書く |
バージョン番号の管理は省略しないでください。変更のたびにプラグインヘッダーの Version を上げると、管理画面のプラグイン一覧に「更新あり」の表示が出ます。
自分で管理する非公開プラグインには公式の更新経路がありません。git で管理し、デプロイ時はサーバーのディレクトリへ直接 git pull するのがおすすめです。本番サイトでファイルを直接編集しないでください。
FAQ よくある質問
どんなときにサードパーティ製を入れず、自分でプラグインを書くべきですか?
コードをテーマの functions.php に書いてはだめですか?
最小構成で動く WordPress プラグインは何行くらいですか?
プラグインでデータテーブルを作った場合、アンインストール時に自分で削除しますか?
非公開プラグインは wordpress.org に申請する必要がありますか?
なぜプラグインのコードがフロント側で毎回実行されるのですか?
最後に
自作プラグインの価値は技術の見せびらかしではなく、制御できる小さなロジックをサードパーティのプラグイン群から取り戻すことにあります。サードパーティのプラグインは更新で挙動が変わるかもしれませんが、自分のコードは常に自分の手元にあります。
実践は3ステップです。functions.phpにあるカスタムロジックを1つずつ単一ファイルのプラグインへ移す。テーブルを作ったならuninstallで削除処理を書く。私有プラグインはzipにするかmu-pluginsに置き、公式ディレクトリへの申請は見送る。
よくある疑問が1つあります。プラグインを書くのにComposerやフレームワークを使うべきか? 単一のクライアントサイト向けの軽量プラグインには不要です。
フレームワークを導入すると、vendorディレクトリが数十MB増え、更新のたびに依存関係を保守することになります。300行で書けることをフレームワークでやるのは、自分で自分の首を絞めるようなものです。
Composerや名前空間を導入する価値があるのは、プラグインを公開製品にし、ユニットテストを書き、複数人で開発する場合だけです。
セキュリティについてもう一点。プラグインで管理画面のフォームを処理する際は、必ずcheck_admin_refererでnonce検証を行い、クロスサイトリクエストフォージェリを防いでください。これはWordPressの管理画面フォームの標準的な作法で、漏らすと管理画面のインターフェースを無防備に晒すことになります。
ユーザー入力をページに出力する際は、esc_htmlやesc_attrといったエスケープ関数を使い、直接echoしないでください。自分で管理画面に書いた設定値であっても、フロント側に出力するならエスケープが必要です。
最後に、取り掛かる順序を1つ挙げます。初めてプラグインを書くなら、送信元の変更や抜粋の文字数変更のような純粋なfilterから始め、データベースにも管理画面ページにも触らないことです。
「コードを書く、アップロードする、有効化する、効果を確認する」という一連の流れを一度通してから、少しずつ機能を足していきます。最初からデータテーブルや管理画面ページを伴うプラグインを書くと、フックとライフサイクルに振り回されがちです。
プラグインは小さく書くほど正解です。機能を1つ足す前に、既存のフックで20行で書けないか自問してください。書けるならテーブルを作らない、書けるなら管理画面ページを足さない。
プラグインのコード量を300行以内に抑えることは、自分のためでもありクライアントのためでもあります。この規模を超えるなら、成熟したサードパーティのプラグインを使うべきでしょう。
最後にもう一度強調します。カスタムロジックをテーマのfunctions.phpに積み上げないでください。独立したプラグインへ移せば、テーマを安心して入れ替えられ、クライアント自身が長く保守できます。
覚えておきたい一言:20行のフックで済むことを、5000行のサードパーティ製プラグインで解決するな。コードが少ないほど、制御不能になる範囲は小さくなる。
