MediaWiki  REL1_23
MWMessagePack.php
Go to the documentation of this file.
00001 <?php
00035 class MWMessagePack {
00036 
00038     public static $bigendian = null;
00039 
00051     public static function pack( $value ) {
00052         if ( self::$bigendian === null ) {
00053             self::$bigendian = pack( 'S', 1 ) === pack( 'n', 1 );
00054         }
00055 
00056         switch ( gettype( $value ) ) {
00057         case 'NULL':
00058             return "\xC0";
00059 
00060         case 'boolean':
00061             return $value ? "\xC3" : "\xC2";
00062 
00063         case 'double':
00064         case 'float':
00065             return self::$bigendian
00066                 ? "\xCB" . pack( 'd', $value )
00067                 : "\xCB" . strrev( pack( 'd', $value ) );
00068 
00069         case 'string':
00070             $length = strlen( $value );
00071             if ( $length < 32 ) {
00072                 return pack( 'Ca*', 0xA0 | $length, $value );
00073             } elseif ( $length <= 0xFFFF ) {
00074                 return pack( 'Cna*', 0xDA, $length, $value );
00075             } elseif ( $length <= 0xFFFFFFFF ) {
00076                 return pack( 'CNa*', 0xDB, $length, $value );
00077             }
00078             throw new InvalidArgumentException( __METHOD__ . ": string too long (length: $length; max: 4294967295)" );
00079 
00080         case 'integer':
00081             if ( $value >= 0 ) {
00082                 if ( $value <= 0x7F ) {
00083                     // positive fixnum
00084                     return chr( $value );
00085                 }
00086                 if ( $value <= 0xFF ) {
00087                     // uint8
00088                     return pack( 'CC', 0xCC, $value );
00089                 }
00090                 if ( $value <= 0xFFFF ) {
00091                     // uint16
00092                     return pack( 'Cn', 0xCD, $value );
00093                 }
00094                 if ( $value <= 0xFFFFFFFF ) {
00095                     // uint32
00096                     return pack( 'CN', 0xCE, $value );
00097                 }
00098                 if ( $value <= 0xFFFFFFFFFFFFFFFF ) {
00099                     // uint64
00100                     $hi = ( $value & 0xFFFFFFFF00000000 ) >> 32;
00101                     $lo = $value & 0xFFFFFFFF;
00102                     return self::$bigendian
00103                         ? pack( 'CNN', 0xCF, $lo, $hi )
00104                         : pack( 'CNN', 0xCF, $hi, $lo );
00105                 }
00106             } else {
00107                 if ( $value >= -32 ) {
00108                     // negative fixnum
00109                     return pack( 'c', $value );
00110                 }
00111                 if ( $value >= -0x80 ) {
00112                     // int8
00113                     return pack( 'Cc', 0xD0, $value );
00114                 }
00115                 if ( $value >= -0x8000 ) {
00116                     // int16
00117                     $p = pack( 's', $value );
00118                     return self::$bigendian
00119                         ? pack( 'Ca2', 0xD1, $p )
00120                         : pack( 'Ca2', 0xD1, strrev( $p ) );
00121                 }
00122                 if ( $value >= -0x80000000 ) {
00123                     // int32
00124                     $p = pack( 'l', $value );
00125                     return self::$bigendian
00126                         ? pack( 'Ca4', 0xD2, $p )
00127                         : pack( 'Ca4', 0xD2, strrev( $p ) );
00128                 }
00129                 if ( $value >= -0x8000000000000000 ) {
00130                     // int64
00131                     // pack() does not support 64-bit ints either so pack into two 32-bits
00132                     $p1 = pack( 'l', $value & 0xFFFFFFFF );
00133                     $p2 = pack( 'l', ( $value >> 32 ) & 0xFFFFFFFF );
00134                     return self::$bigendian
00135                         ? pack( 'Ca4a4', 0xD3, $p1, $p2 )
00136                         : pack( 'Ca4a4', 0xD3, strrev( $p2 ), strrev( $p1 ) );
00137                 }
00138             }
00139             throw new InvalidArgumentException( __METHOD__ . ": invalid integer '$value'" );
00140 
00141         case 'array':
00142             $buffer = '';
00143             $length = count( $value );
00144             if ( $length > 0xFFFFFFFF ) {
00145                 throw new InvalidArgumentException( __METHOD__ . ": array too long (length: $length, max: 4294967295)" );
00146             }
00147 
00148             $index = 0;
00149             foreach ( $value as $k => $v ) {
00150                 if ( $index !== $k || $index === $length ) {
00151                     break;
00152                 } else {
00153                     $index++;
00154                 }
00155             }
00156             $associative = $index !== $length;
00157 
00158             if ( $associative ) {
00159                 if ( $length < 16 ) {
00160                     $buffer .= pack( 'C', 0x80 | $length );
00161                 } elseif ( $length <= 0xFFFF ) {
00162                     $buffer .= pack( 'Cn', 0xDE, $length );
00163                 } else {
00164                     $buffer .= pack( 'CN', 0xDF, $length );
00165                 }
00166                 foreach ( $value as $k => $v ) {
00167                     $buffer .= self::pack( $k );
00168                     $buffer .= self::pack( $v );
00169                 }
00170             } else {
00171                 if ( $length < 16 ) {
00172                     $buffer .= pack( 'C', 0x90 | $length );
00173                 } elseif ( $length <= 0xFFFF ) {
00174                     $buffer .= pack( 'Cn', 0xDC, $length );
00175                 } else {
00176                     $buffer .= pack( 'CN', 0xDD, $length );
00177                 }
00178                 foreach ( $value as $v ) {
00179                     $buffer .= self::pack( $v );
00180                 }
00181             }
00182             return $buffer;
00183 
00184         default:
00185             throw new InvalidArgumentException( __METHOD__ . ': unsupported type ' . gettype( $value ) );
00186         }
00187     }
00188 }