Parser.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. namespace PHPSocketIO\Engine;
  3. use \PHPSocketIO\Debug;
  4. class Parser
  5. {
  6. public function __construct()
  7. {
  8. Debug::debug('Engine/Parser __construct');
  9. }
  10. public static $packets=array(
  11. 'open'=> 0 // non-ws
  12. , 'close'=> 1 // non-ws
  13. , 'ping'=> 2
  14. , 'pong'=> 3
  15. , 'message'=> 4
  16. , 'upgrade'=> 5
  17. , 'noop'=> 6
  18. );
  19. public static $packetsList = array(
  20. 'open',
  21. 'close',
  22. 'ping',
  23. 'pong',
  24. 'message',
  25. 'upgrade',
  26. 'noop'
  27. );
  28. public static $err = array(
  29. 'type' => 'error',
  30. 'data' => 'parser error'
  31. );
  32. public static function encodePacket($packet)
  33. {
  34. $data = !isset($packet['data']) ? '' : $packet['data'];
  35. return self::$packets[$packet['type']].$data;
  36. }
  37. /**
  38. * Encodes a packet with binary data in a base64 string
  39. *
  40. * @param {Object} packet, has `type` and `data`
  41. * @return {String} base64 encoded message
  42. */
  43. public static function encodeBase64Packet($packet)
  44. {
  45. $data = isset($packet['data']) ? '' : $packet['data'];
  46. return $message = 'b' . self::$packets[$packet['type']] . base64_encode($packet['data']);
  47. }
  48. /**
  49. * Decodes a packet. Data also available as an ArrayBuffer if requested.
  50. *
  51. * @return {Object} with `type` and `data` (if any)
  52. * @api private
  53. */
  54. public static function decodePacket($data, $binaryType = null, $utf8decode = true)
  55. {
  56. // String data todo check if (typeof data == 'string' || data === undefined)
  57. if ($data[0] === 'b')
  58. {
  59. return self::decodeBase64Packet(substr($data, 1), $binaryType);
  60. }
  61. $type = $data[0];
  62. if (!isset(self::$packetsList[$type]))
  63. {
  64. return self::$err;
  65. }
  66. if (isset($data[1]))
  67. {
  68. return array('type'=> self::$packetsList[$type], 'data'=> substr($data, 1));
  69. }
  70. else
  71. {
  72. return array('type'=> self::$packetsList[$type]);
  73. }
  74. }
  75. /**
  76. * Decodes a packet encoded in a base64 string.
  77. *
  78. * @param {String} base64 encoded message
  79. * @return {Object} with `type` and `data` (if any)
  80. */
  81. public static function decodeBase64Packet($msg, $binaryType)
  82. {
  83. $type = self::$packetsList[$msg[0]];
  84. $data = base64_decode(substr($data, 1));
  85. return array('type'=> $type, 'data'=> $data);
  86. }
  87. /**
  88. * Encodes multiple messages (payload).
  89. *
  90. * <length>:data
  91. *
  92. * Example:
  93. *
  94. * 11:hello world2:hi
  95. *
  96. * If any contents are binary, they will be encoded as base64 strings. Base64
  97. * encoded strings are marked with a b before the length specifier
  98. *
  99. * @param {Array} packets
  100. * @api private
  101. */
  102. public static function encodePayload($packets, $supportsBinary = null)
  103. {
  104. if ($supportsBinary)
  105. {
  106. return self::encodePayloadAsBinary($packets);
  107. }
  108. if (!$packets)
  109. {
  110. return '0:';
  111. }
  112. $results = '';
  113. foreach($packets as $msg)
  114. {
  115. $results .= self::encodeOne($msg, $supportsBinary);
  116. }
  117. return $results;
  118. }
  119. public static function encodeOne($packet, $supportsBinary = null, $result = null)
  120. {
  121. $message = self::encodePacket($packet, $supportsBinary, true);
  122. return strlen($message) . ':' . $message;
  123. }
  124. /*
  125. * Decodes data when a payload is maybe expected. Possible binary contents are
  126. * decoded from their base64 representation
  127. *
  128. * @api public
  129. */
  130. public static function decodePayload($data, $binaryType = null)
  131. {
  132. if(!preg_match('/^\d+:\d/',$data))
  133. {
  134. return self::decodePayloadAsBinary($data, $binaryType);
  135. }
  136. if ($data === '')
  137. {
  138. // parser error - ignoring payload
  139. return self::$err;
  140. }
  141. $length = '';//, n, msg;
  142. for ($i = 0, $l = strlen($data); $i < $l; $i++)
  143. {
  144. $chr = $data[$i];
  145. if (':' != $chr)
  146. {
  147. $length .= $chr;
  148. }
  149. else
  150. {
  151. if ('' == $length || ($length != ($n = intval($length))))
  152. {
  153. // parser error - ignoring payload
  154. return self::$err;
  155. }
  156. $msg = substr($data, $i + 1/*, $n*/);
  157. /*if ($length != strlen($msg))
  158. {
  159. // parser error - ignoring payload
  160. return self::$err;
  161. }*/
  162. if (isset($msg[0]))
  163. {
  164. $packet = self::decodePacket($msg, $binaryType, true);
  165. if (self::$err['type'] == $packet['type'] && self::$err['data'] == $packet['data'])
  166. {
  167. // parser error in individual packet - ignoring payload
  168. return self::$err;
  169. }
  170. return $packet;
  171. }
  172. // advance cursor
  173. $i += $n;
  174. $length = '';
  175. }
  176. }
  177. if ($length !== '')
  178. {
  179. // parser error - ignoring payload
  180. echo new \Exception('parser error');
  181. return self::$err;
  182. }
  183. }
  184. /**
  185. * Encodes multiple messages (payload) as binary.
  186. *
  187. * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
  188. * 255><data>
  189. *
  190. * Example:
  191. * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
  192. *
  193. * @param {Array} packets
  194. * @return {Buffer} encoded payload
  195. * @api private
  196. */
  197. public static function encodePayloadAsBinary($packets)
  198. {
  199. $results = '';
  200. foreach($packets as $msg)
  201. {
  202. $results .= self::encodeOneAsBinary($msg);
  203. }
  204. return $results;
  205. }
  206. public static function encodeOneAsBinary($p)
  207. {
  208. // todo is string or arraybuf
  209. $packet = self::encodePacket($p, true, true);
  210. $encodingLength = ''.strlen($packet);
  211. $sizeBuffer = chr(0);
  212. for ($i = 0; $i < strlen($encodingLength); $i++)
  213. {
  214. $sizeBuffer .= chr($encodingLength[$i]);
  215. }
  216. $sizeBuffer .= chr(255);
  217. return $sizeBuffer.$packet;
  218. }
  219. /*
  220. * Decodes data when a payload is maybe expected. Strings are decoded by
  221. * interpreting each byte as a key code for entries marked to start with 0. See
  222. * description of encodePayloadAsBinary
  223. * @api public
  224. */
  225. public static function decodePayloadAsBinary($data, $binaryType = null)
  226. {
  227. $bufferTail = $data;
  228. $buffers = array();
  229. while (strlen($bufferTail) > 0)
  230. {
  231. $strLen = '';
  232. $isString = $bufferTail[0] == 0;
  233. $numberTooLong = false;
  234. for ($i = 1; ; $i++)
  235. {
  236. $tail = ord($bufferTail[$i]);
  237. if ($tail === 255) break;
  238. // 310 = char length of Number.MAX_VALUE
  239. if (strlen($strLen) > 310)
  240. {
  241. $numberTooLong = true;
  242. break;
  243. }
  244. $strLen .= $tail;
  245. }
  246. if($numberTooLong) return self::$err;
  247. $bufferTail = substr($bufferTail, strlen($strLen) + 1);
  248. $msgLength = intval($strLen, 10);
  249. $msg = substr($bufferTail, 1, $msgLength + 1);
  250. $buffers[] = $msg;
  251. $bufferTail = substr($bufferTail, $msgLength + 1);
  252. }
  253. $total = count($buffers);
  254. $packets = array();
  255. foreach($buffers as $i => $buffer)
  256. {
  257. $packets[] = self::decodePacket($buffer, $binaryType, true);
  258. }
  259. return $packets;
  260. }
  261. }