WordPressテーマのfunctions.phpをクラス化する

プラグインを作成するとき、まるっとクラス化することで 関数名のコンフリクトの心配やら、ちまちまプレフィックスをつける面倒から解放されます。じゃぁテーマの functions.php でも同じことができるんではないか 。子テーマとか考えると function_exists() での判定やら、親テーマと同じプレフィックス付きの長い関数名やら、なんちゅう読み難いんじゃー、といった諸々から解放されてもっと幸せになれるんではないか。そんな思いからテーマ(のfunctions.php)をクラス化する方法を考えてみました。何十個も twentyeleven_ とか書くのシンドイ。

#prefix ってプリフィクスじゃないのね。。。

親テーマ

Web 制作会社なら(そうでなくても)自社ルールに則って構築されたテーマのひな形が存在すると思います。無い場合は、この際だから作ってしまいましょう!いつまでもまんま TwentyTen でいいハズないじゃない。そういうテーマを自社の親テーマにして、各々のクライアントにフィットさせた子テーマを納品するようにすると制作効率が向上したりテーマ管理がし易くなったり彼女ができたりと色々ステキです。
そんなわけで、以下 テーマ(の functions.php)をクラス化して子テーマも使う場合の最少コード例です。

親テーマの style.css

正規の親テーマの書き方と同じです。

/*
Theme Name: Wpxtreme Parent
Theme URI: http://wpxtreme.jp/
Author: kz
Author URI: http://kzxtreme.com/
Description: 当社の親テーマであります
Version: 1.0
*/
/* スタイルの記述は省略 */
親テーマの functions.php

ポイントは、after_setup_theme アクションの優先度です。functions.php は 子テーマ ---> 親テーマ の順に読み込まれますが、アクションの優先度により 親テーマのクラス定義 ---> 子テーマのクラス定義 の順に実行されるように指定します。で、最後に現在のテーマのクラスのオブジェクトを作って完了です。ののの気になる。
PHP 的なポイントは new static() 辺り。この、親クラスに書いてるけど「子クラスの」という扱いができる late bind は PHP 5.3.0〜使えます。

<?php
// テーマオブジェクトをグローバル変数にします。
// the_theme なんて危険な名前は避けるといいかもね!
add_action( 'after_setup_theme', 'instantiate_theme', 99999 );
function instantiate_theme() {
  $GLOBALS['the_theme'] = call_user_func( array( str_replace( ' ', '_', get_current_theme() ), 'get_object' ) );
}

// 親テーマは最優先でアクション実行!
add_action( 'after_setup_theme', 'setup_parent_theme', 0 );
function setup_parent_theme() {
  if ( ! class_exists( 'Wpxtreme_Parent' ) ) { // テーマ名のスペースを '_' で置換してクラス名にしましょう!

    class Wpxtreme_Parent {
      static private   $classobj = NULL;

      public function __construct() {
        // construct self
        // add_theme_support(), add_filter(), add_action() などなど
      }

      public static function get_object() {
        if ( NULL === self :: $classobj ) {
          self :: $classobj = new static();
        }
        return self :: $classobj;
      }
    }
  }
}
子テーマの style.css

これも正規の子テーマの書き方と同じです。

/*
Theme Name: Wpxtreme Child
Theme URI: http://wpxtreme.jp/
Author: kz
Author URI: http://kzxtreme.com/
Description: クライアントA様向けテーマです!
Version: 1.0
Template: wpxtremeparent
*/
@import url("../wpxtremeparent/style.css");
/* スタイルの記述は省略 */
子テーマの functions.php
<?php
// 親テーマより後にアクション実行!
add_action( 'after_setup_theme', 'setup_child_theme',1 );
function setup_child_theme() {
  if ( ! class_exists( 'Wpxtreme_Child' ) ) {
	
    class Wpxtreme_Child extends Wpxtreme_Parent {
      public function __construct() {
	// construct parent
	parent :: __construct();

        // construct self
        // add_theme_support(), add_filter(), add_action() などなど
      }
    }
  }
}

これだけで OK です。楽チン!ってこれだけじゃ何だかよくわかりませんネ。

もうちょっと具体的に

サンプル的にそれらしい処理を書いてみますね。※コードはクラスの定義部分だけです。

親テーマの functions.php
class Wpxtreme_Parent {
  static private   $classobj = NULL;
  static protected $domain;  // text domain
  static protected $userdata;  // 'name' => array( 'label' => 'label to display', 'type' => 'text'|'textarea'|..., 'description' => 'description.' )
  static public $path;  // stylesheet directory uri
  /**
   * Constructor, init on defined hooks of WP and include second class
   *
   * @access public
   * @since 0.0.1
   * @uses add_filter, add_action
   * @return void
   */
  public function __construct() {
    // member variables
    self :: $domain = strtolower( str_replace( '_', '', get_class( $this ) ) );
    self :: $path = get_stylesheet_directory_uri();
	
    load_theme_textdomain( self :: $domain, TEMPLATEPATH . '/languages' );
    
    // supports
    add_editor_style();
    add_theme_support( 'post-thumbnails' );

    // user profile
    add_filter( 'user_contactmethods', array( $this, 'user_contactmethods' ) );
    if ( isset( self :: $userdata ) ) {
      add_action( 'personal_options_update', array( $this, 'update_profile' ) );
      add_action( 'edit_user_profile_update', array( $this, 'update_profile' ) );
      add_action( 'show_user_profile', array( $this, 'user_profile' ) );
      add_action( 'edit_user_profile', array( $this, 'user_profile' ) );
    }
  }
  /**
   * Handler for the action 'init'. Instantiates this class.
   *
   * @access public
   * @since 0.0.1
   * @return object $classobj
   */
  public static function get_object() {
    if ( NULL === self :: $classobj ) {
      self :: $classobj = new static();
    }
    return self :: $classobj;
  }
  /**
   * Delete obsolete contact methods.
   *
   * @access public
   * @since 0.0.1
   * @return array $contactmethods
   */
  public function user_contactmethods( $contactmethods ) {
    unset( $contactmethods['aim'] );
    unset( $contactmethods['jabber'] );
    unset( $contactmethods['yim'] ); 
    return $contactmethods;
  }
  /**
   * Update user meta.
   *
   * @access public
   * @since 0.0.1
   * @return void
   */
  public function update_profile( $user_id ) {
    if ( !current_user_can( 'edit_user', $user_id ) )
      return false;

    foreach ( array_keys( (array) self :: $userdata ) as $field )
      update_user_meta( $user_id, $field, $_POST[$field] );
  }
  /**
   * Print form for user profile.
   *
   * @access public
   * @since 0.0.1
   * @return void
   */
  public function user_profile( $user ) {
    if ( !current_user_can( 'edit_user', $user->ID ) )
      return false;

?><table class="form-table"><?php			
    foreach ( (array) self :: $userdata as $name => $data )
      printf( '<tr><th><label for="%1$s">%2$s</label></th>'
        . '<td><input type="%3$s" name="%1$s" id="%1$s" value="%4$s" class="regular-text" />'
        . '<br /><span class="description">%5$s</span></td></tr>',
      esc_attr( $name ),
      esc_html( isset( $data['label'] ) ? $data['label'] : $name  ),
      esc_attr( isset( $data['type' ] ) ? $data['type' ] : 'text' ),
      esc_attr( get_user_meta( $user->ID, $name, true ) ),
      esc_html( isset( $data['description' ] ) ? $data['description' ] : '' )
    );
?></table><?php
  }
  /**
   * Prints HTML with meta information for the current post-date/time.
   *
   * @access public
   * @since 0.0.1
   * @return void
   */
  public function posted_on() {
    printf( __( '<time class="entry-date" datetime="%1$s" pubdate>%2$s</time>', self :: $domain ),
      esc_attr( get_the_time() ),
      esc_html( get_the_date() )
    );
  }
}
子テーマの functions.php
class Wpxtreme_Child extends Wpxtreme_Parent {
  /**
   * Constructor, init on defined hooks of WP and include second class
   *
   * @access public
   * @since 0.0.1
   * @uses add_filter, add_action
   * @return void
   */
  public function __construct() {
    // set protected variables
    self :: $userdata = array(
      'yakusyoku' => array( 'label' => '役職', 'type' => 'text', 'description' => '社長、平社員、などを書いて!' ),
    );

    // construct parent
    parent :: __construct();

    // construct $this
  }
  /**
   * Add contact methods.
   *
   * @access public
   * @since 0.0.1
   * @return array $contactmethods
   */
  public function user_contactmethods( $contactmethods ) {
    $contactmethods = parent :: user_contactmethods( $contactmethods ); 
    $contactmethods['skype'] = 'Skype 名';
    $contactmethods['twitter'] = 'Twitter ユーザー名';
    $contactmethods['facebook'] = 'Fecebook ユーザー名';
    return $contactmethods;
  }
  /**
   * Prints HTML with meta information for the current post-date/time and author.
   *
   * @access public
   * @since 0.0.1
   * @return void
   */
  public function posted_on() {
    $user = get_userdata( get_the_author_meta( 'ID' ) );
    printf(  __( '<time class="entry-date" datetime="%1$s" pubdate>%2$s  %3$s</time>%4$s', self :: $domain ),
      esc_attr( get_the_time() ),
      esc_html( get_the_time( get_option( 'date_format' ) ) ),
      esc_html( get_the_time( get_option( 'time_format' ) ) ),
      get_avatar( $user->user_email, 40, self :: $path . '/images/default.gif' )
    );
  }
}

  • $domain
    テキストドメインはクラス名を英数小文字にし、'_' を無くしたものです。
    親テーマ:wpxtremeparent
    子テーマ:wpxtremechild
  • $userdata
    子テーマで label, type, description を設定すると、親テーマでプロフィールページに入力欄を追加します。
  • user_contactmethods()
    プロフィールページの「連絡先」欄
    親テーマ:不要な項目を削除
    子テーマ:新規に項目を追加
  • posted_on()
    子テーマで処理をまるっとオーバーライドしてます。
    テンプレートファイル内で以下のように使用します。
global $the_theme;
$the_theme->posted_on();

ザッと思いつきですが、何かイケてる気がしませんか!?しませんかそうですか。全部 static function でええやんとか色々検討事項はございましょうが、たまにはこんなことを考えてみるのも息抜きになって良いかもですね!

動作確認バージョン
  • WordPress 3.1.3
  • PHP 5.3.5

20 Comments

  • 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • あとでじっくり読む。てか、また変態なPOSTやなーw RT @kzxtreme: 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • やっぱ変態やなー。 RT @kzxtreme: 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • ちょうど昨日functions.phpをクラスで書くのにチャレンジしてたんだけど、なんと子テーマで継承できるのか。そりゃそのほうがいいじゃん – WordPressテーマのfunctions.phpをクラス化する http://t.co/NSYbOhY

  • 変態~言うてたけど、子テーマで親テーマの関数継承はおでこもいつもやってます! | WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://bit.ly/nVMMqB

  • http://wpxtreme.jp/use-class-in-wordpress-theme WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme

  • WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://bit.ly/rhdgZA #wordpress

  • WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme #prefix ってプリフィクスじゃないのね。。。 親テーマ Web 制作会社なら(そうでなくても)自社ルールに則って構築されたテーマの…http://bit.ly/pkpBEv

  • Now Browsing: WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme – http://bit.ly/qr0fNX

  • 【ブログ】WordPressテーマのfunctions.phpをクラス化する http://bit.ly/rhs6Gd #wordpressjp @wordpress_fan

  • こういうの勉強になるなー。PHP5.3の機能とか使われてる。 – WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://bit.ly/pOwZCp

  • こういうの勉強になるなー。PHP5.3の機能とか使われてる。 – WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://bit.ly/pOwZCp

  • WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://htn.to/MbvSnU

  • WordPressテーマのfunctions.phpをクラス化する http://wpxtreme.jp/use-class-in-wordpress-theme

  • 整頓とコンフリクト回避> WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://t.co/OgaY5wD

  • 整頓とコンフリクト回避

  • 整頓とコンフリクト回避> WordPressテーマのfunctions.phpをクラス化する  |  wpxtreme http://t.co/OgaY5wD

  • WordPressテーマのfunctions.phpをクラス化する http://t.co/V4HKj9u #WordBeach