子テーマ制作のパフォーマンスを上げる!functions.phpをクラス化する方法

WordPressの便利な機能の1つ、子テーマ。ベースとなるテーマを制作して親テーマとし、その親テーマのテンプレートを継承して子テーマを制作できる機能です。子テーマ機能を使用することにより、例えば、下記のようなメリットが。

  • 制作会社の方で複数サイトをWordPressで構築している場合、それぞれのテーマを共通の親テーマの子テーマとして制作しておけば、テーマのバージョンアップが簡単に。
  • 個人でWordPressでサイトをお持ちの方で、複数のテーマをよく切り替えたりする場合、共通の親テーマをもとに各テーマを制作しておけば、機能を追加したい時に、いちいち各テーマにコードをつかしなくても良い

ただ、そんな便利な子テーマなのですが、一つだけ気になることが…。

子テーマ機能の使いづらい点

上記でテーマの機能追加に着目して子テーマのメリットを上げましたが、だいたいそのような場合ってfunctions.phpにコードを追加しますよね。もちろん、親テーマのfunctions.phpに書いたコードも子テーマに反映されます。しかし、親テーマのfunctions.phpにある関数の一部を子テーマで変更したい場合、とたんに難しくなります。例えば、次のようなエントリーを出力するコードがあったとします。

// 親テーマ functions.php
function the_entry {
	echo '<h2>'.get_the_title().'</h2>';
	the_content();
}

// 子テーマ functions.php
// 子テーマでは見出し部分を日付にしたい…
function the_entry {
	echo '<h2>'.get_the_date( 'Y.m.d' ).'</h2>';
	the_content();
}
// 重複エラー!

親テーマを複数のテーマのベースとする場合、上記のように親テーマと子テーマで、同じ関数名だけど中身が違う処理を書きたい場合が多々あると思います。しかし、そのようなコードを書いてしまうと、PHPの「関数の重複エラー」となってしまうのです!せっかくテンプレートも継承上書きできて、functions.phpも同様に継承できるのに、上書きができない!不便すぎる!

もっと便利にfunctions.phpを使う

同名の関数を上書きする…実はPHPにはそれを行う便利な特徴があるのです。それがクラスです。

functions.phpは、子テーマ %gt; 親テーマの順に読み込まれます。ベースクラスは親テーマに定義する必要がありますが、そのままだと子テーマが先に読み込まれるので、ベースクラスはその時点では読み込まれておらずエラーとなってしまいます。読み込みの順番は操作できないので、「after_setup_themeフック」を用い、「クラスの実行順番」を親テーマ > 子テーマとすることがポイントとなります。

親テーマ

<?php
// 子テーマとの衝突を防ぐため、一番最後に実行
add_action( 'after_setup_theme', 'parent_theme_setup', 99999 );
function parent_theme_setup() {
	if ( !class_exists( 'fc' ) ) {
		// このテーマで実行したい処理をまとめたfcクラス
		class fc extends _baseFunctions {
			/**
			 * __construct
			 */
			public function __construct() {
				parent::__construct();
			}
		}
	}
	// 実行
	$fc = new fc();
}

/**
 * 全てのテーマで共通に実行したいベースクラス
 */
class _baseFunctions {

	// フィード配信する投稿タイプを指定
	public $feed_posts = array( 'post' );

	/**
	 * __construct
	 * 初期化処理 ここでは適当にサンプルを入れておきます
	 */
	public function __construct() {
		// generatorの表示を削除
		remove_action( 'wp_head', 'wp_generator' );
		// アイキャッチ画像
		add_theme_support( 'post-thumbnails' );
		set_post_thumbnail_size( 150, 9999, false );
		// カスタム投稿のフィード出力
		add_filter( 'pre_get_posts', array( $this, 'custom_post_rss_set' ) );
	}

	/**
	 * カスタム投稿のフィード出力
	 */
	public function custom_post_rss_set( $query ) {
		if ( is_feed() ) {
			$post_type = $query->get( 'post_type' );
			if ( empty( $post_type ) ) {
				$query->set( 'post_type', $this->feed_posts );
			}
			return $query;
		}
	}

	/**
	 * エントリーを出力
	 * テンプレート内で汎用的に使いたいメソッドはstatic指定すると便利
	 * fc::メソッド名() で使用できる。
	 */
	public static function the_entry() {
		echo '<h2>'.get_the_title().'</h2>';
		the_content();
	}
}

子テーマ

<?php
// 親テーマの後に実行
add_action( 'after_setup_theme', 'child_theme_setup' );
function child_theme_setup() {
	if ( !class_exists( 'fc' ) ) {
		// このテーマで実行したい処理をまとめたfcクラス
		class fc extends _baseFunctions {

			// フィード配信する投稿タイプを上書き
			public $feed_posts = array( 'post', 'page' );
	
			/**
			 * __construct
			 */
			public function __construct() {
				parent::__construct();
			}

			/**
			 * エントリーを出力(タイトルを日付に)
			 * 上書きしたいメソッドがある場合だけ子テーマの
			 * _baseFunctionsクラスの中に同名のメソッドを定義。
			 * 親テーマにあるメソッドは自動的に子テーマにも反映されるので
			 * custom_post_rss_setメソッドは定義しなくても良い。
			 */
			public static function the_entry() {
				echo '<h2>'.get_the_date( 'Y.m.d' ).'</h2>';
				the_content();
			}
		}
	}
}

全体的に少しコード量が増えてしまいますが、一度書いてしまえば圧倒的に便利にfunctions.phpを使えるようになるので、ぜひ一度お試しくださいね。

  • ブックマーク
  • Feedly

この記事を書いた人

キタジマタカシ

長崎在住、フリーランスのWordPress テーマ / プラグインデベロッパー。 多数のプロダクトをオープンソースで開発・公開しています。現在は WordPress 有料テーマ Snow Monkey を開発・販売しています。