どうも、SSECの何でも屋さん、ささぴよです。

最近はテーマから自作するというシーンは減っているのかも知れませんが、なんだかんだと自分で作らないといけない案件というのはある気がします。

先日、テーマから自作する際に躓いたところがあったのでそのあたりを書いて行きたいとおもいます。初心者からでもできるPHPのクラスを使った書き方のお話です。

クラスで書くメリットとは

どれだけ情報を探しても、functions.phpに書いている内容をPHPのクラスで書く方法というのは情報が少なく、ここで初心者から脱することができないパターンもあるのかなと言う気がします。あっても基本パターンのみで、うまく行かないときの情報が少ないというか、先人達の知恵がネット上に無い!という感じがしています。

でも、私はクラスで書くことを推したい民です。自分でもずっと「関数で成立するならそれでいいじゃん」とやってきたのですが、クラスで書くことのメリットはやっぱり大きいと感じるようになってきました。

私の考えるメリットは…

  • オブジェクト指向に近づくので、整理されたコードになりやすい
  • ファイルごとの行数を少なくしやすい(ので、見渡しが良い)
  • プラグインへの切り出しがしやすくなる
  • なんかかっこいい(かっこいい)

というあたりです。デバッグするにしても、書いて行くにしても、1ファイル当たりの行数が少ないと見渡しが良いですよね。functions.phpに関数として並べていくと、どうしてもfunctions.phpが長くなる。どこに書いたっけなー?ってなりやすいので、ファイル分割をしようという話になります。

関数を別ファイルにして、 require するという手もありますが、そこまで行くならクラスにしちゃいませんか?というお話です。

コーダーの皆さんのイメージだったらSass(SCSS)のパーシャルの考え方に近いと思います。というかほぼそのままなんじゃないかなと。

そもそもPHPのクラスとは

クラスを使うのが初めての方向けに簡単な解説です。知ってるよーという方は飛ばして頂いて良いと思います。

ザックリとお話しすると、関数の中には変数や配列を入れて一連の処理をまとめることができる様になっていますが、それを更に複数まとめられるのがクラスです。
例えば、「掃除」というクラスの中に「台所掃除」「トイレ掃除」「風呂掃除」等と纏まっているようなイメージです。

クラスは「設計図」と例えられることもありますが、原本をそのまま使う事は出来ず、コピーして使います。このときできたコピーのことを「インスタンス」と言います。そして、コピーを作る作業・処理を「インスタンス化」と言いますね。必要に応じて、最後に必要になる情報を与えながらコピーを作る事が出来ます。コレは関数に引数を渡すのと同じ考え方です。

WordPressで登場するクラス

WordPressの標準で使われるクラスで、一番よく見るクラスであろうものが WP_Query です。これでサブループの実例を見ながら、クラスのことを整理していきましょう。今回はクラスについて理解したいだけなので、具体的な動きの話とか余計なコードは端折ります。

$args = array( <ここに呼び出したい条件を連想配列で用意> );

$the_query = new WP_Query( $args );

if ($the_query->have_posts()) :
  <記事があったとき、記事の処理を始める前にやりたい事を書く場所>
  while ($the_query->have_posts()) :
    $the_query->the_post();
    <1つの記事ごとにやりたい事を書く場所>
  endwhile;
  <記事があったとき、記事の処理を終えた後にやりたい事を書く場所>
else :
  <記事が見つからなかったときにやりたい事を書く場所>
endif;

テンプレ通りに書くとこんな感じになるのがサブループですね。対象となる記事の条件を指定して記事情報を取ってきて出力する処理で使われます。

ポイントは new WP_Query( $args ); の部分です。 WP_Query というのはクラスなわけですが、クラスをインスタンス化する為に new というのを付けています。コレがクラスらしいところ。それを $the_query におさめることで、後で呼び出せるようにしています。プログラムは保存しないとどんどん忘れていきますからね。

このインスタンス化の時に各種条件をまとめた $args を引数として渡しています。コレによって、設計図が完成し、一連の処理が動く状態になります。つまり $the_query が「インスタンス」になります。

後は、 $the_query->have_posts() です。 have_posts() というのは、ザックリ言うと「次に処理するべき記事があるかどうかをチェックする」という関数なんですが、普通に have_posts() だけを書いてしまうと、あくまでもメインループの have_posts() を実行してしまいます。折角作ったインスタンスですので、その中に有る have_posts() を実行したい。ということで、「このインスタンスの中の」を示す $the_query->がくっついています。スペースを空けずにくっついていますね。コレによって、作ったインスタンスの中にある関数を呼び出すことができるようになっています。同様に the_post() についても $the_query の中にある関数を引っ張り出して実行していることになります。

とりあえずはこれくらいがわかっていれば大丈夫です。

WordPressにおけるシンプルなPHPクラスの使い方

WordPressで機能を追加するようなときは、クラスを作りインスタンス化すれば終わりです。そう、 new すれば終わりです。そこまで難しくない。なのでコードとしては以下のような形になります。

class ssecExampleClass {
  <何かしらの処理がここに入る>
}

new ssecExampleClass();

めちゃくちゃ簡単じゃないですか?
ちなみに言語としての縛りはないですが、クラス名はキャメルケースになっている事が多い印象です。なお、PHPとしては、クラス名について大文字小文字の区別をしないそうです。(でも合わせるのが基本でしょう。)

他のプラグインやテーマのクラス名と被ってしまうといけないので、何かしら接頭辞を付けてあげると良いでしょう。ここでは ssec と言う文字を接頭辞としているイメージです。

コレをファイルに書いて、functions.phpで require すれば良いだけです。簡単ですね。

…とは言いつつ、この中に関数を書けば終わりーと言うわけでも無いので続きを進めましょう。

クラスの中に書くこと

コンストラクタ

クラスはインスタンス化されるときに勝手に実行される「コンストラクタ」というものを指定出来ます。

インスタンス化されるときに無条件に実行対象になるので、初期化する作業などを書いておくのが一般的ですが、WordPressのカスタマイズなどではアクションフック・アクションフィルタの関数を書くことが多いです。

コンストラクタの書き方は決まっていて function __construct() {} の形で記述します。ここでは一番ありそうな「テーマのCSSを読み込む」という作業を対象に書いてみることにしましょう。

class ssecExampleClass {
  function __construct() {
    add_action( 'wp_enqueue_scripts', <ここに実行させる関数を書くことになる> ));
  }
  <処理の続きがここに入る>
}

new ssecExampleClass();

実行したい関数をクラス内に作る

さて、アクションフック・アクションフィルターは実行する関数を書いてあげる必要があります。

コレはクラスの中に通常通り作ってあげればOKです。 publicだのstaticだのprivateだのといろいろありますが、いったん置いておきます。シンプルな状態を作りましょう。書いてみるとこんな感じ。

class ssecExampleClass {
  function __construct() {
    add_action( 'wp_enqueue_scripts', <ここに実行させる関数を書くことになる> ));
  }

  function ssec_enqueue_styles_func() {
    wp_enqueue_style( 'ssec', get_stylesheet_directory_uri() . '/style.css', array());
  }
}

new ssecExampleClass();

wp_enqueue_styleについての詳細は割愛しますが、特に変わった書き方はしないで大丈夫です。

ちなみに「関数じゃなくてメソッドだ!」という方がいらっしゃるかも知れませんが、PHPの公式ページで「関数 (“メソッド” といいます) 」と書かれているので、どちらでも良いと思います。

さて、ここまで来たのですが、add_actionの中で呼び出すときの書き方は今まで通りの書き方ではいけません。クラスで書くときには一手間必要です。

クラス内での対象の関数の書き方

クラス内の関数を呼び出すとき、通常ならば $this->関数名と言うような書き方をするのですが、 add_actionに渡すときには配列で渡します。0番目に $this 、1番目に実行したい関数名を入れる形です。ですので、以下のようになりますね。

class ssecExampleClass {
  function __construct() {
    add_action( 'wp_enqueue_scripts', array( $this, 'ssec_enqueue_styles_func' )));
  }

  function ssec_enqueue_styles_func() {
    wp_enqueue_style( 'ssec', get_stylesheet_directory_uri() . '/style.css', array());
  }
}

new ssecExampleClass();

これで完成です。複数のスタイルを読み込みたいなら、関数の中に wp_enqueue_stylewp_enqueue_script を追加していきましょう。

動かないときや重大なエラーが発生するときは、大抵 array( $this, <関数名> )にし忘れているか、インスタンス化を忘れていることが殆どだと思います。

でも、うまく行かなかったテーマカスタマイザ

テーマカスタマイザは比較的最近登場した機能の1つで、WordPressのテーマ特有の設定をダッシュボードでインタラクティブに確認しながら設定出来るモノです。(ホントはコレを書きたかっただけなんですが、前振りがすっかり長くなりましたね。)

ここにパネルを追加したりして設定項目を増やすことができるのですが、それがこの書き方だけではうまく行きませんでした。通常なら引数で受け取れるものが受け取れていない様子…という所まではわかりましたが、どうしたモンかと頭を抱えていました。

ここまで読んでいる方はある程度クラスやテーマの作りに理解のある方でしょうから、細かいことを抜きにして結論だけ触れていくと、 $wp_customizeglobalする必要がありました。中身は割愛しますが、具体的なコードはこんな感じです。

<?php
class ssecCustomizer
{
    function __construct()
    {
        add_action('customize_register', array($this, 'ssec_theme_message_customizer_func'));
    }

    static function ssec_theme_message_customizer_func()
    {
        global $wp_customize;
        $wp_customize->add_section(ssec_theme_message', array(
            'title'     => 'トップページメッセージ',
            'description' => 'トップページのカバー画像上に差し込むメッセージを設定できます。',
            'priority'  => 60,
        ));

        $wp_customize->add_setting('ssec_theme_message[display]', array(
            'default'   => false,
            'type'      => 'option',
        ));
        $wp_customize->add_control('ssec_theme_message_display', array(
            'settings'  => 'ssec_theme_message[display]',
            'label'     => 'メッセージの表示切り替え(チェックをオンで表示状態)',
            'section'   => 'ssec_theme_message',
            'type'      => 'checkbox',
        ));

        $wp_customize->add_setting('ssec_theme_message[text]', array(
            'default'   => '',
            'type'      => 'option',
            'transport' => 'postMessage',
        ));
        $wp_customize->add_control('ssec_theme_message_text', array(
            'settings'  => 'ssec_theme_message[text]',
            'label'     => 'トップに表示するメッセージ',
            'section'   => 'ssec_theme_message',
            'type'      => 'textarea',
        ));
    }
}

new ssecCustomizer();

こんなこと、どこにも載ってなくて、全然関係ない文献から見つけることになりました。

ものによっては引数で受け取るものをこういう形で書かないと行けないパターンがあるという事もあるという事ですね。
またこんなパターンを見つけたら載せていこうと思いますので、チェックして頂けたらと思います。

それでは、また。