123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- <?php
- namespace PHPSocketIO\Engine;
- use \PHPSocketIO\Debug;
- class Parser
- {
- public function __construct()
- {
- Debug::debug('Engine/Parser __construct');
- }
- public static $packets=array(
- 'open'=> 0 // non-ws
- , 'close'=> 1 // non-ws
- , 'ping'=> 2
- , 'pong'=> 3
- , 'message'=> 4
- , 'upgrade'=> 5
- , 'noop'=> 6
- );
- public static $packetsList = array(
- 'open',
- 'close',
- 'ping',
- 'pong',
- 'message',
- 'upgrade',
- 'noop'
- );
- public static $err = array(
- 'type' => 'error',
- 'data' => 'parser error'
- );
- public static function encodePacket($packet)
- {
- $data = !isset($packet['data']) ? '' : $packet['data'];
- return self::$packets[$packet['type']].$data;
- }
-
- /**
- * Encodes a packet with binary data in a base64 string
- *
- * @param {Object} packet, has `type` and `data`
- * @return {String} base64 encoded message
- */
-
- public static function encodeBase64Packet($packet)
- {
- $data = isset($packet['data']) ? '' : $packet['data'];
- return $message = 'b' . self::$packets[$packet['type']] . base64_encode($packet['data']);
- }
-
- /**
- * Decodes a packet. Data also available as an ArrayBuffer if requested.
- *
- * @return {Object} with `type` and `data` (if any)
- * @api private
- */
-
- public static function decodePacket($data, $binaryType = null, $utf8decode = true)
- {
- // String data todo check if (typeof data == 'string' || data === undefined)
- if ($data[0] === 'b')
- {
- return self::decodeBase64Packet(substr($data, 1), $binaryType);
- }
-
- $type = $data[0];
- if (!isset(self::$packetsList[$type]))
- {
- return self::$err;
- }
-
- if (isset($data[1]))
- {
- return array('type'=> self::$packetsList[$type], 'data'=> substr($data, 1));
- }
- else
- {
- return array('type'=> self::$packetsList[$type]);
- }
- }
-
- /**
- * Decodes a packet encoded in a base64 string.
- *
- * @param {String} base64 encoded message
- * @return {Object} with `type` and `data` (if any)
- */
-
- public static function decodeBase64Packet($msg, $binaryType)
- {
- $type = self::$packetsList[$msg[0]];
- $data = base64_decode(substr($data, 1));
- return array('type'=> $type, 'data'=> $data);
- }
-
- /**
- * Encodes multiple messages (payload).
- *
- * <length>:data
- *
- * Example:
- *
- * 11:hello world2:hi
- *
- * If any contents are binary, they will be encoded as base64 strings. Base64
- * encoded strings are marked with a b before the length specifier
- *
- * @param {Array} packets
- * @api private
- */
-
- public static function encodePayload($packets, $supportsBinary = null)
- {
- if ($supportsBinary)
- {
- return self::encodePayloadAsBinary($packets);
- }
-
- if (!$packets)
- {
- return '0:';
- }
-
- $results = '';
- foreach($packets as $msg)
- {
- $results .= self::encodeOne($msg, $supportsBinary);
- }
- return $results;
- }
-
-
- public static function encodeOne($packet, $supportsBinary = null, $result = null)
- {
- $message = self::encodePacket($packet, $supportsBinary, true);
- return strlen($message) . ':' . $message;
- }
-
-
- /*
- * Decodes data when a payload is maybe expected. Possible binary contents are
- * decoded from their base64 representation
- *
- * @api public
- */
-
- public static function decodePayload($data, $binaryType = null)
- {
- if(!preg_match('/^\d+:\d/',$data))
- {
- return self::decodePayloadAsBinary($data, $binaryType);
- }
-
- if ($data === '')
- {
- // parser error - ignoring payload
- return self::$err;
- }
-
- $length = '';//, n, msg;
-
- for ($i = 0, $l = strlen($data); $i < $l; $i++)
- {
- $chr = $data[$i];
-
- if (':' != $chr)
- {
- $length .= $chr;
- }
- else
- {
- if ('' == $length || ($length != ($n = intval($length))))
- {
- // parser error - ignoring payload
- return self::$err;
- }
-
- $msg = substr($data, $i + 1/*, $n*/);
-
- /*if ($length != strlen($msg))
- {
- // parser error - ignoring payload
- return self::$err;
- }*/
-
- if (isset($msg[0]))
- {
- $packet = self::decodePacket($msg, $binaryType, true);
-
- if (self::$err['type'] == $packet['type'] && self::$err['data'] == $packet['data'])
- {
- // parser error in individual packet - ignoring payload
- return self::$err;
- }
-
- return $packet;
- }
-
- // advance cursor
- $i += $n;
- $length = '';
- }
- }
-
- if ($length !== '')
- {
- // parser error - ignoring payload
- echo new \Exception('parser error');
- return self::$err;
- }
- }
-
- /**
- * Encodes multiple messages (payload) as binary.
- *
- * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
- * 255><data>
- *
- * Example:
- * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
- *
- * @param {Array} packets
- * @return {Buffer} encoded payload
- * @api private
- */
-
- public static function encodePayloadAsBinary($packets)
- {
- $results = '';
- foreach($packets as $msg)
- {
- $results .= self::encodeOneAsBinary($msg);
- }
- return $results;
- }
-
- public static function encodeOneAsBinary($p)
- {
- // todo is string or arraybuf
- $packet = self::encodePacket($p, true, true);
- $encodingLength = ''.strlen($packet);
- $sizeBuffer = chr(0);
- for ($i = 0; $i < strlen($encodingLength); $i++)
- {
- $sizeBuffer .= chr($encodingLength[$i]);
- }
- $sizeBuffer .= chr(255);
- return $sizeBuffer.$packet;
- }
-
- /*
- * Decodes data when a payload is maybe expected. Strings are decoded by
- * interpreting each byte as a key code for entries marked to start with 0. See
- * description of encodePayloadAsBinary
- * @api public
- */
-
- public static function decodePayloadAsBinary($data, $binaryType = null)
- {
- $bufferTail = $data;
- $buffers = array();
-
- while (strlen($bufferTail) > 0)
- {
- $strLen = '';
- $isString = $bufferTail[0] == 0;
- $numberTooLong = false;
- for ($i = 1; ; $i++)
- {
- $tail = ord($bufferTail[$i]);
- if ($tail === 255) break;
- // 310 = char length of Number.MAX_VALUE
- if (strlen($strLen) > 310)
- {
- $numberTooLong = true;
- break;
- }
- $strLen .= $tail;
- }
- if($numberTooLong) return self::$err;
- $bufferTail = substr($bufferTail, strlen($strLen) + 1);
-
- $msgLength = intval($strLen, 10);
-
- $msg = substr($bufferTail, 1, $msgLength + 1);
- $buffers[] = $msg;
- $bufferTail = substr($bufferTail, $msgLength + 1);
- }
- $total = count($buffers);
- $packets = array();
- foreach($buffers as $i => $buffer)
- {
- $packets[] = self::decodePacket($buffer, $binaryType, true);
- }
- return $packets;
- }
- }
|