WordPressの the_post() ってそもそも何者?

先日、WordPressのユーザーコミュニティである『WordBench』にて「ループ」をテーマにした約4時間のハンズオンセミナーを行いました。4時間、ループのことしかやらない勉強会はWordBenchでも異色だと思うのですが、参加された方の感想はおおむね好評だったようです。公式ドキュメントのCodex日本語版の「The Loop」の項未翻訳というのも理由の一つかもしれません。
[2017.3.11追記 日本語訳されてました!]

さて、WordPressカスタマイズのキモともいえるこのループ。私もかつて解説本から学んだとおり、

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<!-- 記事があるときの表示内容をここに書く -->
<?php endwhile; else : ?>
<!-- 記事がないときの表示内容をここに書く -->
<?php endif; ?>

てな感じでしゃしゃっと書いています。WordPressを始める前にMovableTypeをちょっとかじったこともあり、ループについて「あーそんなもんかー」という感覚で、疑問も抱かずやっていたのですね。

で、今回の勉強会をやるにあたって、はたと思ったのです。「if とか while が条件分岐ってのはわかるけど、この the_post() ってのはそもそも何をしてるヤツなんだ?」と。

そう思ってググってみたものの the_post() に関する記事がほとんど見つからないという現実が。頼みのCodexも未翻訳状態で残念なことになっている。うむ、それならひとつ、学習をかねてやってみようじゃありませんか……ということでこのエントリーを起こすことにしました。

WordBenchを運営してるくせに実はよくわかってない点も多々ありますので、そのあたりはバシバシつっこんでいただければ幸いに存じます。では始めていきましょう。

超・ミニマムなテーマを有効化する

ループの構造をわかりやすくするため、最低限の要素しか記述していないテーマをつくりました。index.php と style.css だけのテーマ『Skeleton』です。よろしければ、以下よりダウンロード→サーバにアップロード→有効化してみてください。

Skeletonテーマ ダウンロード

ループの基本構造をもうちょっとほどいてみよう

『Skeleton』の index.php をひらいてみてください。ループのスタートを示す11行目の記述です。

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>

いきなり「あちゃー」な感じですね。個人的にはこの1行が、ループに対する苦手意識の第一歩じゃないかと思うので、もうちょっとほどいてみることにします。プログラマーさんなら「こんな書き方エレガントじゃないヤイヤイ!」とおっしゃることでしょうが、そこは母ちゃん勘弁イデデデデ。

ついでに、記事がないときの処理を省いて(実際にはちゃんと書いてね)さらにループを簡潔にしてみましょう。『Skeleton』の10~17行目を次のように書き換えてみます。

<?php if ( have_posts() ) : ?>
	<?php while ( have_posts() ) : ?>
		<?php the_post(); ?>
		<p><?php the_title(); // 記事のタイトルを表示 ?></p>
	<?php endwhile; ?>
<?php endif; ?>

このように見ると、ループは if ~ endif が while ~ endwhile を包み込む、二重の構造でできていることが分かります。

そもそも if ~ endif と while ~ endwhile がやっていることって?

ここで改めて if ~ endif, while ~ endwhile の動作についてふれておきましょう。ともにPHPで利用可能な制御構造の仕組みで、PHPのプログラム内で次のような振る舞いをします。

  • if ~ endif……if に与えられた条件が満たされる場合 if ~ endif の間の処理を行う
  • while ~ endwhile……while に与えられた条件が満たされる場合 while ~ endwhile の間の処理を繰り返し行う

この制御構造をWordPressのループに当てはめてみます。ループ内に2回出てくる have_posts() は「表示できる記事がある」ことを示す印のようなものだと考えてください。have_posts() は if, while 双方に条件として与えられているので、上記をふまえるとWordPressのループでは

  • if ~ endif……表示できる記事がある場合 if ~ endif の間の処理を行う
  • while ~ endwhile……表示できる記事がある場合 while ~ endwhile の間の処理を繰り返し行う

ということになります。

繰り返しの条件を与えないと while ~ endwhile は働いてくれない

「表示できる記事がある場合」という条件は同じながら if ~ endif と while ~ endwhile の処理には、実は大きな違いがあります。while ~ endwhile の説明をもう一度ご覧ください。「繰り返し」という表現を強調しているのがお分かりいただけるでしょうか。

その理解を深めていただくため、PHPマニュアルの while の項よりこのようなサンプルコードを引用してみます。while ~ endwhile を用いて同じ処理を10回繰り返し、1から10までを表示するコードです。

$i = 1;
while ($i <= 10):
	echo $i;
	$i++;
endwhile;

《コード解説》
1行目……while ~ endwhile に入る前に、表示すべき数字(=処理を繰り返した回数)を $i という入れ物(PHPでは「変数」といいます)に入れます。
2行目……while で「$i が10以下の場合」という条件を判断。1行目の記述により、そのまま while ~ endwhile のループに入ります。
3行目……$i の内容を表示( 1 が表示されます)。
4行目……$i の数値を 1 加算( $i の値が 2 となります)。
5行目……ループ終了。

その後処理は2行目に戻り $i の値が 2 であることから2回目のループに入っていきます。以降 $i の値が 10 になるまで処理が繰り返されるというわけです。

さて、このコードでたいへん重要な役割を果たしているのが、$i をカウントアップしている

$i++;

の1行。これがないと $i の値は 1 のままなので何度ループしても「$i が10以下の場合」の条件が変わらず

111111111111111111111111111...

と、終わりのない表示結果となってしまいます。これらのことから while ~ endwhile においては

  • ループの外側で初期値を与える
  • ループ内でその値を変える

のが使いこなしのカギとなるのがお分かりいただけるでしょうか。

the_post() がないとループはどうなる?

前フリが長くなりましたが、ようやく the_post() の話にたどりつきました。

これまでの説明をふまえて、『Skeleton』テーマから試しに the_post() を外して実行してみます。

<?php if ( have_posts() ) : ?>
	<?php while ( have_posts() ) : ?>
		<p><?php the_title(); // 記事のタイトルを表示 ?></p>
	<?php endwhile; ?>
<?php endif; ?>

なんということでしょう。最初の記事のタイトルだけがただただ表示されて、終わりのない状態になってしまったではありませんか(ブラウザの「読み込み停止」をクリックすれば中止できます)。

そうです。the_post() にはループ内で値をカウントアップする役割があるのです。Codexを見ても、解説書を読んでも、the_post() についてはサラリと触れられているに過ぎませんが、実は実はとんでもなくたいへんな役割をもっている重要なキャラだったのでありました。

the_post() の下地を作る $wp_query

さきほど while ~ endwhile においては

  • ループの外側で初期値を与える
  • ループ内でその値を変える

のが使いこなしのカギとなることを書きました。ということはWordPressでも、ループの外側で何らかの初期値が与えられ、ループ内で the_post() がその値を加減算していると推測できます。ループを成り立たせるために、どんな値が動いているのでしょう?

カギを握っているのは、$wp_query という隠れた変数です。この変数は、WordPressのプログラム側であらかじめ「予約」されているものなので、テンプレートなどで同じ名前の変数を作っちゃうとエラいことに。まぜるな危険。

で、『Skeleton』テーマを有効化した状態でサイトトップ(index.php)にアクセスしたとき、WordPressのプログラムは $wp_query にこんな情報を入れてくれます(ものすごく端折ってるので、ほかにもいっぱいいろいろ入ってる)。

  1. 投稿10件分のタイトルや本文、作成日・更新日など(投稿には0~9の連番がふられてます)
  2. そのうち、最初の投稿のタイトルや本文、作成日・更新日など
  3. $wp_query に入っている記事の件数(10)
  4. ループカウンタ(ループ外にいることから初期値 -1 が入ってます)

※1, 3の内容は、管理パネルの[設定]-[表示設定]-[1ページに表示する最大投稿数]の設定により変わります

変数にはずーっと上で示した $i のように単一の値だけでなく、上記のようなたくさんの情報を詰め込むこともできます(ここを詳しく説明するとPHPの話になるので「変数とはそういうものだ」ってくらいの認識でOKです)。

n番目の記事を表示するとき、ループカウンタはn-1になっている

これをふまえてループの流れを見ていきましょう。まずは、if, while の双方で出てくる have_posts() の役割です。have_posts() は上記1~4の情報をもとに次のような動作をします(ほかにもあるけど省略)。

  • ループカウンタ + 1 < $wp_query 内の記事件数 なら true(真)を返す
  • ループカウンタ + 1 = $wp_query 内の記事件数 かつ $wp_query 内の記事件数 > 0 なら false(偽)を返す

上記より if ( have_posts() ) は「真」となるので、こんどは while ( have_posts() ) の判断に入ります。ループ1回目では if ( have_posts() ) と同じ条件なので、繰り返し処理に入ってもOKですね。

ということでループ1回目の the_post() さんは、こんな振る舞いをします(ほかにもあるけど省略)。

  • ループカウンタを 1 加算する(0 になる)
  • 10件の投稿のうち連番 0 の投稿を表示する準備をする

the_post() さんがこのようにお膳立てしてくれたので、ループ内のテンプレートタグ the_title() で連番 0 番目の投稿タイトルが表示できることになります。

それでは2回目の while ( have_posts() ) 。1回目の処理によりループカウンタは 0 ですね。have_posts() の振る舞いより

  • ループカウンタ + 1 < $wp_query 内の記事件数

となり while ( have_posts() ) は「真」。2回目のループに入って the_post() の処理はこうなります。

  • ループカウンタを 1 加算する(1 になる)
  • 10件の投稿のうち連番 1 の投稿(つまり、2番目の投稿)を表示する準備をする

お分かりいただけるでしょうか? n番目の記事を表示する際のループカウンタが n-1 になっていることが。ということで、10回目のループが終わったところでループカウンタが 9 になります。

まだまだ繰り返すヨー、とばかりに11回目のループに入ろうとしても while ( have_posts() ) の箇所で have_posts() の振る舞いである

  • ループカウンタ + 1 = $wp_query 内の記事件数 かつ $wp_query 内の記事件数 > 0 なら false を返す

が満たされ while ~ endwhile 内の処理はできずループを抜ける、ということになるわけです。

ループの詳しい挙動は query.php にて

ここまでかいて約4600字。ところどころ冗長な表現になってしまったかもしれませんが the_post() の役割とその大切さを少しでも感じていただけたのでしたら、幸いです。

この記事では have_posts() や the_post() の処理をある程度簡略化して説明しました。PHPのコードを追いながらもう少し詳しく知ってみたい方は wp-includes/query.php をぜひご参照ください。他のメソッド(機能)と連携しながら、さまざまな処理をしていることがお分かりいただけるかと思います。

「WordPressの the_post() ってそもそも何者?」への3件のフィードバック

  1. 非常に為になりました! 小骨のようにひっかかっていたので^^;

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です