unichr() があるなら uniord() も欲しいよね、ってことで書いてみました。
unichr.xml の extension 要素に追加する関数定義:
<function name="uniord"> <proto>int uniord(string str)</proto> <description><![CDATA[ Get the code point which corresponds to the given UTF-8 encoded string. ]]></description> <code><![CDATA[ if (str_len == 0) { RETURN_FALSE; } long cp = 0; if ((str[0] & 0xE0) == 0xE0) { if (str_len < 3) { RETURN_FALSE; } cp = ((long)(str[0] & 0x1F)) << 12; cp |= ((long)(str[1] & 0x7F)) << 6; cp |= (long)(str[2] & 0x7F); } else if ((str[0] & 0xC0) == 0xC0) { if (str_len < 2) { RETURN_FALSE; } cp = ((long)(str[0] & 0x3F)) << 6; cp |= (long)(str[1] & 0x7F); } else { if (str[0] & 0x80) { RETURN_FALSE; } cp = (long)str[0]; } RETURN_LONG(cp); ]]></code> </function>
unichr() と同じく UCS-2 のみ対応で、また最低限の長さと第 1 バイトのチェックさえ通れば UTF-8 として不正なシーケンスでもエラーとならない問題があります。
テスト用コード:
<?php dl('unichr.so'); for ($i = 0; $i < 65536; $i++) { if ($i != uniord(unichr($i))) { $str = unichr($i); $fmt = '0x%02X'; for ($j = 1; $j < strlen($str); $j++) { $fmt .= ' 0x%02X'; } $dump = vsprintf($fmt, unpack('C*', $str)); fprintf(STDERR, "failed at U+%04X (%s).?n", $i, $dump); exit(1); } } fwrite(STDERR, "success.?n"); exit(0); ?>
余談になりますが unicode 機能を有効にした PHP6 の chr() / ord() では内部で ICU の関数を呼び出して Unicode 文字とコードポイントの変換ができるようです。(ext/standard/string.c)