worpdressでよく見る$postとは何なのか

wordpressで調べ物をしていると、よく$postという変数を目にすると思います。何となくコピペで使っている方も多いんじゃないかと思いますが、もうちょっと理解したいよ、という方のために記事を書こうと思います。$postの中身は何なのか、どこからやってくるのか、そして例えばループの中で使用する$post->post_contentthe_content()との違いは、などについてのお話です。

$postとは何か

$postはwordpressのコアで定義されるグローバル変数の一種です。グローバル変数というのは、グローバルスコープの変数という意味で、簡単に言うとどこからでもアクセスできる変数です。ただし、関数の中で使用された変数はデフォルトでローカルスコープの変数と扱われるので、関数の中でグローバル変数を利用するには以下のようにします。

<?php
$ foo = 'this is global variable'

function bar() {
  global $foo;  // グローバル変数の$fooを使うと宣言する
  echo $foo;
}

公式ドキュメントを見ると、

$post (オブジェクト) – 現在の投稿オブジェクト。

と書かれています。投稿オブジェクトとは何なのでしょうか?

投稿オブジェクトとは

wordpressの投稿オブジェクト($postの中身)は、実は使用する文脈によって個別投稿だったり、固定ページだったり、添付ファイルだったりします。これを理解するためには、投稿タイプとwordpressのデータベースについて少しだけ知っておく必要があります。

wordpressにはデフォルトで5つの投稿タイプが用意されています。

  • post(個別記事)
  • page(固定ページ)
  • revision(リビジョン)
  • attachment(添付ファイル)
  • nav_menu_item(ナビゲーションメニュー)

個別記事(一般的に投稿と言われるとこれを指す)も固定ページも添付ファイルも、各投稿タイプの通称と言えます。

ではそれぞれの投稿タイプのデータはどこに保存されているのでしょうか?

http://wpdocs.osdn.jp/wiki/images/WP3.9.4-ERD.png

これはwordpressのER図ですが、その中央に「wp_posts」というテーブルがあり、その中に「post_type」というカラムがあります。これが投稿タイプを表す部分です。

wordpressは各投稿タイプのデータを別々のテーブルに格納するわけでななく、全部wp_postsにぶち込みpostオブジェクトとして扱います。そして、wp_posts.post_typeに'post', 'page', 'revision', 'attachment', 'nav_menu_item'などの投稿タイプ名を保存しておき、どの投稿タイプのオブジェクトなのかを判別する仕組みになってます。

つまり、個別記事も固定ページも添付ファイルもwp_postsから取り出されたpostオブジェクトです。なので、$postの中身は個別記事(投稿タイプ: 'post')の場合もあれば、固定ページ(投稿タイプ: 'page')の場合もあり、文脈によって異なるのです。ちなにpostオブジェクトの投稿タイプを判別するにはis_single($post), is_page($post), is_attachment($post)などの条件判定タグが利用できます。

$postはどこでセットされるのか

$postグローバル変数なのでどこでも使えるかというと実はそうではありません。というか、アクセスはできるもののnullの場合があります。

そもそもグローバル変数$postがどこで最初に定義されるかというと、wordpressのヘッダーで呼ばれるWPクラスのregister_globals()というメソッド内で定義されます。

<?php
// wp-includes/class-wp.php
class WP {
 // 省略
 public function register_globals() {
  global $wp_query;
  // Extract updated query vars back into global namespace.
  foreach ( (array) $wp_query->query_vars as $key => $value ) {
   $GLOBALS[ $key ] = $value;
  }
  $GLOBALS['query_string'] = $this->query_string;
  $GLOBALS['posts']        = & $wp_query->posts;
  $GLOBALS['post']         = isset( $wp_query->post ) ? $wp_query->post : null;
  $GLOBALS['request']      = $wp_query->request;
  if ( $wp_query->is_single() || $wp_query->is_page() ) {
   $GLOBALS['more']   = 1;
         $GLOBALS['single'] = 1;
  }
  if ( $wp_query->is_author() && isset( $wp_query->post ) ) {
   $GLOBALS['authordata'] = get_userdata( $wp_query->post->post_author );
  }
 }
 // 省略
}

isset( $wp_query->post ) ? $wp_query->post : nullとあることから、この段階では$post === nullの場合があると分かります。

$wp_queryというのはWP_Queryクラスのインスタンスで、リクエストされたページからクエリを生成して必要なpostオブジェクトを取得してくるやつです。

例えばアーカイブページなどの記事一覧ページをリクエストした場合、$wp_queryは複数の投稿データを取得するはずですよね。$wp_query->postは一つの投稿データを表すプロパティなので、投稿データが複数ヒットしたのでこの時点では$postはnullの状態になります。

ではそのような場合、いつ$postに投稿データが入れられるのかというと「ループの中」です。テンプレートで呪文のように書いてるアレです。

<?php
if ( have_posts() ) {
    while ( have_posts() ) {
        the_post(); 
        //
        // 投稿がここに表示される
        //
    } // end while
} // end if

このthe_post()$wp_query->the_post()$wp_queryも実はグローバル変数なのです。)のラッパー関数で、その実態を見てみると、

<?php
// wp-includes/class-wp-query.php
class WP_Query {
  // 省略
    public function the_post() {
        global $post;
        $this->in_the_loop = true;
        if ( $this->current_post == -1 ) { // loop has just started
            /**
            * Fires once the loop is started.
            *
            * @since 2.0.0
            *
            * @param WP_Query $this The WP_Query instance (passed by reference).
            */
            do_action_ref_array( 'loop_start', array( &$this ) );
        }
        $post = $this->next_post();
        $this->setup_postdata( $post );
    }
  // 省略
}

ここでグローバル変数の$postを宣言して、$post = $this->next_post()で次の投稿オブジェクトがセットされていることが分かります。ということで、ループの中では、グローバル変数$postには現在の投稿データが入っているということが理解できました。

グローバル変数とテンプレートタグのどちらを使うべきか

よくwordpressの解説記事とかで、$post->post_contentのように$postオブジェクトのプロパティに直接アクセスしているものを見かけます。一方でwordpressには投稿本文を取得するthe_content()というテンプレートタグも用意されています。どちらを使うべきでしょうか?

結論を言うと、なるべくthe_content()を使うべきです。the_content()グローバル変数$postからデータを取り出しているのですが、若干違いがあります。the_content()はwp-includes/post-template.phpで定義されているのでそれを見てみましょう。

<?php
// wp-includes/post-template.php
//////  中略 ///////
function the_content( $more_link_text = null, $strip_teaser = false ) {
        $content = get_the_content( $more_link_text, $strip_teaser );

        /**
         * Filters the post content.
         *
         * @since 0.71
         *
         * @param string $content Content of the current post.
         */
        $content = apply_filters( 'the_content', $content );
        $content = str_replace( ']]>', ']]&gt;', $content );
        echo $content;
}

$content = apply_filters( 'the_content', $content )とあるように、取得した本文が'the_content'というフィルターフックを通過していることがわかります。

wordpressではこうしたフックが至る所で仕込まれていて、誰でもフックを利用して出力内容をカスタマイズすることができます。そして、現在使用中のプラグインでもこうしたフックを利用して場合があります。もしthe_content()を使わず、$post->post_contentで本文を出力すると、プラグインで予定されていたフックを通過せず、予期した結果が得られない、などの可能性が考えられます。

こうしたことはthe_content()に限らず、色々なテンプレートタグであり得ます。なので、欲しい値を出力するテンプレートタグが用意されている場合には、他に理由がない限りテンプレートタグを使用した方がいいです。

テンプレートタグの解説については【初級・中級】wordpressのテンプレートタグをTwenty Nineteenを見ながら解説するをご覧ください。

よきwordpressライフを!