# $Id: README.UNICODE.ja 372 2008-01-01 00:06:30Z m-takagi $
# EN-Revision: 1.9
# (http://cvs.php.net/viewcvs.cgi/php-src/README.UNICODE?revision=1.9&view=markup)
対象読者
========
この README では、PHP 6 が Unicode 標準規格をどのようにしてサポートする
のかを説明します。PHP について詳しい知識を持ち、さらに Unicode の概念に
ついて基本的な知識がある人たちを読者として想定しています。技術的により詳
細な内容、たとえば PHP 6の設計思想や Unicode 対応の PHP 拡張モジュールを
作成する方法などについては README.UNICODE-UPGRADES を参照ください。
導入
====
ここ数年、PHP はどんどん成長を遂げてきました。一方その間、多言語環境や多
国籍環境のサポートは遅れをとってきました。PHP も、Unicode 化に向かう世間
の流れに逆らうことはできません。mbstring も最近改良され、マルチバイトデ
ータの扱いが簡単にできるようになりましたが、このモジュールは Unicode を
ネイティブでサポートしていません。
Unicode 標準規格を自分たちで完全に実装するのは非常に面倒なので、私たちは
より効率的な方法をとることにしました。つまり、十分にテストが行われていて
かつ Unicode の全機能を実装しているフリーなライブラリである ICU
(International Components for Unicode) を採用することにしたのです。
全般的な注意点
==============
International Components for Unicode
------------------------------------
ICU (International Components for Unicode) は広く使い込まれている C/C++
および Java のライブラリで、Unicode のサポートやソフトウェアの国際化
(internationalization)、世界化 (globalization) を行います。以下のような
機能を持っています。
- エンコーディングの変換
- 文字列照合 (Collation)
- Unicode テキストの処理
- その他もろもろ
PHP 6 をビルドすると、Unicode サポートは常に有効となります。開発時に指定
するであろう唯一の設定オプションは、ICU のヘッダやライブラリの場所を指定
する次のものです。
--with-icu-dir=
の部分に、ICU のヘッダやライブラリファイルの場所を指定します。これ
を省略した場合は、/usr および /usr/local から ICU のファイルを探します。
注意: ICU はまだ PHP 6 にはバンドルされていません。ICU は
http://icu.sourceforge.net からダウンロードしてください。PHP で必要とな
るのは、バージョン 3.4 以降の ICU です。
過去との互換性
--------------
Unicode サポートを組み込むにあたっては、過去との互換性の維持を最優先に考
えています。PHP は数多くのサイトで利用されているので、既存のデータ型や関
数がこれまでどおりに動作する必要があるからです。しかし、PHP のインターフ
ェイスについては過去との互換性を維持するものの、特定の操作の処理速度につ
いては何らかの影響がでるかもしれません (内部の実装が変わることによるもの
です)。
エンコーディング名
------------------
このドキュメントで取り上げているエンコーディング関連の設定については、
ICU がサポートするすべてのエンコーディング名を使用できます。使用できるエ
ンコーディングの一覧については、ICU のオンラインドキュメントをご覧くださ
い。
注意: 特に指定しない限り、このドキュメントで "Unicode" と言ったときには
UTF-16 文字エンコーディングを指しているものとします。
Unicode セマンティクスの切り替え
================================
Unicode を必要としないアプリケーションも多いので、PHP 6 では INI 設定で
サーバ全体での Unicode 有効/無効 を切り替えられるようになっています。
unicode.semantics = On/Off
このスイッチはデフォルトではオフになっています。アプリケーションで
Unicode のネイティブサポートが不要な場合はこのままにしておき、必要な場合
だけ Unicode 文字列を使うようにするといいでしょう。
しかし、アプリケーション側で完全に Unicode をサポートしているのなら、こ
のスイッチはオンにするべきです。オンにすると、以下のようなさまざまな
Unicode サポート機能が使えるようになります。
* 文字列リテラルがすべて Unicode に
* HTTP リクエストから受け取る変数がすべて Unicode に
* PHP の識別子での Unicode 文字の使用に対応
要するに、PHP が Unicode 環境になったということです。PHP の内部で扱う文
字列は Unicode となり、まわりからやってくる非 Unicode 文字列 (たとえば
HTTP 入出力やストリーム、ファイルシステムの操作など) はシステムが責任を
持って変換するようになります。unicode.semantics をオンにした場合は、バイ
ナリ文字列を扱う際にはそれを明示する必要があります。PHP がバイナリ文字列
の内容をよきにはからってくれるというわけではないので、アプリケーション側
でバイナリ文字列を適切に処理する必要があります。
逆に unicode.semantics をオフにすると、これまでのバージョンと同じような
挙動になります。文字列リテラルが Unicode になることもなく、ファイルの内
容も過去との互換性を考慮してバイナリ文字列となります。もちろん、プログラ
ム上で Unicode 文字列を作成することは可能です。Unicode 文字列に対応して
いる関数や演算子なら、作成した Unicode 文字列を透過的に使用することがで
きます。
代替エンコーディング
====================
代替エンコーディングは、INI 設定 unicode.*_encoding のデフォルト値を指定
するものです。unicode.*_encoding の指定を省略した場合に、PHP はこのエン
コーディングを使用します。代替エンコーディングも省略した場合は、UTF-8 を
使用します。
unicode.fallback_encoding = "iso-8859-1"
実行時エンコーディング
======================
実行時エンコーディングは、PHP 内部でバイナリ文字列を変換する際に使用する
エンコーディングを指定するものです。
unicode.runtime_encoding = "iso-8859-1"
この設定は、入出力関連の操作 (標準出力への書き込みやファイルシステムから
の読み込み、HTTP 入力変数のデコードなど) には何の影響も及ぼしません。
以下のキャストを使用して、文字列を明示的に変換することもできます。
* (binary) -- バイナリ文字列型にキャストします
* (unicode) -- Unicode 文字列型にキャストします
* (string) -- unicode.semantics がオンの場合は Unicode 文字列型、
それ以外の場合はバイナリ文字列型にキャストします
たとえば、unicode.runtime_encoding が iso-8859-1 で $uni が Unicode 文字
列の場合、
$str = (binary)$uni
とすると $str は ISO-8859-1 エンコーディングのバイナリ文字列となります。
連結や比較、あるいはパラメータとして渡す際などに暗黙の変換が行われます。
変換の精度を上げるため、PHP は文字列をいったん Unicode に変換してからこ
れらの操作を行います。たとえばバイナリ文字列 $str を Unicode リテラルと
連結する際には、PHP はまず $str を Unicode に変換します。その際には
unicode.runtime_encoding で指定したエンコーディングを使用します。
出力エンコーディング
====================
PHP は、'print' や 'echo' のように標準出力ストリームに書き出す際には出力
を自動的に変換します。
unicode.output_encoding = "utf-8"
しかし、バイナリ文字列については変換を行いません。ファイルや外部リソース
に書き出す際には、ストリームのエンコード機能を使用するか unicode 拡張モ
ジュールの関数による手動エンコードを行う必要があります。
既存の INI 設定項目 default_charset は「非推奨」となり、代わりに
unicode.output_setting を使うようになります。これまでの
default_charset では、Content-Type MIME ヘッダの中の文字セットの部分しか
指定していませんでした。今後は default_charset が有効になるのは
unicode.semantics がオフの場合のみとなり、出力ストリームの実際のコード変
換には何の影響も及ぼさなくなります。unicode.output_encoding を設定すると
Content-Type ヘッダの 'charset' 部を設定し、default_charset の設定を上書
きします。
HTTP 入力エンコーディング
=========================
HTTP 入力エンコーディングは、HTTP で受け取った変数 ($_GET や $_POST の中
身など) のエンコーディングを指定するものです。
この機能は現在開発中です。PHP 6 開発チームがどんな議論をしているのかにつ
いては、以下を参照ください。
http://marc.theaimsgroup.com/?t=116613047300005&r=1&w=2
ファイルシステムエンコーディング
================================
ファイルシステムエンコーディングは、ファイルシステム上のファイル名やディ
レクトリ名のエンコーディングを指定するものです。
unicode.filename_encoding = "utf-8"
ファイルシステムがらみの関数、たとえば opendir() などは、ファイル名をや
り取りする際にこの変換を行います。このエンコーディングは、ファイルシステ
ムで使用しているエンコーディングとそろえておく必要があります。
スクリプトエンコーディング
==========================
ICU がサポートするエンコーディングの中から好きなものを用いて PHP スクリ
プトを書くことができます。サイト全体のスクリプトエンコーディングを設定す
るには、次の INI 設定項目を使用します。
unicode.script_encoding = utf-8
システム全体の設定を変更することができない場合は、スクリプト内でプラグマ
を使用して INI 設定を上書きします。
このプラグマ設定は、スクリプトの先頭で行う必要があります。これは設定した
スクリプト内でのみ有効で、インクルードしたファイルには影響を及ぼしません。
INI ファイル
============
unicode.semantics がオンの場合は、INI ファイルのキーや値は UTF-8 でエン
コードされているものとみなされます。unicode.semantics がオフの場合は、
データはあるがままの状態で扱われます。これは PHP 5 のときと同じ挙動です。
INI ファイルのパース時には一切の検証を行いません。しかし、ini_*() 関数で
アクセスした場合は、無効な UTF-8 シーケンスを受け取った場合にそれを認識
します (エラーを捕捉します)。
ストリーム入出力
================
PHP はストリームベースの入出力システムを持っており、ファイルシステムへの
アクセスやネットワーク上のアクセス、データの圧縮などの操作を同じように行
うことができます。ストリームの向こう側のデータのエンコーディングが何であ
るかはわからないので、ここでもデータの変換を考慮する必要があります。
う~ん、もうちょっとはっきりに言うべきでしょうね。「デフォルトでは」実際
のところ、ストリームはバイナリモードでオープンされます。テキストモードで
オープンするには 't' フラグあるいは FILE_TEXT を指定する必要があるわけで
すが、このテキストモードの場合に変換が適用されることになります。また実の
ところ、テキストモードのストリームにおけるデフォルトのエンコーディングは
UTF-8 となります。
デフォルトでは、PHP はバイナリモードでストリームをオープンします。ファイ
ルをテキストモードでオープンするには、't' フラグ (あるいは FILE_TEXT
パラメータ -- 以下を参照ください) を使用する必要があります。テキストモー
ドのストリームにおけるデフォルトのエンコーディングは UTF-8 です。つまり、
仮に 'file.txt' が UTF-8 のテキストファイルだったとすると、次のコード
$fp = fopen('file.txt', 'rt');
$str = fread($fp, 100)
は Unicode の文字を 100 文字返します。一方、
$fp = fopen('file.txt', 'wt');
$fwrite($fp, $uni)
とすると UTF-8 のテキストファイルを書き込みます。
もし UTF-8 以外のエンコーディングのファイルを主に使用するのなら、デフォ
ルトのコンテキストエンコーディング設定を変更します。
stream_default_encoding('Shift-JIS');
$data = file_get_contents('file.txt', FILE_TEXT);
// $data に対して何らかの作業をして……
file_put_contents('file.txt', $data, FILE_TEXT);
file_get_contents() 関数および file_put_contents() 関数は、追加のパラメ
ータ FILE_TEXT を受け付けるようになっています。file_get_contents() に
FILE_TEXT を指定すると、PHP は Unicode 文字列を返します。FILE_TEXT を指
定しない場合はバイナリ文字列を返します (これは、画像ファイルなどのバイナ
リデータを扱う際に適しています)。Unicode 文字列を file_put_contents()
で書き出す際は FILE_TEXT パラメータを指定する必要があります。さもないと
PHP が警告を発生します。
複数のエンコーディングを使用する必要がある場合は、独自のコンテキストを
stream_context_create() で作成してそれを追加のパラメータで渡します。たと
えば次のようになります。
$ctx = stream_context_create(NULL, array('encoding' => 'big5'));
$data = file_get_contents('file.txt', FILE_TEXT, $ctx);
// $data に対して何らかの作業をして……
file_put_contents('file.txt', $data, FILE_TEXT, $ctx);
変換規則およびエラー処理
========================
PHP が行う文字列の変換には、明示的なもの (キャスト) と暗黙的なもの (連結
や比較、パラメータとして渡す作業など) があります。たとえば Unicode 文字
列とバイナリ文字列を連結する場合、PHP はまずバイナリ文字列を Unicode 文
字列に変換し、より高精度な処理を行えるようにします。
しかし中には、Unicode とレガシーエンコーディングとの間の変換ができない文
字も存在します。たとえば、文字列データが壊れていたり無効なバイト列が含ま
れていたりする場合などがありえます。このようなときには、次のような警告を
出してコンバータが停止します。
Warning: Could not convert binary string to Unicode string
(converter UTF-8 failed on bytes (0xE9) at offset 2)
逆に Unicode からレガシーエンコーディングへの変換の際に同様のエラーが発
生した場合は、コンバータが発する警告は次のようになります。
Warning: Could not convert Unicode string to binary string
(converter ISO-8859-1 failed on character {U+DC00} at offset 2)
この挙動を変更するには、以下にある "独自のエラーハンドラの作成" を参照く
ださい。
もうひとつありえるのが、Unicode 文字の中にレガシーエンコーディングでは表
せない文字が登場する可能性です。デフォルトでは、Unicode からレガシーエン
コーディングへの変換時にそのような文字があらわれた場合は、そのコードペー
ジにあわせた置換シーケンスで置き換えられます。この置換シーケンスは、たと
えば ISO-8859-1 の場合は 0x1A (Control-Z) となります。逆にレガシーエンコ
ーディングから Unicode に変換する際は、Unicode に同等な文字がないバイト
列については Unicode の置換文字 (U+FFFD) で置き換えます。
変換中にエラーが起こった場合の挙動は、次のように変更することもできます。
- 変換を中止し、空の文字列を返す
- 無効な文字を読み飛ばす
- 無効な文字を、独自に設定した置換文字に置き換える
- 無効な文字を、さまざまな方法でエスケープする
変換中にエラーが起こった場合の挙動を全体的に変更するには、次の関数を使用
します。
unicode_set_error_mode(int direction, int mode)
unicode_set_subst_char(unicode char)
direction には FROM_UNICODE (Unicode からの変換) あるいは TO_UNICODE
(Unicode への変換) のいずれか、そして mode には以下の定数のいずれかを指
定します。
U_CONV_ERROR_STOP (停止)
U_CONV_ERROR_SKIP (読み飛ばし)
U_CONV_ERROR_SUBST (置換)
U_CONV_ERROR_ESCAPE_UNICODE (Unicode 形式のエスケープ)
U_CONV_ERROR_ESCAPE_ICU (ICU 形式のエスケープ)
U_CONV_ERROR_ESCAPE_JAVA (Java 形式のエスケープ)
U_CONV_ERROR_ESCAPE_XML_DEC (XML 形式の10進エスケープ)
U_CONV_ERROR_ESCAPE_XML_HEX (XML 形式の16進エスケープ)
たとえば、実行時のエンコーディングが ISO-8859-1 である場合に、次の変換
$str = (binary)"< \u30AB >";
の結果は以下のようになります。
モード 結果
--------------------------------------
stop ""
skip "< >"
substitute "< ? >"
escape (Unicode) "< {U+30AB} >"
escape (ICU) "< %U30AB >"
escape (Java) "< \u30AB >"
escape (XML decimal) "< カ >"
escape (XML hex) "< カ >"
実行時のエンコーディングが UTF-8 のときに、次のような無効なシーケンス
$str = (unicode)b"< \xe9\xfe >";
を渡すと、以下のような結果になります。
モード 結果
--------------------------------------
stop ""
skip ""
substitute ""
escape (Unicode) "< %XE9%XFE >"
escape (ICU) "< %XE9%XFE >"
escape (Java) "< \xE9\xFE >"
escape (XML decimal) "< éþ >"
escape (XML hex) "< éþ >"
置換文字を指定できるのは FROM_UNICODE の場合のみで、またその文字が変換後
の文字セットに存在する必要があります。デフォルトの置換文字は (?) です。
注意: キャストを行うと、unicode.runtime_encoding を使用した変換を行いま
す。別のエンコーディングによる変換を行うには unicode_encode() および
unicode_decode() 関数を使用します。たとえば次のようになります。
$str = unicode_encode($uni, 'koi8-r', U_CONV_ERROR_SUBST);
この結果は、KOI8-R エンコードのバイナリ文字列となります。
独自のエラーハンドラの作成
--------------------------
変換時にエラーが発生した場合は、PHP はその内容を示す警告を発生させます。
このデフォルトの挙動を変更して、ユーザ定義のエラーハンドラを実行させるこ
ともできます。ちょうど、現在の PHP におけるカスタムエラーハンドラと同じ
ようなことです。独自の変換エラーハンドラを定義するには、次の関数を使用し
ます。
mixed unicode_set_error_handler(callback error_handler)
この関数は、関数のコール前に定義されていたエラーハンドラを返します。それ
までにエラーハンドラが定義されていなかった場合、あるいはハンドラを返す際
にエラーが発生した場合などは、この関数の返り値は NULL となります。
独自のエラーハンドラを設定すると、標準のエラーハンドラの処理は迂回される
ようになります。つまり、何らかのメッセージを出力したりログを記録したり、
あるいは例外を発生させたり die() させたりといった処理は、必要なら自前の
ハンドラで行う必要があるということです。しかし、自前のハンドラが FALSE
を返した場合は、その後で標準のハンドラを実行します。
error_handler に指定するユーザ関数は、次の 5 つのパラメータを受け付ける
必要があります。
mixed error_handler($direction, $encoding, $char_or_byte, $offset,
$message)
それぞれの意味は次のとおりです。
$direction - 変換の方向。FROM_UNICODE/TO_UNICODE のいずれか。
$encoding - 変換を試みるエンコーディングの名前。
$char_or_byte - エラーの原因となったのが Unicode 文字なのか、あるいは
バイトシーケンス (direction に依存します) なのか。
$offset - エラーが発生した文字 (あるいはバイトシーケンス) の、
文字列中における位置。
$message - エラーの原因を表す文字列。
注意: unicode_set_error_mode() で設定したエラーモードが substitute、
skip あるいは escape のいずれかである場合は、ハンドラはコールされません。
これらの操作ではエラーは発生しないからです。常に自前のハンドラを実行させ
たい場合は、エラーモードを U_CONV_ERROR_STOP にします。
Unicode 文字列型
================
Unicode 文字列型 (IS_UNICODE) に含まれる内容は、UTF-16 でエンコードされ
たテキストデータです。これは、Unicode セマンティクスをオンにした場合にお
ける PHP の主な文字列型となります。このスイッチをオフにした場合でも
Unicode 文字列は使用できますが、その場合は Unicode 型を返す関数をコール
してプログラム上で作成する必要があります。
バイナリ文字列型
================
バイナリ文字列型 (IS_STRING) の使用目的は、次のふたつです。まずは過去と
の互換性を保つこと。そして、非 Unicode 文字列やバイナリデータを扱うこと
です。Unicode セマンティクススイッチをオフにすると、PHP で扱うすべての文
字列がバイナリ文字列となります。これは今までのバージョンと同じ挙動です。
このスイッチをオンにした場合は、Unicode 以外のエンコーディングのテキスト
や (画像、PDF などの) バイナリデータを処理する際にバイナリ文字列を使用し
ます。
バイナリデータを標準出力に渡す際には、出力エンコーディングが何であるかに
かかわらず、あるがままの状態で出力します。
バイナリ文字列リテラルを指定する例については、"言語使用の変更点" を参照
ください。
言語仕様の変更点
================
Unicode スイッチをオンにすると、PHP の文字列リテラル (シングルクォートで
囲んだもの、ダブルクォートで囲んだもの、ヒアドキュメントなど) は Unicode
文字列 (IS_UNICODE 型) となります。これまで使用していたエスケープシーケ
ンスや変数展開といった機能はすべて同じように使えます。さらに、いくつか新
たなエスケープシーケンスが追加されています。
PHP は、次の手順でコンテンツを解釈します。
- エスケープされていないすべての文字は、現在のスクリプトエンコーディン
グにもとづいて、対応する Unicode コードポイントとして解釈されます。
たとえば ASCII の 'a' (0x61) => U+0061、Shift-JIS の (0x92 0x86)
=> U+4E2D のようになります。
- 既存の PHP エスケープシーケンスも Unicode コードポイント上で解釈され
ます。これには \xXX (16 進数) や \OOO (8 進数) も含まれ、たとえば
"\x20" => U+0020 のようになります。
- 新しく追加されたエスケープシーケンス \uXXXX および \UXXXXXX は、それ
ぞれ 16 進 4 桁あるいは 6 桁の Unicode コードポイント値を表します。
たとえば \u0221 => U+0221、\U010410 => U+10410 となります (二種類の
エスケープが存在する理由は、どちらかはっきりしない状態を避けるためで
す。こうしておかないと、たとえば \u020608 などの解釈で問題が発生しま
す。これは U+0206 の後に "08" を続けたものなのか、それとも U+020608
なのか?)
- 新しく追加されたエスケープシーケンスを使用すると、Unicode 名称で文字
を表すことができます。たとえば \C{THAI CHARACTER PHO SAMPHAO} => U+0E20
となります。
ダブルクォートで囲まれた文字列やヒアドキュメントでは、変数の展開が行われ
ます。しかし、文字列のパーサがリテラル部と変数部を区別する (つまり、
"abc $var def" が "abc" . $var . "def" であると解釈する) のはコンパイル
時の話です。つまり、Unicode サポートを考慮している限りは、PHP でリテラル
部をきちんと処理できるということです。
すべての文字列リテラルはデフォルトで Unicode となるので、PHP 6 ではバイ
ト指向の文字列 (バイナリ文字列) を作成するための新たな構文が追加されてい
ます。文字列リテラルの先頭に 'b' をつけると、バイナリ文字列を作成するよ
うになります。
$var = b'abc\001';
$var = b"abc\001";
$var = b<<[_