【PHP】number_format()のエラー対処法と型キャスト

[`number_format()`](https://www.php.net/manual/ja/function.number-format.php) のエラー対処法のついでに、PHPの「型」についても軽く触れた、初心者向けの記事です。

--------------------------------------------------------------------------------

## エラーの例

```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 初心者がまずやるべき設定（エラー設定）](https://qiita.com/fallout/items/31f793708b243033fab1)を参考に、PHP の設定を見直す事をオススメします。

--------------------------------------------------------------------------------

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

[エラー内容でググってみる](https://www.google.co.jp/search?q=number_format()+expects+parameter+1+to+be+float&ie=UTF-8)と…

```php
number_format((int) $var);
```

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

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

```php
number_format((float) $var);
```

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

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

- [全角スペース等対応のtrim処理](https://qiita.com/fallout/items/a13cebb07015d421fde3)
- [PHP 初心者がまずやるべきエラー設定](https://qiita.com/fallout/items/31f793708b243033fab1)

※[ググる時に期間指定をする](https://www.google.co.jp/search?q=Google%E6%A4%9C%E7%B4%A2+%E6%9C%9F%E9%96%93%E6%8C%87%E5%AE%9A&ie=UTF-8)と、かなりマシになります。

--------------------------------------------------------------------------------

## なぜ (int) だと駄目なの？

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

```php
$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)` とでは、違う結果になりました。もし、何故こういう結果になるのか分からない場合は…

```php
$test = '12345.678';    // string型

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

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

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

- [PHP・Ruby・Perl等のスクリプト言語をサクっと書いてサクっと実行（EmEditor編）](https://qiita.com/fallout/items/0d4f221e176d6521091f)

--------------------------------------------------------------------------------

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

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

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

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

--------------------------------------------------------------------------------

### 例

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

```php
var_dump(number_format('qiita'));
```

これは当然
`number_format() expects parameter 1 to be float, string given in...`
というエラーになります。


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

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

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


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

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

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


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

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

- [WordPress 4\.7\.1 の権限昇格脆弱性について検証した \| 徳丸浩の日記](https://blog.tokumaru.org/2017/02/wordpress-4.7.1-Privilege-Escalation.html)

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

--------------------------------------------------------------------------------

## 型の相互変換

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

```php
//-- 以下はいずれもエラーにはなりません
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` 型でもエラーにならないんか～い！とツッコミたくなるでしょうが、このような挙動は、主には[型の相互変換](https://www.php.net/manual/ja/language.types.type-juggling.php)によるものです。

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

なお、[PHP7 からは、型周りの問題に対する強力な解決法がサポートされています](https://www.google.co.jp/search?q=qiita+PHP7+%E5%9E%8B%E5%AE%A3%E8%A8%80&ie=UTF-8)。

--------------------------------------------------------------------------------

## 結局どうすれば？

`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` は「賃料」でしょうか。趣味で作った個人サイトならまだしも、仕事に関わってそうな匂いもしますね…。