もう「global $post」を使うのはやめようか?というお話|WPのコラム

WordPress(ワードプレス)に携わる方にはお馴染みのグローバル変数「$post」。
簡単に説明すると「そのページで必要となる投稿に関する情報が詰まったオブジェクト変数」といった感じ。
WP内では、タイトルや本文、ID等といった投稿に関わる情報は基本このグローバル変数「$post」に格納されていると思っていい。


関数開発の魔法の言葉「global $post」

WPのカスタマイズでは独自に関数(function)を追加することも多いですが、PHPの基本として変数のスコープの関係上 、関数(function)内では、この「$post」を直接参照することができません。
例えば「$post」から投稿IDといった情報を取得しようとしても、関数(function)内からであればアクセス不可、ということになります。

ところが、魔法の言葉「global $post」を関数内で宣言するとどうでしょう、グローバル変数である $post は function 内でもあっさりアクセス可能になってしまいます。

function hogehoge_get_post_id() {
  // グローバル宣言!
  global $post;
  
  // すると関数内でもグローバル変数「$post」から投稿IDを取得できる
  $post_id = $post->ID; // 投稿オブジェクトから投稿IDを取得

}

これは正直よく使う、いや使ってた。関数開発するならとりあえず…くらいの軽いノリでやってたかもしれません。ただ…

本当は怖い、グローバル変数の「汚染」

他のプログラム言語でもいえることですが、基本、グローバル変数は使わないに越したことはない、です。

これはWordPressでも然り。理由は様々ありますが、ここで一番の問題はこの $post がWPの中でも非常に重要かつメジャーなグローバル変数である点。
当然コア関数、場合によってはテーマ、プラグインでも多用されがちなので、万が一、これを上書き・書き換えてしまうと予期しないトラブルの原因になったりします。所謂、グローバル変数の汚染ともいえそうな事態です。
また逆に、グローバル変数として多用されている以上、この $post に常に正しい値が入っているという保証もありません。他のプラグインやテーマからでも同様の理由で書き換えのリスクはあります。

「そんな大袈裟なw」「考え過ぎやw」とのご意見は当然でしょうし、WP側も グローバル変数 $post の利用を特に非推奨としているわけでもありません。
ただ「変数」である以上書き換えは容易です。個人的にはちょっとスッキリしない部分もある、もっとこう …WordPressっぽい処理はできないものかと…

get_queried_object() という選択肢

ならば global $post を使わずに関数(function)内で同様な投稿情報を参照するにはどうしたらいいか? 実はこういうときのための関数がWordPressには予め用意されているケースが多い。

そして探してみたらやっぱりありました。それが「get_queried_object()」。

「現在クエリされているオブジェクトを取得する」というそのまんま且つざっくりとした関数ですが、ソースを見てみると

function get_queried_object() {
  global $wp_query;
  return $wp_query->get_queried_object();
}

単に $wp_query->get_queried_object() のラッパーであることがわかります。ソースもざっくりでした。

「いやいや結局これもグローバル変数ですやんw」というツッコミが聞こえてきそうですが、まあ、グローバル変数の比較的安全な取り回しとして「関数化して値の取得しかできなくする」は常套手段ともいえます。変数と違い、意図せぬ書き換えのリスクは最低限回避できます。

「get_queried_object()」の優れた点は内部でグローバル変数「$wp_query」を利用しているため、関数(function)内でも問題なく動きます。なので、

function hogehoge_get_post() {

  // 投稿オブジェクトを取得
  $post_obj = get_queried_object();

  $post_id = $post_obj->ID; // 投稿ID取得
  $post_title = $post_obj->post_title; // 投稿タイトル取得
  $post_content = $post_obj->post_content; // 投稿コンテンツ取得
  ...

}

といった感じで、global $post と近い感覚でfunction内でも投稿オブジェクト( 投稿情報 )を取得可能です。ただし、アーカイブ系のページでは投稿ではなくTerm情報を返す挙動をしたり、そもそもオブジェクトを取得できないケースもあるため $post の完全な代替とはいかないようです。


そもそもその関数に $post は必要?

$post は非常に便利なグローバル変数です。便利すぎるがゆえにWordPressのカスタマイズにも多く利用されてきました。ただ…

そもそも「global $post」を使う理由はなんでしょうか?

例えば、$post に相当するような投稿情報を関数内で参照したいのなら、上記の get_queried_object() を利用すればほぼ事足りるでしょうし、ループ処理が必要なら WP Query でサブループを構築する方が安全性は高いでしょう。

なにより、関数内で利用したい情報は引数(パラメータ)で予め受け取る方が確実です。例えば、

※関数を実行時に「投稿ID」を引数で渡す
hogehoge_get_post( get_the_ID() );


※受け取った引数「投稿ID」を元に「get_post()」で投稿オブジェクトを取得する
function hogehoge_get_post( $post_id ) {
  
  // 投稿オブジェクトを取得
  $post_obj = get_post( $post_id );

    ...(処理開始)

}

…のように、$post から取れる類の投稿情報なら、引数で投稿IDさえ受け取っておけば get_post() などで関数内でも確実に処理が可能です。また必要な情報がタイトルやコンテンツ、パーマリンクなど限定されたものなら

※関数を実行時に「投稿タイトル」を引数で渡す
hogehoge_echo_title( get_the_title() );


※引数で受け取った「投稿タイトル」を元に処理を実行
function hogehoge_echo_title( $post_title = false ) {
  
  // $post_title がない場合の処理
  if ( !$post_title ) {
    echo "いつからタイトルがあると錯覚していた?";
 return;
  }
  
  // 引数で取得したタイトルを表示
  echo $post_title;
return;
}

…みたいな形で予め関数外で処理したうえで引数で渡しても特に問題はないわけです。

まあ、それでも「処理コストやコーディングの手間暇」と「実際起こるかどうかもわからないグローバル変数の汚染リスク」のトレードオフと考えれば「global $post」の利用を全否定するものではありません。
また管理画面のカスタマイズやフィルターフックでの処理の場合などでは「global $post」を利用したほうが無難なケースもあります。

これはどちらかというと、WordPressカスタマイズの「お作法」的問題なのかもしれません。