この記事は PHP Advent Calendar 2022 13日目の記事です。
つい先日、PHP8.2がリリースされました ![]()
毎回メジャーリリースでは便利な新機能が追加される一方で、下位互換性のなくなる変更や非推奨になる機能も存在します。
既存システムのPHPをバージョンアップするためには、これらの変更に対応していく必要があります。
ここでは、PHP8.2での変更の1つ「mbstringの一部エンコーディングの非推奨化」を取り上げます。
変更の概要
mbstringのmb_convert_encoding()やmb_strlen()などの関数には、$encodingという引数があり、引数の文字列の文字コードを指定できます。
ここに指定できるエンコーディングのうち、qprint, base64, uuencode, HTML-ENTITIESの4種が非推奨となります。
UTF-8やShift_JISなどが「文字をバイト列にエンコードする」ものであるのに対し、上記4つは「バイト列をバイト列でエンコードする」ものであるというのが理由のようです。
PHP8.2以降の代替手段
今までに上記のエンコーディングを$encoding引数に利用していた場合、PHP8.2からは以下のいずれかの対応になると思います。
-
mb_convert_encoding()を使っていた場合:他の関数を使って変換を行う - その他の関数を使っていた場合:他の文字コードに変換した上で扱う
つまり、いずれの場合もmb_convert_encoding()相当の変換さえできればいいわけです。
では、各エンコーディングの変換処理をどのように置き換えればいいか、ここから紹介していきます。
qprint (Quoted-printable)
qprint (Quoted-printable) は以下の関数で変換できます。
名前がそのまんまですね ![]()
実際に動かしてみても、同じように変換されていることがわかります。
base64
Base64も同様に、PHPの関数で変換できます。
- エンコード:
base64_encode() - デコード:
base64_decode()
HTML-ENTITIES
この流れで行くとhtmlentities(), html_entity_decode()で変換、となりそうですが、これは違う変換をする関数です。
echo htmlentities("<p>Hello世界</p>\n");
echo mb_convert_encoding("<p>Hello世界</p>\n", 'HTML-ENTITIES');
<p>Hello世界</p>
<p>Hello世界</p>
HTML-ENTITIES相当の変換を行うのは、同じmbstringモジュールのmb_encode_numericentity() / mb_decode_numericentity() です。
どちらの関数も第2引数に、文字数値参照に変換する文字の範囲などを表す配列を渡しますが、[0x80, 0x10ffff, 0, 0x1fffff]としておくとよさそうです。
参考:
- https://github.com/symfony/symfony/pull/45532/files#diff-940b51ffa31dedac17aa49cd8e04e44aa8b3747782c7f9f27457b0510587c05d
- https://github.com/symfony/symfony/pull/46221/files
uuencode
uuencodeの変換関数は、冒頭で紹介した公式の移行ガイドにも記載されています。
- エンコード:
convert_uuencode() - デコード:
convert_uudecode()
これらを使えば解決……と思いきや、uuencodeの場合はそこまで単純ではありません。
mbstring と convert_uuencode() / convert_uudecode() の違い
Wikipediaにあるように、uuencodeは「一行のヘッダー、複数行のエンコード文字列、一行のフッターからなる」フォーマットです。また、エンコード文字列は`のみを含む行で終わります。
しかし、PHPが扱うuuencodeのフォーマットは上記の一部のみを満たすもので、さらにmbstringとconvert_uuencode()とで微妙に異なります。
例えば、testという文字列をPHP8.2のmb_convert_encoding()とconvert_uuencode()でエンコードすると、以下の結果になります。
# mb_convert_encoding('test', 'uuencode')
begin 0644 filename
$=&5S=```
# convert_uuencode('test')
$=&5S=```
`
つまり、以下のようになっています。
| mbstring |
convert_uuencode()convert_uudecode()
|
|
|---|---|---|
| ヘッダー行 | ○ | × |
エンコード文字列末尾の`だけの行 |
× | ○ |
| フッター行 | × | × |
なぜこうなっているのかは謎ですが、変換を行うにはこの差異を吸収するように自前で実装する必要がありそうです。
ただ、最初や最後の行を付け足したり削除したりするだけなので、さほど難しくはないはずです。
mb_convert_encoding()によるuuencode変換
上でmb_convert_encoding('test', 'uuencode')の結果の例を載せましたが、3v4l の実行結果をよく見ると、8.0, 8.1ではbool(false)が出力されています。
実は、uuencodeフォーマットのエンコード処理は8.1まで実装されていなかったようなのです。
それが、8.2でuuencodeが非推奨になると同時に実装されました。
なぜこうなったのか、詳しい経緯は正直謎ですが、いずれにしてもわざわざ非推奨の変換を今から新しく使うことはないでしょう。
おわりに
PHP8.2で非推奨になったmbstringのエンコーディングと、その置き換え方法を紹介しました。
PHPバージョンアップの際の参考になれば幸いです。
ここまで書いてたらアドベントカレンダー担当日を数分過ぎてしまったのは内緒です。uuencodeがいろいろカオスなのが悪い!()