💜

【PHP】文字列と数値の比較

2021/04/05に公開

以下のsample.phpのコードで何が出力されるか説明できるだろうか。

sample.php
<?php

if('9crz' < 88){
    print 'answer7';
}else {
    print 'answer8';
}

おそらく大半の答えが「answer7」だと思う。しかしもし「answer8」と答える人がいれば、その人はPHPの最新ニュースを追っている人か、わからないから当てずっぽうで答えた人かどちらかだろう(笑)。

結論から言うとsample.phpのコードは
PHP7.4までは「answer7」、PHP8.0以降では「answer8」と出力される。

PHPを学習していて、バージョンによって文字列と数値の比較の挙動が異なっていたので以下にメモしておく。

文字列と数値の比較

文字列と数値を比較する場合、
PHP7.4までは、文字列が数値に変換される。
比較する値は文字列の先頭部分。先頭部分が有効な数値の場合はその値、有効な数値でない場合は値が0になる。
sample.phpでは、文字列「9crz」の先頭部分が数値であるため、数値の「9」として扱われる。数値の「9」と「88」の比較で「9」が小さいとみなされる。よって「'9crz' < 88」はtrueになるため「answer7」が出力される。

ところがPHP8.0以降では全く逆の変換がおきる。

PHP8.0以降では、数値が文字列に変換される。
sample.phpでは、数値の「88」が文字列「88」として扱われ、文字列「9crz」と「88」の比較になる。文字列はUnicode値(バイト列)をもとに辞書的に比較されるため、文字列の「9」と「8」の比較で「8」が小さいとみなされる。よって「'9crz' < 88」はfalseになるため「answer8」が出力される。

補足

2022年11月28日にPHP7.4は完全にサポート終了する予定。2020年11月26日にすでにPHP8.0がリリース。

PHP8.0はほかにも新機能がある。警告のエラーレベルが厳しくなる(Reclassifying engine warnings)・内部関数に正しくない型の引数を渡したとき常にTypeErrorが発生(Consistent type errors for internal functions)・配列orリソースor(オーバーロードされていない)オブジェクトに算術演算が使われたときTypeErrorになる(Stricter type checks for arithmetic/bitwise operators)・・・などなど。
今回の文字列と数値の比較の仕様変更は、こちらの新機能によってエラーになるので見つかりやすいとの意見が強い。

とりあえず動くコードを書くのが比較的簡単であるという、PHPの利点を感じ始めていた自分にとっては、型定義が厳しくなっていくのはもはやJavaに近付こうとしているようにも見えて、ちょっと寂しい。

PHPの動作確認は複数バージョンの比較もできる3v4l.orgがおすすめ。今回の事象はここで実行確認していて発見。
https://3v4l.org/

練習問題

以下は簡単な問題。PHP7の場合とPHP8の場合とで何が出力されるか、その理由も説明できればOK。

Exercise1.php
<?php

if('c' < 8){
    print 'A1';
}else {
    print 'B1';
}
Exercise2.php
<?php

if('99crz' < 88){
    print 'A2';
}else {
    print 'B2';
}

参考

公式ドキュメント
https://www.php.net/manual/ja/language.operators.comparison.php
【PHP8.0】PHP8.0の新機能
https://qiita.com/rana_kualu/items/fe7998fbe773544d5d25#saner-string-to-number-comparison

Discussion