parse_url()の結果が壊れる

parse_url()で日本語URL(IRI状態)を渡すと壊れる。具体的には全角空白が別のUTF-8として無効な文字列になった。(PHP7.1)

調べてみるとphp_replace_controlchars_ex()でiscntrl()を呼び出し、コントロール文字を'_'に置換している。
日本語のWindows環境ではASCIIの\x00-\x1f, \x7fだけでなく\x80も対象になるため、UTF-8の全角空白(U+3000 = \xe3\x80\x80)が壊れたようだ。
バグ報告も昔からされているUNIX系環境のen_US.UTF8で普段作業するせいか知らなかった。回避するにはlocaleを無難なものに設定すればよい。
普通のウェブプログラムではlocale依存の処理はOSに任せずに文字コード系もmbstringのUTF-8一択なので、余計な作用が出ないほうが望ましい。
そこで冒頭で

<?php
setlocale(LC_ALL, 'C');

とした。限定的ならばLC_CTYPEのみを一時的に変更する。

<?php
$oldLocale = setlocale(LC_CTYPE, 0); // Japanese_Japan.932
setlocale(LC_CTYPE, 'C');
parse_url(...);
setlocale(LC_CTYPE, $oldLocale);

PHP8以降ではデフォルトでlocaleはCのため不要。