典型的なプロセスは、ブラウザまたはクライアントURLがパラメーターをエンコードして送信することです(たとえば、 ?name =%e4%f0%e2%e5%f2 )。正しいUTF-8テキストに復元するには、通常、2つのステップが必要です。
URLエンコード( rawurldecode / urldecode )をデコードして、元のバイトシーケンスを取得します。 php.net +1
このバイトシーケンスを正しいシングルバイトエンコード(例:Windows-1251、KOI8-R、CP866など)からUTF-8に変換します。一般的なキリル語のエンコーディングの場合、 convert_cyr_stringは、サーバーPHPバージョンでサポートされている場合に文字セット変換を完了できます。 php.net
注: convert_cyr_stringは、php 7.4以降に非推奨され、php 8.0で削除されます。 MB_CONVERT_ENCODING / ICONVまたはサードパーティのUTF-8ライブラリは、最初に新しい環境で使用する必要があります。互換性と代替ソリューションを以下に示します。 php.net
convert_cyr_string(string $ str、string $ from、string $ to)を使用して、共通の識別は次のとおりです。
K - Koi8-r
W - Windows-1251
I - ISO-8859-5
A / D - X-CP866(DOS CP866)
M - X-MAC-Cyrillic。 php.net
実用的なPHP関数は次のとおりです。URLエンコードされている文字列を受信し、何らかのキリルエンコード文字列(クエリ文字列またはパスセグメントから)を使用し、UTF-8に変換します。注:使用する前に、PHPバージョンが引き続きconvert_cyr_string (php≤7.3)をサポートしていることを確認してください。実行中の環境がPHP 8+の場合は、次のセクションにスキップして、代替案を確認してください。
<span><span><span class="hljs-meta"><?php</span></span><span>
<span class="hljs-comment">/**
* 意思 URL パラメーター(単一バイトのキリル語のためにエンコードされ、パーセントの記号で逃げる可能性があります)に標準化されています UTF-8 弦。
*
* $rawUrlPart: オリジナル URL 一部(例えば $_GET['name'],またはから PATH_INFO/ルートで得られたフラグメント)
* $sourceCode: ソースコード識別,使用 convert_cyr_string 単一の文字コード('w','k','i','a','d','m')
*
* 戻る UTF-8 弦(若无法转换则戻るオリジナル经过 rawurldecode 的弦)
*/</span>
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">normalize_cyrillic_url_param</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$rawUrlPart</span></span><span>, </span><span><span class="hljs-keyword">string</span></span><span> </span><span><span class="hljs-variable">$sourceCode</span></span><span> = </span><span><span class="hljs-string">'w'</span></span><span>): </span><span><span class="hljs-title">string</span></span><span> {
</span><span><span class="hljs-comment">// 先把百分号转义还原のためにオリジナル字节</span></span><span>
</span><span><span class="hljs-variable">$decoded</span></span><span> = </span><span><span class="hljs-title function_ invoke__">rawurldecode</span></span><span>(</span><span><span class="hljs-variable">$rawUrlPart</span></span><span>); </span><span><span class="hljs-comment">// 予約済みバイト,しないでください + 宇宙に向かいます(に適用されます path segment);から query そして、あります +,利用可能 urldecode()</span></span><span>
</span><span><span class="hljs-comment">// システムがある場合 convert_cyr_string(知らせ:存在する PHP 8+ 削除)</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">function_exists</span></span><span>(</span><span><span class="hljs-string">'convert_cyr_string'</span></span><span>)) {
</span><span><span class="hljs-comment">// 最初に単一のバイトをエンコードします(sourceCode)に変換します windows-1251('w'),</span></span><span>
</span><span><span class="hljs-comment">// それから windows-1251 に頼ります UTF-8(使用 mb_convert_encoding)</span></span><span>
</span><span><span class="hljs-variable">$asWin1251</span></span><span> = </span><span><span class="hljs-title function_ invoke__">convert_cyr_string</span></span><span>(</span><span><span class="hljs-variable">$decoded</span></span><span>, </span><span><span class="hljs-variable">$sourceCode</span></span><span>, </span><span><span class="hljs-string">'w'</span></span><span>);
</span><span><span class="hljs-comment">// 意思 windows-1251 二进制字节に頼ります UTF-8 弦</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">function_exists</span></span><span>(</span><span><span class="hljs-string">'mb_convert_encoding'</span></span><span>)) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$asWin1251</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>, </span><span><span class="hljs-string">'Windows-1251'</span></span><span>);
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-comment">// バックアップとして,試す iconv(のように果利用可能)</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">function_exists</span></span><span>(</span><span><span class="hljs-string">'iconv'</span></span><span>)) {
</span><span><span class="hljs-variable">$utf8</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">iconv</span></span><span>(</span><span><span class="hljs-string">'CP1251'</span></span><span>, </span><span><span class="hljs-string">'UTF-8//IGNORE'</span></span><span>, </span><span><span class="hljs-variable">$asWin1251</span></span><span>);
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$utf8</span></span><span> !== </span><span><span class="hljs-literal">false</span></span><span> ? </span><span><span class="hljs-variable">$utf8</span></span><span> : </span><span><span class="hljs-variable">$asWin1251</span></span><span>;
}
</span><span><span class="hljs-comment">// 都不利用可能时,戻るオリジナル解码弦</span></span><span>
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$asWin1251</span></span><span>;
}
}
</span><span><span class="hljs-comment">// そうでない場合 convert_cyr_string(のように PHP 8+),直接戻るオリジナル解码弦,発信者に代替案を使用させます</span></span><span>
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$decoded</span></span><span>;
}
</span></span>使用例:
<span><span><span class="hljs-comment">// 仮定 URL のために: /?name=%D0%9C%D0%B8%D1%80</span></span><span>
</span><span><span class="hljs-variable">$raw</span></span><span> = </span><span><span class="hljs-variable">$_GET</span></span><span>[</span><span><span class="hljs-string">'name'</span></span><span>] ?? </span><span><span class="hljs-string">''</span></span><span>;
</span><span><span class="hljs-variable">$name</span></span><span> = </span><span><span class="hljs-title function_ invoke__">normalize_cyrillic_url_param</span></span><span>(</span><span><span class="hljs-variable">$raw</span></span><span>, </span><span><span class="hljs-string">'w'</span></span><span>); </span><span><span class="hljs-comment">// 仮定客户端以 Windows-1251 送信</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-variable">$name</span></span><span>; </span><span><span class="hljs-comment">// 出力は正しいです UTF-8 名前</span></span><span>
</span></span>%エスケープのデコードは、パラメーターソースrawurldecode() (パスセグメントに適しています)またはurldecode() ( + +がクエリ文字列のスペースを表す場合)に基づいて選択する必要があります。 2つの違いと推奨使用については、公式ドキュメントを参照してください。 php.net guides.codepath.com
新しいプロジェクトまたはPHP 8+環境の場合、 MB_DETECT_ENCODING + MB_CONVERT_ENCODING / ICONVを使用するか、クライアントがUTF-8を均一に使用できるようにすることをお勧めします(ベストプラクティス)。例:
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">normalize_cyrillic_url_param_modern</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$rawUrlPart</span></span><span>, </span><span><span class="hljs-keyword">array</span></span><span> </span><span><span class="hljs-variable">$tryEncodings</span></span><span> = [</span><span><span class="hljs-string">'Windows-1251'</span></span><span>,</span><span><span class="hljs-string">'KOI8-R'</span></span><span>,</span><span><span class="hljs-string">'CP866'</span></span><span>]) : </span><span><span class="hljs-title">string</span></span><span> {
</span><span><span class="hljs-variable">$decoded</span></span><span> = </span><span><span class="hljs-title function_ invoke__">rawurldecode</span></span><span>(</span><span><span class="hljs-variable">$rawUrlPart</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">function_exists</span></span><span>(</span><span><span class="hljs-string">'mb_detect_encoding'</span></span><span>) && </span><span><span class="hljs-title function_ invoke__">function_exists</span></span><span>(</span><span><span class="hljs-string">'mb_convert_encoding'</span></span><span>)) {
</span><span><span class="hljs-comment">// 試す检测并转换到 UTF-8</span></span><span>
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$tryEncodings</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$enc</span></span><span>) {
</span><span><span class="hljs-comment">// 检测字节串是否のために此编码(テストは信頼できない場合があります,故采用試す转换后判断)</span></span><span>
</span><span><span class="hljs-variable">$maybe</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$decoded</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>, </span><span><span class="hljs-variable">$enc</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$maybe</span></span><span> !== </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-comment">// 簡単な検証:変換後の元のバイトの長さに似ていますか?(いいえ 100% 信頼性のある,しかし、実用的です)</span></span><span>
</span><span><span class="hljs-variable">$back</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$maybe</span></span><span>, </span><span><span class="hljs-variable">$enc</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$back</span></span><span> !== </span><span><span class="hljs-literal">false</span></span><span> && </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$back</span></span><span>) >= </span><span><span class="hljs-title function_ invoke__">strlen</span></span><span>(</span><span><span class="hljs-variable">$decoded</span></span><span>) - </span><span><span class="hljs-number">2</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$maybe</span></span><span>;
}
}
}
}
</span><span><span class="hljs-comment">// 最后退回オリジナル解码后的弦(おそらくすでに UTF-8)</span></span><span>
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$decoded</span></span><span>;
}
</span></span>さらに、成熟したサードパーティライブラリ( Voku/Portable-UTF8など)を使用して、特に高い堅牢性を必要とする生産システムでは、複雑なコーディング/正規化の問題に対処することもできます。 github
優先順位:最良の解決策は、すべてのクライアントがUTF-8を均一に使用できるようにすることです(HTML <Meta charset = "utf-8"> 、HTTPヘッダー、APIドキュメントで説明)。これは、基本的に文字化けのコードを避けるためのベストプラクティスです。
機能選択:URLエンコードを受信するとき、クエリパーツ( ?a = b + c )の場合、 urldecode()は+ +を空間に変えます。パスセグメントの場合、 rawurldecode()が推奨されます。 php.net +1
サーバー側の変換:履歴データまたはサードパーティシステムのみを処理できる場合、バイトシーケンスは上記の変換チェーンを使用してUTF-8に変換されます( RawurldeCode → Convert_Cyr_String (利用可能な場合)またはMB_CONVERT_ENCODING / ICONV )。 php.net +1
検出とフォールバック:自動検出エンコーディングは100%正確ではありません。重要なシナリオ(検出後の一貫性のチェックなど)に「信頼性」判断を追加し、手動介入の障害を記録したり、特定のルールを追加したりすることをお勧めします。
非推奨注: convert_cyr_stringは、php 7.4で非推奨としてマークされ、php 8.0で削除されています。コードが最新のPHP環境で長い間実行する必要がある場合は、互換性のある代替( MB_CONVERT_ENCODING / ICONV /サードパーティライブラリ)を実装してください。 php.net
シナリオA:レガシークライアントはKOI8-Rでパラメーター(クエリ)を送信し、サーバーはUTF-8を取得したいと考えています。
<span><span><span class="hljs-variable">$raw</span></span><span> = </span><span><span class="hljs-variable">$_GET</span></span><span>[</span><span><span class="hljs-string">'q'</span></span><span>]; </span><span><span class="hljs-comment">// オリジナル %xx 弦</span></span><span>
</span><span><span class="hljs-variable">$bytes</span></span><span> = </span><span><span class="hljs-title function_ invoke__">rawurldecode</span></span><span>(</span><span><span class="hljs-variable">$raw</span></span><span>); </span><span><span class="hljs-comment">// バイナリバイトを取得します</span></span><span>
</span><span><span class="hljs-comment">// 若利用可能 convert_cyr_string:</span></span><span>
</span><span><span class="hljs-variable">$win</span></span><span> = </span><span><span class="hljs-title function_ invoke__">convert_cyr_string</span></span><span>(</span><span><span class="hljs-variable">$bytes</span></span><span>, </span><span><span class="hljs-string">'k'</span></span><span>, </span><span><span class="hljs-string">'w'</span></span><span>); </span><span><span class="hljs-comment">// koi8-r -> windows-1251</span></span><span>
</span><span><span class="hljs-variable">$utf8</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$win</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>, </span><span><span class="hljs-string">'Windows-1251'</span></span><span>);
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-variable">$utf8</span></span><span>;
</span></span>シナリオB:PHP 8+環境、最新の方法を使用して自動変換を試してください。
<span><span><span class="hljs-variable">$raw</span></span><span> = </span><span><span class="hljs-variable">$_GET</span></span><span>[</span><span><span class="hljs-string">'q'</span></span><span>];
</span><span><span class="hljs-variable">$bytes</span></span><span> = </span><span><span class="hljs-title function_ invoke__">rawurldecode</span></span><span>(</span><span><span class="hljs-variable">$raw</span></span><span>);
</span><span><span class="hljs-variable">$utf8</span></span><span> = </span><span><span class="hljs-title function_ invoke__">normalize_cyrillic_url_param_modern</span></span><span>(</span><span><span class="hljs-variable">$raw</span></span><span>, [</span><span><span class="hljs-string">'Windows-1251'</span></span><span>,</span><span><span class="hljs-string">'KOI8-R'</span></span><span>,</span><span><span class="hljs-string">'CP866'</span></span><span>]);
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-variable">$utf8</span></span><span>;
</span></span>convert_cyr_stringは、かつてキリル語のシングルバイトエンコーディング間の交換を処理する便利な関数であり、サポートされているコードにはk、w、i、a、d、mが含まれます。ただし、この関数はPHP 7.4で非推奨され、PHP 8.0で削除されます。新しいプロジェクトは、代わりにMB_CONVERT_ENCODING / ICONVまたはサードパーティライブラリを使用することをお勧めします。 php.net
Carlebled URLパラメーターの問題に直面して、キーは次のとおりです。最初にパーセンテージサイン( rawurldecode / urldecode )を正しくデコードし、実際のソースエンコーディングに基づいてバイトシーケンスをUTF-8に変換します。パスとクエリのデコード関数を選択するときは、2つの間のハンドリングスペース( + )の違いに注意してください。 php.net +1
最も安全な長期戦略は、UTF-8を統一された方法で使用し、インターフェイスドキュメントとクライアントの実装のコーディング仕様に明確に同意することです。履歴またはサードパーティのデータが必要な場合、上記の変換チェーンが採用され、堅牢性を確保するために検出およびフォールバックメカニズムが追加されます。 github php.net
関連タグ:
URL