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). * * :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>[...] * * 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; } }