【出力制御】ob_start()で効率よく中身があるときだけ出力する

※本ブログの目的は個人の備忘録であり、コードは参考用として掲載しています。
実際に使用される際は、ご自身の環境で十分に動作確認を行ってください。
コードの利用によって生じたいかなる問題についても責任を負いかねますので、あらかじめご了承ください。

はじめに

WordPressやPHPで「中身がある時だけ表示したい」「表示順をコントロールしたい」「先に処理をしてから後でまとめて出力したい」と思ったことはありませんか?

そんなときに役立つのが、ob_start() を使った出力のバッファリングです。

この機能を使えば、画面にすぐ表示するのではなく、一旦出力を変数にためてから、必要に応じて出力の有無や順序をコントロールできます。

ob_start() とは

ob_start() は「Output Buffering(出力のバッファリング)」の略で、PHPの出力内容を一時的にメモリ上にためておける機能です。

通常、echo や HTML を書くとすぐにブラウザに表示されますが、ob_start() を使うと、

  • 出力を変数として扱える
  • 中身があるかないかを empty() でチェックできる
  • 条件によって「出す or 出さない」を判断できる

というように、出力を“処理対象”としてコントロールできるようになります。

ob_start() は「中身の有無」に依存したレイアウト制御に強い

普段は見逃しがちな ob_start() ですが、

  • 「出力を貯める」
  • 「条件で出す・出さないを判断する」
  • 「順序や構造を後から決める」

という制御を簡単にするための、非常に強力な手段です。

とくに「中身があるときだけリンクを出したい」「順番が自由に変わるセクションに対応したい」といった、柔軟性の高いテンプレートを作りたいときにはとても重宝します。

ob_start()が活躍する場面

  • 複数のカスタムフィールドやセクションの中身が「任意入力」のとき
  • ACF Flexible Content で順番を自由にしているとき
  • 中身の有無によって表示順や表示内容を制御したいとき
  • 動的に目次やリンクリストを作成したいとき
  • 条件によってレイアウトや構造を変えたいとき

ob_start()の基本的な使い方

ob_start() は、echo などで出力される内容を「変数に貯めてから出す」ことができる関数です。

通常、echo などで出力された内容はすぐにブラウザに送られますが、ob_start() を使うと、それらの出力を「一旦ためておく」ことができます。

ob_start();                     // 出力のバッファリングを開始
echo '<p>こんにちは</p>';
$html = ob_get_clean();         // 出力を変数に取り出してバッファをクリア

echo $html;                     // 必要に応じて出力

このように、echo などで出力された内容をそのまま変数に格納できるというのが、ob_start() の基本的な使い方です。

ob_start()の便利な使い方

1. 中身があるときだけ、まとまりを出力する

WordPressでは、カスタムフィールドやループなど、「条件によって表示する項目が変わる」ことがよくあります。

そんなとき、ob_start() を使うことで、以下のようにHTML構造を自然に保ったまま「出すか出さないか」をあとから決められます。

ob_start();

if (get_field('price')) {
    echo '<p>価格:' . esc_html(get_field('price')) . '</p>';
}
if (get_field('date')) {
    echo '<p>開催日:' . esc_html(get_field('date')) . '</p>';
}
if (get_field('location')) {
    echo '<p>場所:' . esc_html(get_field('location')) . '</p>';
}

$content = trim(ob_get_clean());

if (!empty($content)) {
    echo '<section class="event-info">';
    echo '<h2>イベント情報</h2>';
    echo $content;
    echo '</section>';
}

このようにすることで、

  • 空のフィールドばかりだと <section> ごと出力されない
  • フィールド1つでもあれば見出し付きで出力される

という スマートな表示切り替えが可能になります。

2. 複数セクションのリンクリストを効率的に作成できる

たとえば、カスタムタクソノミーのタームごとに投稿を分類し、中身があるタームだけを対象に「リンクリスト(目次)」を作りたい場合、通常は2回処理を回す必要があります。

しかし ob_start() を使えば、カスタムタクソノミーの各タームごとに投稿リストをバッファにため、中身があるタームだけをリンクリストに含めて出力します。

  1. 各タームの投稿一覧(セクション)の出力内容を $sections[] に貯めておく
  2. 中身があるタームだけリンクとしてリスト化する
  3. 最後にまとめて各タームの投稿セクションを出力する

たとえば以下のような流れです。

$sections = [];

$terms = get_terms([
  'taxonomy' => 'sample_taxonomy',
  'hide_empty' => false,
]);

foreach ($terms as $term) {
  ob_start();

  $query = new WP_Query([
    'post_type' => 'sample_post_type',
    'tax_query' => [[
      'taxonomy' => 'sample_taxonomy',
      'field' => 'slug',
      'terms' => $term->slug,
    ]],
    'posts_per_page' => -1,
  ]);

  if ($query->have_posts()) {
    echo '<section id="' . esc_attr($term->slug) . '">';
    echo '<h2>' . esc_html($term->name) . '</h2>';

    while ($query->have_posts()) {
      $query->the_post();
      echo '<article>';
      echo '<h3>' . get_the_title() . '</h3>';
      the_content();
      echo '</article>';
    }

    echo '</section>';
  }

  wp_reset_postdata();

  $content = trim(ob_get_clean());

  if (!empty($content)) {
    $sections[$term->slug] = [
      'title' => $term->name,
      'content' => $content,
    ];
  }
}

// リンクリスト出力
if (!empty($sections)) {
  echo '<ul class="section-links">';
  foreach ($sections as $slug => $section) {
    echo '<li><a href="#' . esc_attr($slug) . '">' . esc_html($section['title']) . '</a></li>';
  }
  echo '</ul>';
}

// セクション本体を出力
foreach ($sections as $section) {
  echo $section['content'];
}

このように、ob_start() で一旦出力をためてから中身の有無を判定し、リンクリストとセクションの両方を効率的に組み立てられます。

3. return したい関数の中で echo を使いたい(ショートコード)

WordPressのショートコードなど、「return で値を返すことが求められる」関数の中では、通常の echo は使えません。

そんなとき、ob_start() が力を発揮します。

通常の return では echo が無効

function my_shortcode() {
    echo '<p>Hello!</p>'; // 表示されない
    return;
}

ob_start() を使った安全なパターン

function my_shortcode() {
    ob_start();
    echo '<p>Hello!</p>';
    return ob_get_clean(); // 表示される
}

echo を使って自由にHTMLを出力しつつ、それを return できるのが ob_start() の強みです。

この方法は、ショートコードだけでなく「ウィジェットの出力」など、出力内容を返す必要があるすべての場面で使えます。

4. 出力と制御を分離してテンプレートの見通しを良くする

「出力」と「制御」を切り離せるため、ロジックを一箇所に集約しやすく、テンプレートの見通しが良くなるのも大きなメリットです。

出力をすぐに画面に流さず、一旦ためてから必要な順番や条件でまとめて出すことで、ロジックと表示を切り離します。複数の任意入力フィールドやセクションの表示順を自由に変えたい場合や、動的にレイアウトを変えるテンプレート作成時などに便利です。

まとめ

ob_start()は、

  • 中身の有無を判定して無駄な出力を防ぐ
  • 複数セクションのリンクリストを効率よく作る
  • 出力とロジックを分離してテンプレートを整理する

といった使い方ができ、WordPressテーマ開発やPHPのテンプレート設計で非常に役立ちます。

使いすぎには注意

ob_start()は強力ですが、多用するとコードが追いにくくなる場合もあります。特にネストが深くなったり、何度もバッファを開始・終了すると混乱しやすいです。

ポイントは「本当に必要な部分だけ」に限定して使い、なるべく処理の流れが分かりやすいコードを書くことです。

おわりに

「何を出すか」と「どう出すか」を分けて考えることは、テンプレート作りにおいて大切なポイントです。

ob_start() を使えばこの区別がつけやすくなり、結果的にコードの整理やユーザー体験の向上につながります。

テンプレート開発の一つの節目として、ぜひ覚えておきたい技術です。