結論
fw3/streamsをcomposer require fw3/streams
して、CSVを扱っている処理を次のように囲ってあげれば文字化けしなくなります。
※ PHP7.2.0未満(下限PHP5.3.3)で解決したい場合はfw3_for_old/streamsを利用してください。(composer require fw3_for_old/streams
)
fw3/streamsおよびfw3_for_old/streams共にPHP8.0 beta 2まで動作確認済みのため、PHP7.2.0以上のシステムの場合はどちらを入れても変わりはありません。
※ composerを使用できない環境の場合は、こちらからZIPファイルをダウンロードし、展開して任意のディレクトリにコピーしてください。
その後、使用対象となる処理より前にrequire_once sprintf('%s/src/filters_require_once.php', $path_to_copy_dir);
としてsrc/filters_require_once.php
を読み込むようにしてください。
2022/01/20追記:PHP8.1における Sigh. Double sigh. な修正について。
PHP8.1になって突然SJIS-win
が削除されました。
https://github.com/php/php-src/commit/e2459857afdf70ed4bc23bc7f614e9f383eb3072
そのため、日本語PHPにおける鉄板エンコーディング設定が使えなくなっています。
この記事で紹介しているfw3/streams
および、fw3_for_old/streams
では「PHP8.1.*
かつSJIS-win
が指定された場合はCP932
に読み替える」対応を行い、実質的な影響が出ないようにしています。
読み込み
既存の実装が次のような形の場合。
$fp = \fopen($csv_file_path, 'r+b');
$data = [];
while (($row = \fgetcsv($fp, 1024)) !== FALSE) {
$data[] = $row;
}
\fclose($fp);
次のようにラップしてあげるだけで解決。
<?php
// fw3_for_oldを使用している場合は、`use fw3\`を`use fw3_for_old\` として読み替えてください。
use fw3\streams\filters\utilitys\StreamFilterSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertEncodingSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertLinefeedSpec;
$data = StreamFilterSpec::decorateForCsv(function () use ($csv_file_path) {
// フィルタの設定
$spec = StreamFilterSpec::resource($csv_file_path)->read([
StreamFilterConvertEncodingSpec::toUtf8()->fromSjisWin(), // Shift_JIS(Windows-31J、MS932)として読み込んでUTF-8として出力
]);
//// STA 元のコード
//
$fp = \fopen($spec->build(), 'r+b'); // ここだけ `$csv_file_path` を `$spec->build()`に書き換える
$data = [];
while (($row = \fgetcsv($fp, 1024)) !== FALSE) {
$data[] = $row;
}
\fclose($fp);
//
//// END 元のコード
return $data;
});
書き込み
既存の実装が次のような形の場合。
$fp = \fopen($csv_file_path, 'w+b');
foreach ($rows as $row) {
\fputcsv($fp, $row);
}
\fclose($fp);
次のようにラップしてあげるだけで解決。
<?php
// fw3_for_oldを使用している場合は、`use fw3\`を`use fw3_for_old\` として読み替えてください。
use fw3\streams\filters\utilitys\StreamFilterSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertEncodingSpec;
use fw3\streams\filters\utilitys\specs\StreamFilterConvertLinefeedSpec;
$data = StreamFilterSpec::decorateForCsv(function () use ($csv_file_path, $rows) {
// フィルタの設定
$spec = StreamFilterSpec::resource($csv_file_path)->write(array(
StreamFilterConvertEncodingSpec::toSjisWin()->fromUtf8(), // UTF-8として読み込んでShift_JIS(Windows-31J、MS932)として出力
StreamFilterConvertLinefeedSpec::toCrLf()->fromAll(), // いかなる改行コードであってもCRLFとして出力
));
//// STA 元のコード
//
$fp = \fopen($spec->build(), 'w+b');
foreach ($rows as $row) {
\fputcsv($fp, $row);
}
\fclose($fp);
//
//// END 元のコード
});