PHP 8 で空の文字と 0 の比べ方が変わり、カテゴリーのボタンが消えた:文字と数の比べ方の変更と、テーマの直し方

広告WordPress・PHP を学べる本

Amazonのアソシエイトとして、WordPress functions.phpは適格販売により収入を得ています。

このサイトの PHP を 8.3 に上げたあと、トップの記事の一覧から、カテゴリーのボタンが 1 つも出なくなっていました。エラーの画面は出ず、ボタンだけが消えていました。

原因は、テーマの中の if ( $meta['tag-exclusion'] == 0 ) という 1 行でした。PHP 8 から、空の文字と 0 を == で比べたときの結果が変わったためです。

何が起きていたか

このテーマには、カテゴリーごとに「関連記事から除外する」というチェックボックスがあります。保存のときは、チェックがあれば '1'、なければ空の文字 '' を入れていました。

functions.php(保存の処理)
// カテゴリーの編集画面で保存するとき
function my_save_category_fields( $term_id ) {
    $meta = get_option( 'cat_' . $term_id );
    if ( isset( $_POST['tag-exclusion'] ) ) {
        $meta['tag-exclusion'] = '1'; // チェックあり:'1'
    } else {
        $meta['tag-exclusion'] = '';                      // チェックなし:空の文字
    }
    update_option( 'cat_' . $term_id, $meta );
}

ボタンを出す所では、「チェックがない」ことを == 0 で見ていました。

functions.php(前)
$meta = get_option( 'cat_' . $category->term_id );

// 「除外」にチェックがないカテゴリーだけ、ボタンを出す
if ( $meta['tag-exclusion'] == 0 ) {
    echo '<a class="cat_btn" href="' . esc_url( $link ) . '">' . esc_html( $category->name ) . '</a>';
}
保存されていた値 PHP 7 での == 0 PHP 8.3 での == 0 ボタン
''(チェックなし) true false PHP 8 で消える
'1'(チェックあり) false false どちらも出ない(ねらいどおり)

PHP 7 では '' == 0 が true だったので、チェックのないカテゴリーのボタンが出ていました。PHP 8 では false になり、全部のカテゴリーでボタンが出なくなりました。

PHP 7 の列は、php.net の資料(PHP 8.0 への移行の、後方互換性のない変更点)にある表によります。PHP 8.3 の列は、下で自分で試した結果です。

PHP 8.0 で変わったこと

php.net の資料によると、PHP 8.0 から、数と「数ではない文字」を == で比べるときは、数のほうを文字にして、文字どうしで比べるようになりました。数と「数として読める文字」の比べ方は、前と同じです。

空の文字 '' は数として読めないので、0 を文字の '0' にして比べます。'' == '0' は false です。

確かめた結果

このサイトの作業環境の PHP 8.3(8.3.32)で、比べ方の結果をファイルに書き出して確かめました。日本語をコマンドの引数に書かないように、試しのコードは .php のファイルにして動かしました。

cmp.php(一部)
<?php
$rows = [
    "'' == 0"   => ( '' == 0 ),
    "'0' == 0"  => ( '0' == 0 ),
    "'abc' == 0" => ( 'abc' == 0 ),
    "null == 0" => ( null == 0 ),
    "' 1' == 1" => ( ' 1' == 1 ),
    // …
];
$out = PHP_VERSION . "\n";
foreach ( $rows as $label => $result ) {
    $out .= $label . "\t" . var_export( $result, true ) . "\n";
}
file_put_contents( __DIR__ . '/result.txt', $out );
比べ方 PHP 7(php.net の資料の表) PHP 8.3(作業環境で実行)
'' == 0 true false
'abc' == 0 true(資料は "foo") false
'42foo' == 42 true false
'0' == 0 true true
'0.0' == 0 true true
' 1' == 1 true(資料は " 42") true
'1' == 1 (資料の表にない) true
'1 ' == 1 (資料の表にない) true
null == 0 (資料の表にない) true
'' == null (資料の表にない) true
'' === 0 — false
'1' === 1 — false
empty( '' ) — true
empty( '0' ) — true
empty( '1' ) — false
empty( null ) — true

変わったのは、片方が数で、もう片方が「数として読めない文字」のときだけです。'0' や ' 1' のような数として読める文字は、PHP 8.3 でも前と同じ結果でした。

このサイトのカテゴリーで数えた

作業環境のカテゴリー 21 個の保存の値を調べると、'' が 12 個、'1' が 9 個でした。PHP 8.3 で、ボタンが出るカテゴリーの数を比べました。

書き方 ボタンが出るカテゴリー(21 個のうち)
$meta['tag-exclusion'] == 0(前) 0 個
empty( $meta['tag-exclusion'] )(直した形) 12 個(チェックのないカテゴリー全部)

直し方

「チェックがない」を見たいので、empty() を使いました。''・'0'・null・キーがないときの、どれも true になります。

functions.php(直した形)
$meta = get_option( 'cat_' . $category->term_id );

// '' も、'0' も、キーがないときも「チェックなし」として扱う
if ( empty( $meta['tag-exclusion'] ) ) {
    echo '<a class="cat_btn" href="' . esc_url( $link ) . '">' . esc_html( $category->name ) . '</a>';
}
書き方 役目
empty( $meta['tag-exclusion'] ) 値が空(''・'0'・null・false)か、キーがないときに true。キーがなくても Warning は出ない
esc_url()・esc_html() 画面に出すときに、URL と文字を安全な形にする

値をはっきり決めて比べたいときは、isset() と === を使います。=== は型まで同じときだけ true なので、PHP のバージョンで結果が変わりません。

型まで合わせて比べる形
// 値をはっきり決めて比べる書き方
$excluded = isset( $meta['tag-exclusion'] ) && '1' === (string) $meta['tag-exclusion'];
if ( ! $excluded ) {
    // ボタンを出す
}
書き方 役目
isset( $meta['tag-exclusion'] ) キーがあって、null でないか
(string) ... 保存の値を文字にそろえる(1 でも '1' でも同じに扱う)
'1' === ... 型も値も同じときだけ true

キーがないときは Warning も出る

もし、カテゴリーをまだ一度も保存していなくて、tag-exclusion のキーがないときは、次のようになりました(PHP 8.3 で確かめた)。

キーがないとき
$meta = [ 'cat_color_bg' => '#fff' ];   // tag-exclusion のキーがない
var_dump( $meta['tag-exclusion'] == 0 ); // true(ただし Warning が出る)
var_dump( empty( $meta['tag-exclusion'] ) ); // true(Warning は出ない)

== 0 のほうは、キーがないと値は null になり、null == 0 は true なので、ボタンは出ます。ただし「Undefined array key "tag-exclusion"」という Warning が出ました。

php.net の資料によると、ないキーを読んだときのお知らせは、PHP 8.0 から Notice ではなく Warning になりました。ログが Warning で増えるときは、ここも見ます。empty() や isset() で先に確かめれば出ません。

テーマの中で同じ書き方を探す

== 0・!= 0・== '' のような、ゆるい比べ方の所を grep で探します。=== と !== は外しています。

ゆるい比べ方を探す
grep -rnE "(^|[^=!<>])[!=]=[[:space:]]*(0|''|\"\")([^0-9.]|$)" --include=*.php wp-content/themes/(テーマのフォルダ)

このサイトのテーマでは 90 行が出ました。多くは、数どうしの比べ(カテゴリーの ID など)で、PHP 8 でも結果は変わりません。

出てきた所 PHP 8 で変わるか
両方とも数(ID・件数など) 変わらない
片方が文字で、中身がいつも数('0'・'12' など) 変わらない
片方が文字で、空や数でない文字が入ることがある(チェックボックス・入力欄・オプションの値) 変わる。直す

オプションやメタ情報の値は、保存のしかたによって '' や '1' の文字になります。数と比べている所は、保存の処理まで見て、どんな値が入るかを確かめます。

まとめ

  • PHP 8.0 から、'' == 0 と 'abc' == 0 は false。'0' == 0 は今も true
  • チェックボックスなどで空の文字が入る値を == 0 で見ていると、PHP 8 で結果が逆になる
  • 空かどうかは empty()、決まった値かどうかは isset() と === で見る
  • PHP を上げる前に、テーマの中のゆるい比べ方を grep で探しておく

参考

関連機能

  1. Google カレンダーで「七五三」「クリスマス」が祝日のように出るとき:「日本の祝日」を祝日だけにする設定

  2. XAMPP の phpMyAdmin で「Maximum execution time of 300 seconds」が出るときの直し方:config.inc.php と php.ini(phpMyA…

  3. WordPress Simply Staticプラグインを使用している際に code 429 Too Many Requestsが発生

サイドバー

よく使うカスタマイズ

最近の記事

Profile

PAGE TOP