WordPressカスタムプラグイン開発:軽量プラグインの構造、フック、公開

独立サイトのちょっとしたカスタマイズは、サードパーティ製プラグインを入れる必要なんてほとんどありません。300行以内の単一ファイルプラグインで十分です。判断基準は、5つ以上の独立した機能が必要ならディレクトリ構造に分け、それ以外はphpファイル1つにプラグインヘッダーコメントを付けるだけです。

自研轻量插件:结构、钩子与发布Lightweight custom plugin: structure, hooks, ship 能用20行钩子解决的事,别装一个5000行的第三方插件Solve it in 20 hook lines instead of a 5000-line plugin 1 主文件1 Main file 插件头注释 + 常量定义Header comment + constants 单文件插件不超过300行Single file under 300 lines 2 钩子2 Hooks add_action / add_filteradd_action / add_filter 后台钩子别挂在前台请求上No admin hooks on frontend 3 激活卸载3 Activate / deactivate register_activation_hookregister_activation_hook 卸载钩子清理自己的表Uninstall hook cleans up 发布方式Shipping 私有插件不上wordpress.orgPrivate plugins skip wp.org 打zip上传 或git拉到mu-pluginsZip upload or mu-plugins 红线Red lines 别改主题functions.phpDo not edit theme functions.php 别在前台直接写SQL不过滤Never run raw SQL unescaped 判定:需求>5个独立功能才拆插件目录,否则单文件插件足够Decision: 5+ features justify a folder, else single file 反直觉:functions.php越写越长不是熟练,是在给下次换主题埋雷Counterintuitive: long functions.php kills the next theme swap 把自定义逻辑从主题搬进插件,主题才能放心换Move logic out of theme into plugins
WordPress軽量プラグインのファイル構造、フックの登録位置、公開方法の比較

WordPressカスタムプラグイン開発:軽量プラグインの構造、フック、公開

結論から言うと:独立サイトのちょっとしたカスタマイズは、サードパーティ製プラグインを入れる必要なんてほとんどありません。300行以内の単一ファイルプラグインで十分です。判断基準は、5つ以上の独立した機能が必要ならディレクトリ構造に分け、それ以外はphpファイル1つにプラグインヘッダーコメントを付けるだけです。直感に反する判断ですが、コードをテーマのfunctions.phpに書き溜めるのは楽に見えても、次にテーマを変えたときそのロジックは全部消えます。

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 よくある質問

どんなときにサードパーティ製を入れず、自分でプラグインを書くべきですか?
要件が送信元メールアドレスの変更、カスタム JS の追加、抜粋の文字数変更など、決まった動作を 1 つ 2 つ変えるだけなら、300 行以内の単一ファイルプラグインを自分で書くほうがすっきりします。独立した機能が 5 つ以上ある場合や管理画面の設定ページが必要な場合に、ディレクトリ構成への分割を検討してください。複雑なフォームや SEO など汎用的な機能が必要なら、実績のあるプラグインを入れます。
コードをテーマの functions.php に書いてはだめですか?
だめです。functions.php はテーマに付随するので、テーマを更新したり別のテーマに変えたりするとカスタムコードはすべて失われます。だからこそロジックを独立したプラグインに移すのです。プラグインはテーマと分離されているため、テーマを変えてもコードはそのまま動きます。目先の手軽さで functions.php に書くと、次にテーマを変えるときにすべて書き直すことになります。
最小構成で動く WordPress プラグインは何行くらいですか?
単一ファイルのプラグインなら、php ファイル 1 つとプラグインヘッダーのコメント(Plugin Name、Description、Version)だけで最小構成になります。あとは必要な add_action や add_filter をフックするだけです。送信元メールアドレスの変更のような要件なら 10 行で足ります。ファイルを wp-content/plugins/ ディレクトリに置けば、管理画面に表示されて有効化できます。
プラグインでデータテーブルを作った場合、アンインストール時に自分で削除しますか?
はい。プラグインディレクトリに uninstall.php を作り、その中に DROP TABLE と delete_option を書きます。register_activation_hook は有効化時のテーブル作成を、uninstall.php は削除時のデータ消去を担当します。deactivate は停止であってデータは消さず、uninstall で削除したときだけ消える点に注意し、この 2 つを混同しないでください。
非公開プラグインは wordpress.org に申請する必要がありますか?
不要です。単一のクライアントサイト向けに書いた私有プラグインは、zipにまとめて管理画面からアップロードするか、wp-content/mu-pluginsディレクトリに置くだけで済みます。MUプラグインはデフォルトで有効になり、誤って停止できないため、コアとなる業務ロジックに向いています。wordpress.orgに申請すべきなのは、汎用的で公開され、他の人にも使ってもらいたいプラグインだけです。
なぜプラグインのコードがフロント側で毎回実行されるのですか?
多くの場合、管理画面用のロジックをinitやplugins_loadedのようなグローバルフックに掛けているのが原因です。管理画面専用のコードはadmin_initやadmin_menuに掛けるべきで、これらはwp-admin以下でしか発火しません。フロント側でCSS/JSを読み込むにはwp_enqueue_scriptsを使い、フックの中で直接echoしてはいけません。

最後に

自作プラグインの価値は技術の見せびらかしではなく、制御できる小さなロジックをサードパーティのプラグイン群から取り戻すことにあります。サードパーティのプラグインは更新で挙動が変わるかもしれませんが、自分のコードは常に自分の手元にあります。

実践は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行のサードパーティ製プラグインで解決するな。コードが少ないほど、制御不能になる範囲は小さくなる。

环形山
环形山 クレーター専門家

公式アカウント · 環形山 Crater

環形山(Crater)は、WordPress独立サイト開発+Google SEO+GEO全域成長に特化した海外展開支援会社で、スマートハードウェア、産業機器、SaaS、新エネルギーなどの分野を手がけています。

プロダクト能力方法論ホワイトペーパー業界レポート
965件のシェア
78件のナレッジベース