【WordPress×PHP8】Warning: Undefined variable / Trying to access array offset on value of type null が同時に出た原因と直し方

PHP7からPHP8に上げた瞬間、今まで問題ない(ように見えていた)WordPressの自作コードが、突然Warningを吐き始めました。

Warning: Undefined variable $object ... on line 41

Warning: Trying to access array offset on value of type null ... on line 33

Warning: Attempt to read property "ID" on null ... on line 228

「PHP8にしたら壊れた?」と思いましたが、実際は壊れたわけではなく、 「今まで見逃してた未定義・nullアクセスが見える化した」だけでした。これは今までのコードを直すチャンスでもあります。

体験談として「なぜ同時にエラーが出たのか」と「WordPress(functions.php+ACF)での直し方」と、また起きない形までまとめます。

WordPressサイトをPHP7→PHP8にして警告が増えた理由

PHP8で突然ダメになったというより、未定義やnullが「Warning」として表に出やすくなったことが理由です。

WordPressは、$post などのグローバル変数や「ループ内ならある前提」で書かれたコードがよくあります。そのためfunctions.phpに関数を書いて、好きなタイミングで呼ぶようになると、その前提が一気に崩れます。

私のケースもまさにそれで、functions.phpの関数内で $post->ID といったコードを当然のように使っていたのが原因でした。

Warning: Undefined va friable(未定義変数)の原因と直し方

「Undefined variable」が出たコードの一例がこちら。

PHP
get_the_terms($post->ID, 'tag');
Warning: Undefined variable $object in /Users/Local Sites/wp-content/themes/original/test.php on line 41
画面に表示されたエラーコード

functions.phpの関数内でこれをやると、状況次第で $post が存在しません。その結果、$post 未定義 → $post->ID 参照不可となってしまいます。

グローバル変数でも常に使えるわけではなく、WordPressは、ループ外・フックの実行順・カスタムクエリ後・管理画面側など、「今の投稿」が決まっていないタイミングがあります。

そこで、必要なところで get_post() を使って “ある/ない” を判定します。get_post() は、null/false/0 などの値が渡された場合、ループ内のグローバルpostを返します。

【これからの書き方】

functions.phpの関数内では $post をしっかりと定義。まず get_post() で取得し、取れなければそのまま return

PHP
function my_func() {
  $post = get_post();
  
  // 早期return:型チェック + ID存在確
  if ( ! $post instanceof WP_Post || empty( $post->ID ) ) {
    return;
  }

  $post_id = absint( $post->ID ); // 整数化
  // 以降で $post_id を使う
}

PHP: 型演算子 – Manual

absint() – Function | Developer.WordPress.org

Warning: Trying to access array offset on value of type null(nullに対して配列アクセス)の原因と直し方

「Trying to access array offset on value of type null」は、「配列じゃないものに配列としてアクセスしようとしている」というエラーです。

今回このエラーが出たのは、ACFを使って画像フィールドを取得しようとした時です。

PHP
$screenshot_url = get_field('screenshot')['url'];
Warning: Trying to access array offset on value of type null $object in /Usaers/Local Sites/wp-content/themes/original/test.php on line 41
画面に表示されたエラーコード

このフィールドの返り値は「画像配列(array)」にしています。そのため通常は ['url'] が取れます。
しかし必須項目にはしていなかったため、空欄になっている投稿がありました。フィールドが空欄だと get_field('screenshot')null/false になることがあります。そのため、「nullなのに配列としてアクセスしようとしている」という警告が出てしまったわけです。

【これからの書き方】

is_arrayempty でチェックしてから ['url'] を読む。

PHP
<?php
$image = get_field( 'screenshot' );

// 型ガード:配列 + url キーの存在確認
if ( ! is_array( $image ) || empty( $image['url'] ) ) {
  return; // または何も出さない
}

// alt属性のフォールバック(ACF配列には alt も入ってる場合が多い)
$alt = ! empty( $image['alt'] ) ? esc_attr( $image['alt'] ) : '';

// 幅・高さも取れるなら出す(レイアウトシフト防止)
$width  = ! empty( $image['width'] ) ? absint( $image['width'] ) : '';
$height = ! empty( $image['height'] ) ? absint( $image['height'] ) : '';

$attr = '';
if ( $width && $height ) {
  $attr = sprintf( ' width="%d" height="%d"', $width, $height );
}
?>
<img src="<?php echo esc_url( $image['url'] ); ?>" alt="<?php echo $alt; ?>"<?php echo $attr; ?>>

ACFのImageフィールドは返り値形式が「array/string/integer」を選べる仕様になっているため、ここの選択も考えておくとこういったコードエラーが減らせるかもです。

2つ(+もう1つ)が同時に出る理由:未定義→null→アクセス

エラーって別々に見えても、実は一つの原因から連鎖してることが多いんですよね。

  • $post が無い(または期待してる文脈じゃない)
  • それでも $post->ID でデータを取ろうとしている
  • ID前提で処理を書き続ける(terms、サムネ、ACF…)
  • 結果として、別々のWarningが同じページに並ぶ

1個目(未定義/取得失敗)を放置したまま次に進むと、nullに対して ->ID['url'] といった記述で、警告がセットで出る。

Attempt to read property "ID" on null というエラーも同時に発生していました。

PHP
// 悪い連鎖の概念図(functions.php内で起きがち)
$post_tag = get_the_terms($post->ID, 'tag');  // $postが未定義 → Undefined variable
$screenshot_url = get_field('screenshot')['url']; // ACF空欄 → array offset on null
$post_id = $post->ID;            // $postがnull扱い → Attempt to read property "ID" on null
Warning: Attenpt to read property 'ID' on null in $object in /Users/Local Sites/wp-content/themes/original/test.php on line 41
 画面に表示されたエラーコード

WordPressでの改善例(テーマ/プラグイン/テンプレで起きるパターン)

私は get_the_terms($post->ID, 'tag') といったコードが「いつでも使える」と思っていました。
でも get_the_terms() は戻り値が WP_Term[]|false|WP_Error です。termsが無いと false も返るので、処理をしっかり記述しないと別のWarningを呼びます。

get_the_post_thumbnail($post_id, ...) も同じで、$post_id をどこで作ってるかが曖昧だとうまくいかなくなります。

functions.php関数内の $post 前提と、ACF/option/metaの返り値を、必ず配列だとしないコードを考える必要があります。

PHP
/**
 * ウィジェット的な出力(記事サムネ+タグ表示)
 * 
 * @return void 出力できない場合は何も返さない
 */
function my_widget_like_output() {
  // 1) post 取得 + 型ガード + 早期return
  $post = get_post();
  if ( ! $post instanceof WP_Post || empty( $post->ID ) ) {
    return;
  }

  $post_id = absint( $post->ID );

  // 2) タクソノミー取得(tags)+ エラーハンドリング
  $terms = get_the_terms( $post_id, 'tag' );
  if ( is_wp_error( $terms ) || false === $terms || ! is_array( $terms ) ) {
    $terms = array();
  }

  // 3) ACF 画像(配列返し)の安全な取得
  $image = get_field( 'screenshot', $post_id ); // 第2引数で post_id を明示(推奨)
  
  $image_html = '';
  if ( is_array( $image ) && ! empty( $image['url'] ) ) {
    $url    = esc_url( $image['url'] );
    $alt    = ! empty( $image['alt'] ) ? esc_attr( $image['alt'] ) : '';
    $width  = ! empty( $image['width'] ) ? absint( $image['width'] ) : '';
    $height = ! empty( $image['height'] ) ? absint( $image['height'] ) : '';

    $size_attr = '';
    if ( $width && $height ) {
      $size_attr = sprintf( ' width="%d" height="%d"', $width, $height );
    }

    $image_html = sprintf(
      '<img class="my-img" src="%s" alt="%s"%s>',
      $url,
      $alt,
      $size_attr
    );
  }

  // 4) 出力(画像が無い場合は出さない early return も可)
  if ( empty( $image_html ) ) {
    return;
  }

  echo $image_html;

  // 5) タグも出すなら(念のためエスケープ)
  if ( ! empty( $terms ) ) {
    echo '<ul class="tags">';
    foreach ( $terms as $term ) {
      if ( ! $term instanceof WP_Term ) {
        continue; // 念のため型チェック
      }
      printf(
        '<li><a href="%s">%s</a></li>',
        esc_url( get_term_link( $term ) ),
        esc_html( $term->name )
      );
    }
    echo '</ul>';
  }
}

再発防止:PHP8時代の書き方(isset / ?? / nullsafe / 初期化)

  1. 配列を扱う前に – is_array()で型チェック(配列かどうか確認) + empty()で空チェックを行う
  2. オブジェクトを扱う時 – 一度変数に取得してから、プロパティやメソッドを操作する(パフォーマンスとnullチェックの効率化)
  3. 未定義変数の対策 – 変数を使う前に初期化しておく
  4. Null合体演算子(???-> – 便利だが、適切なチェックの代替にはならない。適切なバリデーションと併用する
PHP
/**
 * 安全版:画像ブロック出力
 * 
 * @return void
 */
function my_safe_block() {
  // 1) post 取得 + 早期return
  $post = get_post();
  if ( ! $post instanceof WP_Post || empty( $post->ID ) ) {
    return;
  }

  $post_id = absint( $post->ID );

  // 2) ACF 画像取得 + 早期return(画像が無いなら何も出さない)
  $image = get_field( 'screenshot', $post_id );
  if ( ! is_array( $image ) || empty( $image['url'] ) ) {
    return;
  }

  // 3) 安全な出力(alt / width / height 対応)
  $url    = esc_url( $image['url'] );
  $alt    = ! empty( $image['alt'] ) ? esc_attr( $image['alt'] ) : '';
  $width  = ! empty( $image['width'] ) ? absint( $image['width'] ) : '';
  $height = ! empty( $image['height'] ) ? absint( $image['height'] ) : '';

  $size_attr = '';
  if ( $width && $height ) {
    $size_attr = sprintf( ' width="%d" height="%d"', $width, $height );
  }

  printf(
    '<img src="%s" alt="%s"%s>',
    $url,
    $alt,
    $size_attr
  );
}

isset / ?? / nullsafe について

isset() は「変数が宣言されていてnullじゃないか」を判定できます。未定義対策の基本として押さえやすいです。

?? は便利だけど、乱用すると「本当は直すべき取得失敗」を隠してしまうため、必要最低限を心がけたいところです。

?->(nullsafe)はオブジェクトチェーンには効くけど、配列の ['url'] には効きません。ACF画像の事故は is_array + empty が王道です。

状況使うもの理由
変数が未定義かチェックisset()基本。未定義対策の王道
未定義の時デフォルト値??短く書けるが、取得失敗を隠しやすい
オブジェクトチェーン?->PHP 8 で簡潔に。ただし配列には効かない
配列のキー存在確認is_array() + empty()ACF画像など配列アクセスはこれが堅い
早期returnif (!$post) return;WordPress 流。取得失敗を明示的に止める
参考:PHP: isset – Manual

まとめ(今日から直せるチェックリスト)

  • functions.phpの関数内で $post を当然のように使わない
    get_post() して取れなければreturn
  • $post->ID を読む前に $post が存在するか確認
    Attempt to read property "ID" on null とならないように
  • ACF画像(配列返し)は空欄があり得る
    is_array + !empty($image['url']) でチェック

【バージョンごとの変更内容参考】PHP 付録 – Manual

カテゴリー : WordPress

index