このサイトの PHP を 8.3 に上げたあと、トップの記事の一覧から、カテゴリーのボタンが 1 つも出なくなっていました。エラーの画面は出ず、ボタンだけが消えていました。
原因は、テーマの中の if ( $meta['tag-exclusion'] == 0 ) という 1 行でした。PHP 8 から、空の文字と 0 を == で比べたときの結果が変わったためです。
何が起きていたか
このテーマには、カテゴリーごとに「関連記事から除外する」というチェックボックスがあります。保存のときは、チェックがあれば '1'、なければ空の文字 '' を入れていました。
// カテゴリーの編集画面で保存するとき
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 で見ていました。
$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 のファイルにして動かしました。
<?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 になります。
$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 で探しておく


