WordPress・CMS設計

WordPressの自作プラグインとfunctions.phpの境界線:機能の肥大化を防ぐ設計ルール

WordPressの案件で、機能追加のたびにテーマのfunctions.phpへコードを書き足していく。最初は数十行だったファイルが、いつの間にか数百行になり、カスタム投稿タイプ、ショートコード、管理画面の調整、SEOメタデータ、アクセス解析、外部API連携まで同居する。…

WordPressの自作プラグインとfunctions.phpの境界線:機能の肥大化を防ぐ設計ルール

そしてテーマを変更した日、サイトの一部が静かに壊れる。カスタム投稿タイプが消え、本文にはショートコードがそのまま表示され、管理画面の設定項目も見当たらない。よくある話だ。しかも、壊れた理由はWordPressの気まぐれではない。最初から、テーマに依存させてはいけない機能をテーマへ預けていたのである。

WordPressで「プラグインを自作するか、functions.phpに書くか」を考えるとき、処理速度やコード量だけを見てはいけない。判断の中心に置くべきなのは、その機能がテーマに属するのか、サイトそのものに属するのかという境界線だ。

結局のところ、functions.phpと自作プラグインの違いは、便利なコード置き場の違いではない。機能の寿命と責任範囲を決める設計上の違いである。

テーマ依存とサイト依存を最初に切り分ける

functions.phpは、現在有効になっているテーマと一体になって動くファイルだ。テーマが有効な間は読み込まれるが、別のテーマへ切り替えれば、そのテーマのfunctions.phpが読み込まれる。

この挙動は、テーマ固有の表示機能にとっては自然だ。たとえば、見出しのクラスを追加する処理、テーマ専用のウィジェット、独自のナビゲーション、カスタムブロックの見た目を整えるための設定などは、テーマの構造と結びついている。

一方で、サイトのコンテンツや運用データに関わる機能は話が変わる。カスタム投稿タイプやショートコード、SEO用のメタデータ、アクセス解析の識別子、フォーム送信処理などは、テーマを変更しても残っていなければ困る。

この二つを混ぜると、テーマ変更が単なるデザイン変更ではなく、サイト機能の入れ替え作業になる。見た目を変えたいだけなのに、裏側の機能まで巻き込んで移植することになるわけだ。WordPressには、こういう理不尽な仕様に見える事故を自分で育ててしまう罠がある。

functions.phpに向いている機能

テーマのfunctions.phpに置くのは、基本的に「このテーマで表示するために必要な処理」だ。

具体的には、次のようなものが該当する。

  • テーマ専用のCSSやJavaScriptを読み込む処理
  • テーマ独自のウィジェットやメニュー位置の登録
  • 投稿一覧や本文の表示形式を調整するフィルター
  • テーマで使う画像サイズの追加
  • カスタムブロックの表示スタイル
  • テーマ専用のテンプレートタグ
  • 現在のデザインにだけ必要な body class の追加
  • テーマ固有のエディター設定やブロックパターン

たとえば、カード型の記事一覧を表示するために、特定のテーマ専用クラスを追加する処理があるとする。その処理は、別テーマへ切り替えれば不要になる可能性が高い。ならば、テーマ側に置いてよい。

テーマを捨てたら一緒に捨てても構わない。その程度の寿命を持つ機能なら、functions.phpは十分な置き場所だ。

自作プラグインに向いている機能

反対に、テーマ変更後もサイト全体で使い続けたい機能は、自作プラグインへ切り出す。

代表例は次の通りだ。

  • カスタム投稿タイプの登録
  • カスタムタクソノミーの登録
  • ショートコード
  • 投稿本文に保存される独自データの管理
  • SEOメタデータの保存や出力
  • アクセス解析や広告の識別情報
  • 外部APIとの連携
  • 管理画面の設定ページ
  • 会員情報や注文情報など、テーマから独立したデータ処理
  • REST APIのエンドポイント追加
  • WP-CLI用のコマンド
  • サイト全体で共通利用するカスタムブロックのロジック

ここで大事なのは、表示されるかどうかではなく、機能の所有者が誰かという点だ。

カスタム投稿タイプのデータは、テーマの所有物ではない。テーマを変えても、投稿データは残るべきである。同じように、ショートコードを使って長年蓄積した記事も、テーマを変更しただけで未変換の文字列になるべきではない。

テーマは見た目を担当し、プラグインはサイトの機能を担当する。この線引きが崩れると、デザイン変更が改修案件に化ける。

判断に迷ったときの実務的な問い

「これはプラグインか、functions.phpか」と悩んだら、次の問いに置き換えると判断しやすい。

そのテーマを明日やめても、この機能は必要か。

答えが「必要」なら、まず自作プラグインを候補にする。答えが「不要」なら、テーマ側に置く余地がある。

もう一つ、運用担当者の視点も役に立つ。テーマを変更する担当者と、サイト機能を管理する担当者が別なら、機能をfunctions.phpへ押し込むほど引き継ぎが難しくなる。テーマを納品した会社が変わった途端、サイト専用の処理が見つからなくなるという事故は、技術的には単純でも、現場ではかなり高くつく。

機能functions.php自作プラグイン
テーマ専用のCSS・JS読み込み向いている基本的に不要
テーマ固有の表示調整向いている基本的に不要
カスタム投稿タイプ避けたい向いている
ショートコード原則避けたい向いている
SEOメタデータテーマ非依存なら避けたい向いている
アクセス解析コードサイト全体で使うなら避けたい向いている
カスタムブロックの表示スタイルテーマ側が自然ロジック部分は分離可能
管理画面のサイト設定避けたい向いている
テーマ変更時に消えてよい処理向いている必須ではない

もちろん、現実の案件はこの表だけで機械的に決められない。テーマとプラグインの両方に関係する機能もある。それでも、最初の仕分けとしてはかなり強い。

WordPressの読み込み順序を見ると境界線がはっきりする

設計の話をすると、つい「保守しやすいから」「一般的に推奨されているから」という曖昧な説明になりがちだ。しかし、WordPressが実際にどの順番で処理を読み込むかを見ると、なぜプラグインがサイト機能に向いているのかが分かる。

一般的なリクエストでは、index.phpから始まり、wp-blog-header.phpwp-load.phpwp-config.phpを経由して、wp-settings.phpが読み込まれる。その中でWordPress本体の初期化が進み、通常のプラグインが読み込まれ、その後に有効テーマのfunctions.phpが読み込まれる。

大まかな流れは次のようになる。

1. index.phpからWordPressの処理が始まる

2. wp-blog-header.phpなどを通じてWordPress本体を読み込む

3. wp-config.phpでサイト設定を読み込む

4. wp-settings.phpでWordPressの初期化を進める

5. 通常のプラグインを読み込む

6. テーマを読み込み、テーマのfunctions.phpを実行する

7. 各種フックに応じて処理を進める

プラグインはテーマより前に読み込まれる。ここが重要だ。

たとえば、テーマのfunctions.phpで登録したカスタム投稿タイプを、そのテーマが無効になっている状態で参照することはできない。テーマを切り替えれば登録処理そのものがなくなるため、管理画面から該当投稿タイプが消えたり、既存のURLが期待通りに解決されなくなったりする。

プラグインに登録処理を置けば、テーマの状態に左右されずに機能を初期化できる。もちろん、プラグインが停止されていれば動かない。しかし、それは管理者が機能を停止したという明示的な状態だ。テーマ変更に巻き込まれて、意図せず機能が消える状態とは意味が違う。

functions.phpは「共通ファイル」ではない

初心者向けの記事では、functions.phpがテーマの機能をまとめるファイルとして紹介されることが多い。その説明自体は間違っていない。ただし、そこから「サイトに必要なPHPは全部ここへ書く」という結論へ進むと、あとで苦労する。

functions.phpという名前が、いかにもサイト全体の共通関数を置く場所に見えるのもよくない。実際には、テーマが有効化されているときだけ読み込まれるテーマの一部である。

つまり、functions.phpは便利な入口ではあるが、アプリケーション全体のサービス層でも、サイト機能の保管庫でもない。

ここを誤解すると、最初の数週間は開発が速い。ファイルを開いて処理を追加し、管理画面を確認すれば終わるからだ。しかし、テーマ変更、担当者交代、プラグインとの競合、WordPress本体の更新がやってくると、便利さの請求書が届く。

plugin_loadedと読み込みタイミング

WordPressのプラグインは、初期化の過程で読み込まれ、plugin_loadedなどの仕組みを通じてプラグインの読み込みに関わる処理を組み立てられる。細かいフックの順番を暗記する必要はないが、プラグインがテーマより前に存在するという事実は、依存関係を設計するうえで知っておきたい。

テーマ側の処理がプラグインの機能を利用する設計は自然だ。たとえば、自作プラグインでカスタム投稿タイプや設定値を用意し、テーマはそれを表示する。

逆に、サイトの基礎機能をテーマ側で登録し、プラグインや別テーマがそれを前提にする構成は危うい。見た目を担当する層が、データ構造の責任まで背負ってしまうからだ。

「プラグインは重い」という話を一度解体する

「functions.phpに書けば軽いが、プラグインにすると遅くなる」という説明は、WordPress界隈で何度も登場する。半分だけ正しく、半分は誤解である。

同じPHPコードを同じ条件で実行するなら、functions.phpに書いたか、自作プラグインに書いたかだけで、処理速度やサーバー負荷に本質的な差が出るわけではない。プラグインという箱に入れた瞬間、PHPが急に重い言語へ変わるわけではない。

重さの原因になるのは、どのファイルに書いたかではなく、何を実行しているかだ。

たとえば、次のような処理は、置き場所に関係なく負荷を生みやすい。

  • 毎回のページ表示で外部APIへ接続する
  • 必要のないクエリを何度も発行する
  • 投稿一覧のループ内で追加クエリを繰り返す
  • 管理画面でしか使わない処理をフロント側でも実行する
  • 設定値を毎回計算し、キャッシュしていない
  • 保存処理のたびに大量の関連データを更新する
  • 不要なCSSやJavaScriptを全ページで読み込む

逆に、単純なフック登録や軽量な文字列処理をプラグインへ移しただけで、急にサイトが遅くなると考える理由は薄い。

「プラグインだから重い」のではなく、重い処理をするプラグインが存在するだけだ。無料プラグインを大量に追加して設定画面もデータベースも増殖させた結果を、プラグインという仕組み全体の責任にするのは少し乱暴である。

データベースに残るものと、実行時に読むもの

プラグインの負荷を考えるときは、実行時のPHP処理と、データベースに何を残すかを分けて見る。

単一のPHPファイルにフックを登録するだけの自作プラグインであれば、処理内容が同じならfunctions.phpとの差は本質的ではない。一方、プラグインによっては、独自テーブルを作成したり、オプションを大量に保存したり、アンインストール後もデータを残したりするものがある。

ここで問題になるのは、プラグインという形式そのものではなく、データ設計と実行設計だ。

サイト専用プラグインを作る場合も、次のように処理を整理するとよい。

  • フロント側で毎回読む必要のない設定は、必要な場所だけで取得する
  • 管理画面専用の処理は管理画面でだけ読み込む
  • 外部APIの結果はキャッシュする
  • 投稿ループ内で同じ値を何度も取得しない
  • 不要になったデータをアンインストール時にどう扱うか決める
  • 既存のWordPress APIを使い、独自処理を増やしすぎない

性能面で本当に見るべきなのは、プラグインかfunctions.phpかではない。クエリ数、外部通信、キャッシュ、アセットの読み込み範囲、そしてデータの持ち方である。

置き場所を変えただけで高速化する、という話はだいたい都合のよい神話だ。速くしたいなら、まず何を何回実行しているかを見る。

サイト専用自作プラグインは、思っているほど大げさではない

「自作プラグイン」と聞くと、オブジェクト指向設計、ビルド環境、テストスイート、Composer、名前空間といった単語が頭に浮かぶかもしれない。もちろん、大規模な製品として配布するなら、それらは有効だ。

しかし、特定のサイトだけで使う機能を切り出すだけなら、そこまで身構える必要はない。WordPressのプラグインは、/wp-content/plugins/ディレクトリ内に独自のフォルダを作り、その中に標準的なヘッダーを持つPHPファイルを置くところから始められる。

たとえば、次のような構成だ。

  • /wp-content/plugins/my-site-plugin/
  • /wp-content/plugins/my-site-plugin/my-site-plugin.php

PHPファイルの冒頭には、Plugin Name:Description:Version:Author:License:といったプラグイン情報ヘッダーを記述する。これによって、WordPressの管理画面にプラグインとして表示される。

最小構成を作るだけなら、特別なビルド環境は必要ない。独自のフォルダとPHPファイル、標準ヘッダーがあればよい。ここを難しく考えすぎて、結局すべてをfunctions.phpに書き続けるというのは、少しもったいない。

作成の手順

サイト専用プラグインを作る流れは、次のようになる。

1. プラグイン用のフォルダを作る

/wp-content/plugins/の下に、サイト固有の名前を付けたフォルダを用意する。既存プラグインと名前が衝突しないよう、サイト名や制作会社名を含めると管理しやすい。

2. メインPHPファイルを作る

フォルダ名と同じ名前、または役割が分かる名前のPHPファイルを作成する。

3. 標準プラグインヘッダーを記述する

Plugin Name:などの情報をファイル冒頭に記述し、管理画面で認識できる状態にする。

4. 機能を一つずつ移動する

いきなり巨大なfunctions.phpを丸ごと移すのではなく、カスタム投稿タイプ、ショートコード、設定処理など、責任範囲ごとに切り出す。

5. テーマ側の依存関係を確認する

プラグインが停止してもサイトが致命的に壊れないよう、テーマ側で存在確認を行う。プラグインの関数を必ず呼べるとは限らないからだ。

6. テーマを切り替えて動作を確認する

テスト用テーマへ切り替え、カスタム投稿タイプやショートコード、管理画面の設定が維持されるか確認する。

7. バージョン管理とバックアップの対象にする

自作プラグインはサイトの重要な構成要素である。サーバー上に置いたままにせず、Gitなどで変更履歴を管理する。

最初から完璧な構造を作ろうとしなくてよい。目的は、functions.phpを美しい芸術作品にすることではない。テーマ変更でサイト機能が巻き添えになる状態を止めることだ。

カスタム投稿タイプは特に早く切り出す

カスタム投稿タイプをfunctions.phpに登録しているサイトは多い。しかし、これは優先的にプラグインへ移したい処理の一つだ。

カスタム投稿タイプは、単なる表示パーツではない。管理画面のメニュー、投稿データ、パーマリンク、REST API上の扱いなど、サイトのコンテンツ構造に関わる。

テーマを切り替えたら投稿タイプの登録が消える構成では、データが削除されたわけではなくても、管理画面から見えなくなる。URLの解決やテンプレートの選択にも影響し、運用担当者から見ると「記事が消えた」ように見えることがある。

この種の機能は、サイトのデータモデルとして扱うべきだ。表示するテンプレートはテーマに置き、投稿タイプそのものの登録はプラグインに置く。この分離ができていれば、デザイン変更とコンテンツ構造の変更を別々に進められる。

ショートコードをテーマへ埋め込む罠

ショートコードも同じだ。

記事本文に保存されているショートコードは、テーマが有効なときに登録処理が存在して初めて変換される。登録処理をfunctions.phpへ置いたままテーマを変更すると、本文にショートコードの文字列が露出する可能性がある。

記事が長年蓄積されたサイトほど、この問題は厄介だ。数件の記事なら手作業で修正できるが、数百件、数千件になると、テーマ変更のために本文データを洗い出す作業が発生する。

ショートコードの見た目を整える処理はテーマにあってもよい。しかし、ショートコードという機能自体を登録する処理は、サイト専用プラグインへ置く方が安全だ。

functions.phpの肥大化を防ぐ現実的な方法

ここまで読むと、「では、functions.phpにはほとんど何も書かない方がよいのか」と思うかもしれない。そこまで極端に振る必要はない。

テーマ固有の処理をfunctions.phpへ置くこと自体は正しい。ただし、すべてを一つのファイルに押し込まないことだ。functions.phpの肥大化は、機能の数だけでなく、責任範囲が見えなくなることに問題がある。

処理ごとにファイルを分割する

テーマ側に残す処理でも、役割ごとにファイルを分けられる。

たとえば、次のような分類だ。

  • inc/enqueue.php:CSSやJavaScriptの読み込み
  • inc/theme-support.php:テーマサポートの登録
  • inc/widgets.php:ウィジェットやサイドバー
  • inc/editor.php:エディター設定
  • inc/template-tags.php:テーマ専用の表示関数
  • inc/block-patterns.php:ブロックパターンの登録

functions.phpでは、これらをinclude_onceなどで読み込むだけにする。WordPressテーマの構造によっては、get_template_part()を使ってテーマ内の処理を分割する方法もある。

この方法は、処理の場所を整理するだけで、機能の所有者を変えるものではない。サイト全体の機能をテーマに置いたままファイル分割しても、テーマ変更で消える問題は解決しない。そこは混同しない方がよい。

「サイト専用プラグイン」と「配布用プラグイン」は分けて考える

自作プラグインを作るとき、WordPress.orgで配布されているような汎用プラグインを目指す必要はない。

特定のサイトだけで使うなら、サイト専用プラグインとして割り切ってよい。管理画面に複雑な設定を用意せず、定数やコード上の設定で管理することもある。利用者が不特定多数ではないため、汎用性よりも、要件に対する分かりやすさを優先できる。

ただし、サイト専用だから雑にしてよいわけではない。最低限、次の情報は残しておきたい。

  • このプラグインが何を担当するか
  • どのテーマやプラグインに依存しているか
  • 停止した場合に何が使えなくなるか
  • データを作成するか、削除時にどう扱うか
  • 管理画面のどこに設定があるか
  • 外部サービスやAPIキーを使うか
  • 最終更新日と変更履歴

未来の自分は、ほぼ他人だ。半年後の自分に向けてREADMEを残すだけでも、炎上案件の温度はかなり下がる。

mu-pluginsを使う場面

通常のプラグインとは別に、Must-Useプラグイン、いわゆるmu-pluginsを使う方法もある。mu-pluginsは通常のプラグイン画面から有効化・停止する方式ではなく、決められたディレクトリに置かれたものが自動的に読み込まれる。

サイトの基盤処理や、管理者が誤って停止してはいけない処理に向いている場合がある。ただし、通常のプラグインより運用上の見通しが悪くなることもある。管理画面で簡単に状態を切り替えられないため、チーム内で存在場所と役割を共有しておかなければならない。

「必ず読み込ませたいからmu-plugins」という判断は分かるが、何でも入れると、今度は見えない場所に機能が増える。便利な仕組みには、必ず別の罠がある。

個人開発や小規模サイトでは、まず通常のサイト専用プラグインで管理し、停止されると困る基盤処理だけをmu-pluginsへ移すくらいが現実的だ。

プラグインとテーマの依存関係を安全に扱う

自作プラグインを導入すると、今度はテーマとプラグインの依存関係を考える必要が出てくる。ここを無視して「分離したから安全」と言うことはできない。

たとえば、プラグインが登録したカスタム投稿タイプをテーマが表示する場合、テーマはその投稿タイプが存在する前提でテンプレートを組むことになる。プラグインが停止されれば、テーマのテンプレートが想定外の状態になる。

テーマ側で、プラグインの関数や定数が存在するかを確認する設計にしておくと、停止時の致命的なエラーを避けられる。もちろん、機能そのものが使えなくなることはある。しかし、サイト全体が真っ白になるのと、該当機能だけが表示されないのでは、復旧のしやすさが違う。

依存関係を文書化する

自作プラグインのヘッダーやREADMEには、依存するテーマやプラグインを明記しておくとよい。

たとえば、次のような関係を整理する。

  • このプラグインは特定テーマの表示関数を呼び出す
  • このテーマは特定プラグインのカスタム投稿タイプを表示する
  • この機能には別のフォームプラグインが必要
  • プラグイン停止時は該当ショートコードが表示されなくなる
  • テーマ変更時にはテンプレートファイルの移植が必要

ここまで書くと、完全な独立ではないことが見えてくる。それでも、データ構造と表示構造を分けるだけで、変更時の作業量は大きく変わる。

CMS設計において、依存関係をゼロにすることは難しい。目指すべきなのは、依存関係を隠さず、変更する層を限定することだ。

Gutenbergやカスタムブロックでは責任を分ける

ブロックエディターを使うサイトでは、テーマとプラグインの境界がさらに曖昧になりやすい。

カスタムブロックには、編集画面での入力、保存されるデータ、フロント側の表示、スタイル、スクリプトなど複数の要素がある。これらをすべてテーマに置くと、デザイン変更でブロックまで巻き込まれる。

一方で、ブロックの見た目はテーマに強く依存する。色、余白、文字サイズ、レイアウトといった部分までプラグインに固定すると、テーマを変更しても古いデザインが残る。

そこで、次のように分けると扱いやすい。

  • ブロックの機能や保存データ:プラグイン
  • ブロックの登録ロジック:プラグイン
  • サイト全体で使う属性や設定:プラグイン
  • 色や余白などの視覚的な調整:テーマ
  • テーマ固有のレスポンシブ対応:テーマ
  • 特定テーマでのみ必要な表示補正:テーマ

ブロックがサイトのコンテンツとして残るなら、ブロックの存在そのものはテーマから切り離すべきだ。テーマを変えたら編集画面でブロックが消える、という構成は、コンテンツをデザインの所有物にしてしまっている。

ただし、ブロックの見た目を完全にプラグインから分離できるとは限らない。現場では、ブロックの基本スタイルをプラグインに持たせ、テーマ側で上書きする形もある。理想的な完全分離より、変更時にどこを直せばよいか分かる構成の方が役に立つ。

既存のfunctions.phpを移行するときの順番

すでにfunctions.phpが巨大化している場合、最初から全体を作り直す必要はない。むしろ、勢いで全面改修すると別の事故を呼ぶ。

まず、現在の処理を役割ごとに棚卸しする。

1. データを作る処理を探す

最初に確認するのは、カスタム投稿タイプ、タクソノミー、メタフィールド、設定値など、サイトのデータ構造に関わる処理だ。

これらはテーマ変更の影響を受けやすい。表示部分と切り離せるものから、サイト専用プラグインへ移す。

2. 本文やURLに影響する処理を探す

ショートコード、リライトルール、REST API、本文フィルターなどは、記事やURLの継続性に関わる。

処理を移すときは、関数名やショートコード名を変えないことが重要だ。名前を変えると既存記事との互換性が切れる。新しい設計にしたい気持ちは分かるが、移行作業では過去データを敵に回さない方がよい。

3. 外部サービス連携を探す

アクセス解析、広告、フォーム、決済、メール配信など、外部サービスに接続する処理もテーマから切り出しやすい。

ただし、表示上のタグをテーマが管理している場合は、設定の保管とHTML出力を分けるとよい。識別子やAPIキーの管理はサイト機能としてプラグイン側に置き、テーマ固有のマークアップだけテーマ側で扱う、といった分割ができる。

4. 表示専用の処理を残す

最後に、現在のテーマでしか使わないテンプレートタグやCSS読み込み、表示フィルターをテーマ側に残す。

この順番なら、最初にサイトの土台を安定させ、その後でテーマの整理へ進められる。全処理を一気に移動するより、問題の範囲を小さくできる。

どこまでコードで解決し、どこから運用で割り切るか

設計をきれいにしようとすると、すべてを自動化したくなる。プラグインが停止されたら警告を出す、依存するテーマが違えば処理を変える、古いデータを自動移行する。できることは多い。

しかし、すべてをコードで解決する必要はない。

たとえば、サイト専用プラグインが必須で、停止すると記事の一部が表示できなくなるなら、管理手順書に「このプラグインは停止しない」と書くことも一つの解決策だ。管理画面に複雑な制御を追加するより、運用ルールを一行残す方が安全なケースもある。

逆に、停止やテーマ変更によってデータが消える、既存記事が壊れる、外部サービスへ重複送信する、といった事故につながるなら、コードで防ぐ価値がある。

コードで防ぐべきもの

  • カスタム投稿タイプの登録漏れ
  • 既存ショートコードの未登録
  • 必須設定がない状態での致命的エラー
  • 同じデータを二重登録する処理
  • 権限のないユーザーによる管理処理
  • 外部APIへの無制限なリクエスト
  • テーマ変更でデータ自体が失われる構成

運用で管理してよいもの

  • サイト専用プラグインの更新手順
  • テーマ変更前のバックアップ
  • テスト環境での表示確認
  • プラグイン停止が必要なメンテナンス手順
  • 外部サービスのAPIキー更新
  • 管理者向けの設定変更ルール
  • テーマとプラグインのリリース順序

力技だが、運用で済むものまで自動化しない方が、長期的には保守しやすい。コードが増えるほど、別のバグが入り込む余地も増えるからだ。

迷ったら「変更の単位」で考える

最終的な判断基準は、機能の名前ではなく変更の単位に置くとよい。

デザインを変更したいときに一緒に変わる機能はテーマ側へ置く。デザインを変更しても残したい機能はプラグイン側へ置く。

たとえば、記事一覧のカード表示はテーマの仕事だ。しかし、その記事を管理するカスタム投稿タイプはサイト機能である。カスタムブロックの余白はテーマに属し、ブロックが保存する業務データはプラグインに属する。SEOのタイトルをどう見せるかはテーマの領域に入りうるが、メタデータを保存し、管理画面で編集する機能はプラグインへ切り出した方が扱いやすい。

この境界線は、毎回きれいに引けるものではない。案件の規模、担当者、将来のテーマ変更予定、運用体制によって変わる。それでも「テーマを変えても必要か」という問いを通せば、かなりの混乱は減らせる。

functions.phpに書くこと自体が悪いのではない。テーマの責任範囲を越えて書き続けることが問題なのだ。自作プラグインも、作っただけで設計が改善するわけではない。サイト機能をまとめた結果、今度は何でも入った巨大プラグインになれば、問題の引っ越しにすぎない。

まずは、テーマに依存するもの、サイトに依存するもの、外部サービスに依存するものを分ける。次に、データを扱う処理と表示だけの処理を分ける。そして、必要以上に高度な構成へ走らず、運用で管理できる部分は運用へ戻す。

結局のところ、WordPressの自作プラグインとfunctions.phpの使い分けは、速度競争でも宗教戦争でもない。サイトが何年使われるのか、テーマを何度変更するのか、誰が保守するのかという現実の話だ。

テーマは着替える。サイトのデータと機能は、できるだけ着替えに巻き込まない。その程度の境界線を最初に引いておけば、未来の自分が夜中にfunctions.phpを開いて、謎のコードを一行ずつ追いかける時間は減らせる。幸い、WordPressのこの問題は、巨大な仕組みを導入しなくても解決できる。小さなサイト専用プラグインを一つ作るだけで、かなりの火種を外へ出せるのである。

Related reading: WordPress・CMS設計についてよくある質問 and theme.jsonとカスタムCSSの競合:ブロックテーマ開発で破綻しないスタイル優先順位の設計ルール.

よくある質問

functions.phpと自作プラグインのどちらに書くべきか判断する基準は?
そのテーマを明日やめても機能が必要かどうかで判断します。必要であれば自作プラグイン、不要であればテーマのfunctions.phpに置くのが適切です。
カスタム投稿タイプの登録はどこに書くのが正解ですか?
自作プラグインに書くべきです。テーマのfunctions.phpに書くと、テーマを変更した際に管理画面から投稿タイプが消えたり、記事データが正しく表示されなくなったりするリスクがあるためです。
自作プラグインを作るとサイトが重くなるというのは本当ですか?
いいえ、それは誤解です。プラグインかfunctions.phpかという置き場所の違いだけで処理速度が本質的に変わることはなく、重さの原因は実行される処理の内容やクエリの数にあります。
functions.phpが肥大化している場合、どう整理すればいいですか?
役割ごとにファイルを分割して読み込むか、データ構造や外部連携に関わる処理から優先的に自作プラグインへ移行してください。一度にすべてを改修せず、責任範囲ごとに棚卸しを進めるのが安全です。
自作プラグインを作るには高度な開発環境が必要ですか?
いいえ、特別なビルド環境は不要です。プラグイン用のフォルダを作成し、標準的なヘッダー情報を記述したPHPファイルを置くだけでWordPressに認識されます。

参考情報