mb_convert_encoding()の警告

エンコーディング変換でfrom_encodingの候補が複数になってしまうことがある。
普通は送信側の仕様にencoding指定やI/Fのバージョンを導入してあいまいな状況を避けるのだが、受け側で対処しなければならないことがある。
ただ複数エンコーディングを指定して不正な入力を受けるとwarningが発生してしまう。

<?php
$string = mb_convert_encoding($input, 'UTF-8', 'EUC-JP,UTF-8');
// PHP Warning:  Unable to detect character encoding in ...

このとき内部的にfrom_encodingはpass(none)になる。そこでエンコーディングリストにpassと入れてみるが、変わらない。

<?php
$string = mb_convert_encoding($input, 'UTF-8', 'EUC-JP,UTF-8,pass');
// PHP Warning:  Unable to detect character encoding in ...

複数エンコーディング指定時の内部処理はmb_detect_encoding()と似ているが、そもそもpassや8bit(binary)は検出の邪魔になるため対象にならないのだろう。*1

<?php
$encoding = mb_detect_encoding($input, '8bit,pass')
// false

結局mb_convert_encoding()だけで警告を出さないようにするのは無理のようだ。

<?php
if (($encoding = mb_detect_encoding($input, 'EUC-JP,UTF-8')) === false) {
    $encoding = 'pass';
}
$string = mb_convert_encoding($input, 'UTF-8', $encoding);
// または微妙に分かりにくいが
$encoding = array_slice(array_filter([mb_detect_encoding($input, 'EUC-JP,UTF-8'), 'pass']), 0, 1)[0];
$string = mb_convert_encoding($input, 'UTF-8', $encoding);
// または成功するのが分かってる場合は複数エンコーディングでも問題ないので無理をして1文で頑張ると
$string = mb_convert_encoding($input, 'UTF-8', array_filter([mb_detect_encoding($input, 'EUC-JP,UTF-8'), 'pass']));

どの道スマートではないが、ユーテリティ関数や前処理としてくくり出すのが適切なケースが大部分になると思う。

to_encodingでは

余談として、to_encodingについてはpass/8bitの動作は異なる。
base64などが単にBase64エンコードされるのに対し、PHP5.5で試した限りではpass-throughされずISO-8859-1(またはそれに類似したエンコーディング)として変換処理される雰囲気だった。

<?php
$string = mb_convert_encoding($input, 'base64', 'pass');
// ≡ base64_encode($input);

$string = mb_convert_encoding($input, 'pass', 'pass');
$string = mb_convert_encoding($input, '8bit', 'pass');
// ≡ mb_convert_encoding($input, 'ISO-8859-1', 'pass');
// ≠ $input

*1:ドキュメントにはpassが使えるという記述はあっても動作についての細かい説明はない。ベースのlibmbflについても同様。