PHP「fgetcsvはsetlocaleしないと日本語が読めない」は迷信なのか？

PHPの`fgetcsv`はロケールに依存しており、ロケールの環境設定次第では読めないデータが存在するという情報がいくつか存在する。

## fgetcsvのロケール問題に言及した記事

まずはどのような情報があるか見てみよう。

[【php】fgetcsv()はロケールの設定に依存する at softelメモ](https://www.softel.co.jp/blogs/tech/archives/2331)より:

> 「”名前”,”住所”,”岐阜”,”愛知”,”東京”」は読めるけど、「名前,住所,岐阜,愛知,東京」は読めない。

[fgetcsv関数を文字化け対応 setlocaleの文字コード指定 - [PHP + PHP] ぺんたん info](http://pentan.info/php/fgetcsv_char.html)より:

> ```
> ID,名前,都道府県ID,電話番号or携帯番号
> ```
>
> これをfgetcsv()関数で読み込むと
>
> ```
> Array(
>    [0] => ID,
>    [1] => ,
>    [2] => ID,
>    [3] => or携帯番号,
> )
> ```
> となります。

[PHP5 fgetcsv 日本語文字が消える場合 | Bamboo lath　日々の記録](https://www.mogumagu.com/wp/wordpress/archives/233)より:

> 今回問題となった元のデータはこれ
>
> ```
> 9033,裏面その他（ﾓﾉｸﾛ）,"9033,9034,9035,9036,9037,9038,9039,9040",,サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
> ```
>
> 今回問題となった箇所はここ
>
> ```
> サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
> ```
>
> 消えた状態
>
> ```
> <br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。
> ```

見つけられた範囲で読めないデータを提示している情報源は以上の3つだけだったが、データの提示なしに`setlocale`を使うことを推奨する記事はかなりの数があった。

## 本当にfgetcsvはsetlocaleしないとだめなのか？

手元のPHP環境(PHP 7.1)ではsetlocaleをしなくてもfgetcsvで問題なくデータが読み込めていたので、 fgetcsvはsetlocaleしないと読めないデータがあるというのは本当なのかという疑問が生まれた。

一応、macOS上のPHPだけでなく、Alpine Linux(localeがない)やUbuntu(localeがC)でも試したが再現しなかった。

また、SJISのCSVでも再現しなかった。

## 仮説: PHPのあるバージョンからロケール問題がなくなった

fgetcsvでロケールの調整が必要とする記事がPHP5時代のものが多かったので、もしかするとPHPのあるバージョンから上で取り上げたようなロケール問題が解消されているのではないかと考えた。

※ロケール問題が解消されたというのは、`setlocale`をしなくてもデータが読み込めるということで、`fgetcsv`がロケール依存でなくなったという意味ではない。

## 検証

PHPバージョンでの差異を検証するために、次の検証コードを用意した。ロケール`C`でUTF-8のCSVデータを読み込むロジックになっている。

```php
<?php

// テスト用CSVの用意
$filename = tempnam(sys_get_temp_dir(), 'CSV');
file_put_contents(
	$filename, 
	"名前,住所,岐阜,愛知,東京\n". // ロケールが正しくないと読めないとされる行
	'ID,名前,都道府県ID,電話番号or携帯番号'."\n". // ロケールが正しくないと読めないとされる行
    '9033,裏面その他（ﾓﾉｸﾛ）,"9033,9034,9035,9036,9037,9038,9039,9040",,サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。'
);

// テスト
setlocale(LC_ALL, 'C'); // ロケールをCにする
$file = new SplFileObject($filename, 'r');
$file->setFlags(SplFileObject::READ_CSV);
var_dump(iterator_to_array($file, false));
```

検証範囲はPHP5.2.1から7.3.1とする。

## 検証結果

上記の検証コードを[複数のPHPバージョンでコードを実行できる環境](https://3v4l.org/SNQsi#v521)で実行してみたところ、次の結果が得られた。

* PHP 5.3.7 - 5.6.38, 7.0.0 - 7.3.1: 再現せず
* B: PHP 5.2.1 - 5.3.6: ロケール問題が再現した

```php:PHP5.3.7~5.6.38,7.0.0~7.3.1の実行結果
array(3) {
  [0]=>
  array(5) {
    [0]=>
    string(6) "名前"
    [1]=>
    string(6) "住所"
    [2]=>
    string(6) "岐阜"
    [3]=>
    string(6) "愛知"
    [4]=>
    string(6) "東京"
  }
  [1]=>
  array(4) {
    [0]=>
    string(2) "ID"
    [1]=>
    string(6) "名前"
    [2]=>
    string(14) "都道府県ID"
    [3]=>
    string(26) "電話番号or携帯番号"
  }
  [2]=>
  array(5) {
    [0]=>
    string(4) "9033"
    [1]=>
    string(33) "裏面その他（ﾓﾉｸﾛ）"
    [2]=>
    string(39) "9033,9034,9035,9036,9037,9038,9039,9040"
    [3]=>
    string(0) ""
    [4]=>
    string(156) "サンプル画像は見本になります。<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。"
  }
}
```

```php:PHP5.2.1~5.3.6の実行結果
array(3) {
  [0]=>
  array(5) {
    [0]=>
    string(0) ""
    [1]=>
    string(0) ""
    [2]=>
    string(0) ""
    [3]=>
    string(0) ""
    [4]=>
    string(0) ""
  }
  [1]=>
  array(4) {
    [0]=>
    string(2) "ID"
    [1]=>
    string(0) ""
    [2]=>
    string(2) "ID"
    [3]=>
    string(14) "or携帯番号"
  }
  [2]=>
  array(5) {
    [0]=>
    string(4) "9033"
    [1]=>
    string(0) ""
    [2]=>
    string(39) "9033,9034,9035,9036,9037,9038,9039,9040"
    [3]=>
    string(0) ""
    [4]=>
    string(111) "<br />記載内容に合わせ、当方にてバランス良くレイアウトをさせていただきます。"
  }
}
```

## 結論

fgetcsvはsetlocaleしないと読めないデータがあるというのは、PHP5.3.6以下のバージョンで、PHP7の環境が普通の今日現在ではsetlocaleしなくても問題はなさそう。