tags:

views:

191

answers:

2

I'm looking for a way to create valid UTF-16 JavaScript escape sequence characters (including surrogate pairs) from within PHP.

I'm using the code below to get the UTF-32 code points (from a UTF-8 encoded character). This works as JavaScript escape characters (eg. '\u00E1' for 'á') - until you get into the upper ranges where you get surrogate pairs (eg '' comes out as '\u1D715' but should be '\uD835\uDF15')...

function toOrdinal($chr)
{
    if (ord($chr{0}) >= 0 && ord($chr{0}) <= 127) 
    {
        return ord($chr{0});
    }
    elseif (ord($chr{0}) >= 192 && ord($chr{0}) <= 223)
    {
        return (ord($chr{0}) - 192) * 64 + (ord($chr{1}) - 128);
    }
    elseif (ord($chr{0}) >= 224 && ord($chr{0}) <= 239)
    {
        return (ord($chr{0}) - 224) * 4096 + (ord($chr{1}) - 128) * 64 + (ord($chr{2}) - 128);
    }
    elseif (ord($chr{0}) >= 240 && ord($chr{0}) <= 247)
    {
        return (ord($chr{0}) - 240) * 262144 + (ord($chr{1}) - 128) * 4096 + (ord($chr{2}) - 128) * 64 + (ord($chr{3}) - 128);
    }
    elseif (ord($chr{0}) >= 248 && ord($chr{0}) <= 251)
    {
        return (ord($chr{0}) - 248) * 16777216 + (ord($chr{1}) - 128) * 262144 + (ord($chr{2}) - 128) * 4096 + (ord($chr{3}) - 128) * 64 + (ord($chr{4}) - 128);
    }
    elseif (ord($chr{0}) >= 252 && ord($chr{0}) <= 253)
    {
         return (ord($chr{0}) - 252) * 1073741824 + (ord($chr{1}) - 128) * 16777216 + (ord($chr{2}) - 128) * 262144 + (ord($chr{3}) - 128) * 4096 + (ord($chr{4}) - 128) * 64 + (ord($chr{5}) - 128);
    }
}

How do I adapt this code to give me proper UTF-16 code points? Thanks!

+1  A: 

How about using iconv (or similarly mb_convert_encoding)?

eg. something like:

$utf16= iconv('UTF-8', 'UTF-16LE', $text);
$codeunits= array();
for ($i= 0; $i<strlen($utf16); $i+= 2) {
    $codeunits[]= ord($utf16{$i})+ord($utf16{$i+1})<<8;
}
bobince
That's brilliant. Many thanks. Based on your answer, here's what I'm using (and I'm happy to say it works): $utf16 = iconv(fxCHARSET, 'UTF-16BE', $text); for ($i= 0; $i < strlen($utf16); $i+= 2) { $output.= '\\u'.str_pad(dechex((ord($character{$i}) << 8) + ord($character{$i+1})), 4, '0', STR_PAD_LEFT); }
A: 

Here's the final code used (based on the answer from bobince):

$output = '';
$utf16 = iconv(fxCHARSET, 'UTF-16BE', $text);
for ($i= 0; $i < strlen($utf16); $i+= 2)
{
    $output.= '\\u'.str_pad(dechex((ord($character{$i}) << 8) + ord($character{$i+1})), 4, '0', STR_PAD_LEFT);
}