はじめに
こんにちは!株式会社BTM 札幌ラボの齊藤です!
WordPressは、CMS(コンテンツ・マネジメント・システム)の一つで
2003年にブログツールとしてスタートし、
Web全体の約43%を占めるまでに成長しました(W3Techs調べ)。
WordPressの大きな特徴の1つに、
コアを触らずに機能を拡張できる構造があります。
テーマとプラグインによる拡張の仕組みです。
この構造を掘り下げてみると、
一般的なフレームワークと共通する設計パターンが多く見えてきます。
本記事では、最小構成のテーマをゼロから自作しながら、
WordPressの仕組みをフレームワークの概念と対比して読み解いていきます。
WordPressを触ったことのあるエンジニアは多いと思いますが、
「なぜコアファイルを触ってはいけないのか」
「なぜテーマファイルの名前に規則があるのか」
を改めて意識する機会は少ないかもしれません。
「なんとなく使っていたWordPress」の裏側にある
設計の意図が見えてくると面白いかもしれません。
WordPressはなぜ「拡張前提」の構造になったのか
WordPressの拡張性は、最初から備わっていたわけではありません。
初期のWordPressでは、カスタマイズにはコアコードの直接変更が必要でした。
転機は2004〜2005年にかけて訪れます。
| バージョン | 時期 | 導入されたもの |
|---|---|---|
| v1.2(Mingus) | 2004年5月 | プラグインアーキテクチャ(コアを変更せず機能追加が可能に) |
| v1.5(Strayhorn) | 2005年2月 | テーマシステム(デザインの切り替えが可能に) |
| v2.0(Duke) | 2005年12月 | functions.php(テーマごとのカスタマイズロジック集約) |
わずか2年弱で、「コアに触らず、テーマとプラグインで拡張する」
という現在のWordPressの基本構造が出来上がりました。
注目すべきは、この時期にブログプラットフォームの競合であったMovable Typeが
ライセンス条件の変更でユーザーの不満を招いていたことです。
WordPressはオープンソースかつ拡張しやすい構造を武器に、
多くのユーザーを取り込むことに成功しました。
つまり、WordPressの拡張前提の設計は
「誰でも自由にカスタマイズできること」を生存戦略とした結果といえます。
この構造を、フレームワークの概念と対比しながら見ていきましょう。
対象読者
- WordPressをなんとなく触っているが、仕組みをちゃんと理解したい方
- Laravel等のフレームワーク経験はあるが、WordPressに馴染みの薄い方
前提
- WordPressがローカル環境で動作していること
- PHPの基本文法がわかること
筆者の環境:Docker使用の仮想環境にて構築
テーマファイルの配置先はコンテナ内の /var/www/html/wp-content/themes/ 配下
本記事で作るもの
以下の7ファイルで構成される最小テーマを作ります。
wp-content/themes/my-custom-theme/
├── style.css ← テーマ定義
├── functions.php ← カスタマイズロジックの集約
├── front-page.php ← トップページ
├── single.php ← 投稿詳細ページ
├── header.php ← 共通ヘッダー
├── footer.php ← 共通フッター
└── index.php ← フォールバック
1. テーマ定義 ― style.css
WordPressにテーマを認識させるのに、設定ファイルへの登録は不要です。
必要なのは、決まった場所に決まった名前のファイルを置くことだけ。
まずはそれを体験してみましょう。
/*
Theme Name: My Custom Theme
Description: フレームワークとしてのWordPressを理解するための最小テーマ
Version: 1.0
*/
/* 以下、最低限のスタイル */
body {
font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', sans-serif;
margin: 0;
padding: 0;
background-color: #f5f5f5;
color: #333;
}
.site-header {
background-color: #0073aa;
color: #fff;
padding: 20px;
}
.site-header a {
color: #fff;
text-decoration: none;
}
.site-content {
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #fff;
}
.site-footer {
text-align: center;
padding: 20px;
color: #666;
font-size: 0.9em;
}
.post-card {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 15px;
background-color: #fff;
}
.post-card h2 a {
color: #0073aa;
text-decoration: none;
}
Theme Name のコメントヘッダーがあることで、
WordPress管理画面の「外観」→「テーマ」に表示されるようになります。
ここで1つポイントなのは、
WordPressはファイルの「名前」と「場所」と「コメントの記述」によって自動的に認識するという点です。
設定ファイルに「このテーマを使う」と明示的に登録する必要はありません。
これは、フレームワークの世界でよく言われる Convention over Configuration(設定より規約) の考え方に通じます。
WordPressのテーマ認識の仕組みも、同じパターンに沿っていると言えます。
2. テンプレート階層 = ルーティング
ファイルを1つ置くだけで、そのページのルーティングが完成する。
WordPressにはルーティング設定ファイルが存在しません。
ではどうやってURLとテンプレートを対応づけているのか?
フレームワークのルーティングとは
一般的なフレームワークでは、「このURLにアクセスしたら、この処理を実行する」
という対応をルーティング設定ファイルに記述します。
例:Laravelの場合
Route::get('/posts/{id}', [PostController::class, 'show']);
例:Djangoの場合
path('posts/<int:id>/', views.post_detail)
WordPressのルーティング = ファイル名
WordPressでは、ルーティング設定ファイルは存在しません。
代わりに、テーマディレクトリ内のファイル名そのものがルーティングテーブルになります。
これを「テンプレート階層(Template Hierarchy)」と呼びます。
| アクセス先 | WordPressが探すファイル | フレームワークで言うと |
|---|---|---|
| トップページ | front-page.php |
Route::get('/') |
| 投稿詳細 | single.php |
Route::get('/posts/{id}') |
| 固定ページ | page.php |
Route::get('/about') |
| カテゴリ一覧 | category.php |
Route::get('/categories/{slug}') |
| どれにも該当しない | index.php |
Route::fallback() |
つまり、ファイルを置くだけでルーティングが完成します。
設定ファイルに1行も書く必要がありません。
front-page.php(トップページ)
<?php get_header(); ?>
<main class="site-content">
<h1>新着記事</h1>
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article class="post-card">
<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
<time><?php the_time('Y年n月j日'); ?></time>
<?php the_excerpt(); ?>
</article>
<?php endwhile; ?>
<?php else : ?>
<p>記事がありません。</p>
<?php endif; ?>
</main>
<?php get_footer(); ?>
have_posts() と the_post() は、WordPressが自動的にデータベースから
記事を取得してループする仕組みです。
フレームワークでいえば、Controllerがデータを取得してViewに渡す処理に相当しますが、
WordPressではこれがテンプレートファイル内に一体化されています。
single.php(投稿詳細ページ)
<?php get_header(); ?>
<main class="site-content">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<article>
<h1><?php the_title(); ?></h1>
<time><?php the_time('Y年n月j日'); ?></time>
<div class="post-body">
<?php the_content(); ?>
</div>
</article>
<?php endwhile; ?>
<?php endif; ?>
</main>
<?php get_footer(); ?>
投稿詳細へのアクセスは ?p=123 や パーマリンク設定に応じたURLで行われますが、
開発者が明示的にルーティングを書く必要はありません。
WordPressが single.php の存在を検知し、自動的にこのファイルを使用します。
index.php(フォールバック)
<?php get_header(); ?>
<main class="site-content">
<h1>ページが見つかりません</h1>
<p>お探しのページは存在しないか、移動された可能性があります。</p>
</main>
<?php get_footer(); ?>
テンプレート階層のどのファイルにも該当しない場合、
最終的に index.php が使われます。
フレームワークでいう フォールバックルート(404ハンドラー) に相当します。
index.php はWordPressテーマに唯一「必須」のファイルです。
これがないとテーマとして認識されません。
3. 共通レイアウト = テンプレートの継承
front-page.php と single.php に同じヘッダー・フッターを書くのは冗長です。
WordPressではこれを共通パーツとして分離できますが、
その仕組みの中に、プラグインが動作するための重要な伏線が隠れています。
フレームワークのレイアウト機能
フレームワークでは、ヘッダーやフッターなどの共通部分を
レイアウトファイルとして切り出し、各ページで継承します。
例:Laravelの場合(Blade)
@extends('layouts.app')
@section('content')
...
@endsection
例:Djangoの場合
{% extends "base.html" %}
{% block content %}
...
{% endblock %}
WordPressの場合 = get_header() / get_footer()
WordPressでは get_header() と get_footer() で共通パーツを読み込みます。
header.php
<!DOCTYPE html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo('charset'); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
<header class="site-header">
<h1><a href="<?php echo home_url('/'); ?>"><?php bloginfo('name'); ?></a></h1>
</header>
footer.php
<footer class="site-footer">
<p>© <?php echo date('Y'); ?> <?php bloginfo('name'); ?></p>
</footer>
<?php wp_footer(); ?>
</body>
</html>
ここで重要なのは wp_head() と wp_footer() です。
この2つの関数は、WordPressコアやプラグインが
CSSやJavaScriptを出力するためのフックポイントになっています。
これが次の章につながります。
4. フック = イベント駆動
WordPressの拡張性の「核」がここです。
コアファイルを1行も変更せずに、自分の処理を差し込める。
2004年のv1.2で導入されたこの仕組みが、
WordPressをただのブログツールから拡張可能なプラットフォームに変えました。
フレームワークのイベント/ミドルウェア
フレームワークでは、リクエスト処理の途中に独自の処理を差し込む仕組みがあります。
例:Laravelのミドルウェア
public function handle($request, Closure $next)
{
// リクエスト処理の前に実行される
Log::info('リクエスト受信');
return $next($request);
}
例:Djangoのミドルウェア
class MyMiddleware:
def process_request(self, request):
# リクエスト処理の前に実行される
logger.info('リクエスト受信')
WordPressのフック = add_action / add_filter
WordPressでは、コア処理の特定のタイミングに
add_action(アクションフック)で独自の関数を登録できます。
これを functions.php に記述します。
functions.php
<?php
/**
* テーマのCSSを読み込む
*/
function mytheme_enqueue_styles()
{
wp_enqueue_style(
'mytheme-style', // ハンドル名(識別子)
get_stylesheet_uri(), // style.cssのURL
array(), // 依存関係
wp_get_theme()->get('Version') // バージョン(キャッシュ対策)
);
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_styles');
/**
* ページタイトルをWordPressに自動管理させる
*/
function mytheme_setup()
{
add_theme_support('title-tag');
add_theme_support('post-thumbnails');
}
add_action('after_setup_theme', 'mytheme_setup');
/**
* 投稿の抜粋(excerpt)の文字数を変更する
*/
function mytheme_excerpt_length($length)
{
return 40;
}
add_filter('excerpt_length', 'mytheme_excerpt_length');
ここで注目すべきは、コアファイルを1行も変更していないことです。
wp_enqueue_scripts というイベントが発火するタイミングに、
自分の関数 mytheme_enqueue_styles を「登録」しているだけです。
フレームワークのイベントリスナーやミドルウェアと同じ構造です。
| 概念 | フレームワーク | WordPress |
|---|---|---|
| 処理の前後に割り込む | ミドルウェア | アクションフック(add_action) |
| 出力を加工する | レスポンスフィルター | フィルターフック(add_filter) |
| 機能を初期化時に登録 | サービスプロバイダ |
after_setup_theme フック |
5. プラグイン = パッケージ管理
エンジニアにとってのcomposerやnpmが、
WordPressでは管理画面のGUIになっています。
やっていることは同じですが、「誰が使うか」によってインターフェースが異なります。
フレームワークのパッケージ管理
フレームワークでは、外部ライブラリをパッケージマネージャで管理します。
| フレームワーク | パッケージマネージャ | インストール先 |
|---|---|---|
| Laravel | Composer | /vendor/ |
| Django | pip | site-packages/ |
| Next.js | npm / yarn | /node_modules/ |
WordPressのプラグイン
WordPressでは、管理画面から「プラグイン」→「新規追加」で
機能を追加できます。
インストールされたプラグインは /wp-content/plugins/ に配置されます。
仕組みは同じです。違いはGUIで管理できるかどうかだけです。
WordPressのユーザーには非エンジニアも多いため、
コマンドラインではなくGUIという選択肢が用意されています。
フレームワーク:
$ composer require laravel/sanctum
WordPress:
管理画面 → プラグイン → 新規追加 → 「Contact Form 7」で検索 → インストール
なお、WordPress にも WP-CLI というコマンドラインツールがあり、
エンジニアはこちらを使うこともできます。
# WP-CLIでプラグインをインストール
$ wp plugin install contact-form-7 --activate
まとめ ― 「コアに触るな」は設計原則
ここまで見てきた内容を1枚の対応表にまとめます。
| フレームワークの概念 | WordPressでの実現方法 |
|---|---|
| ルーティング | テンプレート階層(ファイル名の命名規則) |
| レイアウト / テンプレート継承 |
get_header() / get_footer()
|
| イベント / ミドルウェア | フック(add_action / add_filter) |
| サービスプロバイダ |
functions.php + after_setup_theme
|
| パッケージ管理 | プラグイン(/wp-content/plugins/) |
| Convention over Configuration | ファイル名による自動認識 |
WordPressの「コアファイルは触らず、テーマとプラグインで拡張する」
というルールは、単なる慣習ではありません。
この構造を整理するのに便利な考え方として、
ソフトウェア設計の原則である
Open-Closed Principle(拡張に対して開き、修正に対して閉じる)
があります。SOLID原則の1つとして知られ、
フレームワークに限らずソフトウェア設計全般で広く使われている考え方です。
WordPressの開発者がこの原則を意識していたかはわかりません。
ただ、「コアを変更せず、テーマとプラグインで拡張する」という構造は、
まさにこの原則と同じ考え方で理解できます。
WordPressはCMSとして生まれましたが、
その内側には、フレームワークと共通する設計パターンが確かに存在します。
この視点を持つと、WordPressの「なぜそうなっているか」が腹落ちし、
今後の開発やトラブルシュートでも的確な判断ができるようになるはずです。
参考
- WordPress公式 テンプレート階層
- WordPress公式 フック
- WordPress公式 テーマ開発
- The History of WordPress from 2003 - 2026(WPBeginner)
- WordPress Market Share, Statistics, and More(WordPress.com)
株式会社BTMではエンジニアの採用をしております。
ご興味がある方はぜひコチラをご覧ください。