PHP
初心者

number_format()のエラー対処法とPHPの型

number_format() のエラー対処法のついでに、PHPの「型」についても軽く触れた、初心者向けの記事です。



エラーの例

$var = 'qiita';

var_dump(number_format($var));

↑のコードを実行すると…

Warning: number_format() expects parameter 1 to be float, string given in...

古い環境の場合は…

Warning: number_format() expects parameter 1 to be double, string given in...

…というエラーが出力されます。

「1 つ目のパラメータは float 型にしてね。string 型が渡されてるよ!」というエラーです。

※もし、開発環境でこのコードを実行してもエラーが出ない場合は、PHP 初心者がまずやるべき設定(エラー設定)を参考に、PHP の設定を見直す事をオススメします。



手っ取り早いエラーの対処法

エラー内容でググってみると…

number_format((int) $var);

…とするのが手っ取り早い対処法だと紹介している記事がたくさん見つかりますが、これはやっては駄目です。

parameter 1 to be float と教えてくれてるのに、なぜ int 型にキャストするのか意味不明ですが、もしやるとすれば(本当はやらない方が良いのですがそれは後述)…

number_format((float) $var);

…と、float 型にキャストします。

※以前、以下のような記事を書いた時にも感じましたが、PHP の古い記事では、こういう間違ったコードがコピペコピペで「負の連鎖」を起こしている事がよくありますので、初心者の方は特に気を付けてください。

ググる時に期間指定をすると、かなりマシになります。



なぜ (int) だと駄目なの?

int 型にキャストしたら解決したと書いてる人がたくさんいるのに、なぜ駄目なの?と思った人は、以下のコードを実行してみてください。

$test = '12345.678';    // string型

var_dump(number_format((int) $test)); // string(6) "12,345"
var_dump(number_format((float) $test)); // string(6) "12,346"

//-- 第2引数を付けた場合
var_dump(number_format((int) $test, 2)); // string(9) "12,345.00"
var_dump(number_format((float) $test, 2)); // string(9) "12,345.68"

(int)(float) とでは、違う結果になりました。もし、何故こういう結果になるのか分からない場合は…

$test = '12345.678';    // string型

var_dump((int) $test); // int(12345)
var_dump((float) $test); // float(12345.678)

こういうコードを書いて実行してみれば、「int 型にキャストすると小数点以下が失われてしまうから」だという事が分かると思います。

※PHP はこのように、「書いて」「実行して」「結果を見る」のがとても楽な言語ですので、疑問に思ったらすぐにコードを書いて実行してみると良いです。



安易な型キャストは止めよう!

以上は、「PHP はコピペレベル。手っ取り早く直りさえすればOK」という人向けの内容です。

エラーでググると、(int) で型キャストすると書いているページがたくさん見つかりますので、(float) の方がまだ多少はマシではと思い、訂正の意味を込めて書きました。

しかし、本来は、このような安易な型キャストはしてはいけません。



以下のコードを実行すると、どういう結果になるでしょうか?

var_dump(number_format('qiita'));

これは当然

number_format() expects parameter 1 to be float, string given in...

というエラーになります。

では、以下のコードだとどうなるでしょうか?

var_dump(number_format((float) 'qiita'));

結果は、エラーにはならず string(1) "0" となります。

var_dump((float) 'qiita');    // float(0)

↑から分かる通り、string(5) "qiita"float 型にキャストすると、float(0) になるからです。

このような挙動を知らないと、型キャストしなければ気付けたバグに気付けない事に繋がるかもしれません。


重大なセキュリティホールに繋がる事も…

以下は、一時期世間を賑わせた WordPress 4.7.1 の「極めて致命的なセキュリティホール」についての解説記事です。

安易な型キャストは、時としてこのような重大なセキュリティホールに繋がる場合もあるという事を知っておいてください。



型の相互変換

ここで、更に話をややこしくしてみます(笑)

//-- 以下はいずれもエラーにはなりません

var_dump(number_format('12345.678')); // string(6) "12,346"
var_dump(number_format(null)); // string(1) "0"
var_dump(number_format(false)); // string(1) "0"

//-- ceil()もnumber_format()同様、本来はfloat型を受け取る関数
var_dump(ceil('qiita')); // float(0) ※エラーにならない

expects parameter 1 to be float やないんか~い! ceil()string 型でもエラーにならないんか~い!とツッコミたくなるでしょうが、このような挙動は、主には型の相互変換によるものです。

ちなみに、number_format('qiita') がエラーになるようになったのは、PHP5.3 からだったと思います。上記は PHP7.3.5 での実行結果ですが、今後のバージョンアップで、こういった挙動が変わる可能性もあると思います。

なお、PHP7 からは、型周りの問題に対する強力な解決法がサポートされています



結局どうすれば?

number_format((float) $var); のような対処法は、「臭いものに蓋をする」だけの処置だと思っておいてください。

number_format($var); がエラーを出すという事は、$var に 意図しない値が渡る事が原因ですので、そうならないよう修正するのが本筋です。

以下は、ググった時に見つけた内容ですが、ページにアクセスすると…

Warning:number_format() expects parameter 1 to be double, object given in...

…というエラーが出るので…

$chinryo = number_format((int)$alldata->data[$i]->chinryo);

…とすれば解決!というのは、ある意味メチャクチャな解決方法です。

エラーはそもそも利用者の目に触れさせてはいけません(公開環境では、エラーはログに記録し画面に出力してはいけません)し、object 型が渡されてエラーが出てるという事は、かなり致命的なバグだと思われます。

$chinryo は「賃料」でしょうか。趣味で作った個人サイトならまだしも、仕事に関わってそうな匂いもしますね…。