private static function decElement($data, &$offset) { $sig = ord($data[$offset]); $offset++; $name = Util::parseCString($data, $offset); switch ($sig) { case self::ETYPE_ID: $binId = Util::unpack('a12id', $data, $offset, 12)['id']; $value = new \MongoId(str_pad(bin2hex($binId), 24, '0')); break; case self::ETYPE_STRING: case self::ETYPE_SYMBOL: $len = Util::unpack('Vlen', $data, $offset, 4)['len']; $value = substr($data, $offset, $len - 1); // subtract 1 for nul-terminator $offset += $len; break; case self::ETYPE_ARRAY: case self::ETYPE_DOCUMENT: $value = self::decDocument($data, $offset); break; case self::ETYPE_INT32: $value = Util::unpack('lint', $data, $offset, 4)['int']; break; case self::ETYPE_BOOL: $value = Util::unpack('C', $data, $offset, 1); if ($value[1]) { $value = true; } else { $value = false; } break; case self::ETYPE_UNDEF: case self::ETYPE_NULL: $value = null; break; case self::ETYPE_INT64: $vars = Util::unpack('V2i', $data, $offset, 8); $value = $vars['i1'] | $vars['i2'] << 32; break; case self::ETYPE_DOUBLE: $value = Util::unpack('ddouble', $data, $offset, 8)['double']; break; case self::ETYPE_CODE_W_S: $offset += 4; // skip whole element size int // skip whole element size int case self::ETYPE_CODE: $scope = []; $len = Util::unpack('Vlen', $data, $offset, 4)['len']; $code = substr($data, $offset, $len - 1); // subtract 1 for nul-terminator $offset += $len; if ($sig === self::ETYPE_CODE_W_S) { $scope = self::decDocument($data, $offset); } $value = new \MongoCode($code, $scope); break; case self::ETYPE_REGEX: $regex = Util::parseCString($data, $offset); $flags = Util::parseCString($data, $offset); $value = new \MongoRegex('/' . $regex . '/' . $flags); break; case self::ETYPE_DATE: $vars = Util::unpack('V2i', $data, $offset, 8); $ms = ($vars['i2'] << 32) + $vars['i1']; $value = \MongoDate::createFromMs($ms); break; case self::ETYPE_TIMESTAMP: $vars = Util::unpack('V2i', $data, $offset, 8); $value = new \MongoTimestamp($vars['i2'], $vars['i1']); break; case self::ETYPE_BINARY: $vars = Util::unpack('Vlen/Csubtype', $data, $offset, 5); $len = $vars['len']; $subtype = $vars['subtype']; if ($subtype == 2) { // binary subtype 2 special case $len2 = Util::unpack('Vlen', $data, $offset, 4)['len']; if ($len2 == $len - 4) { $len = $len2; } else { // something is not right, restore offset $offset -= 4; } } if ($len < 0) { throw new \RuntimeException(sprintf('invalid binary length for key "%s": %d', $name, $len), 22); } $bin = substr($data, $offset, $len); $value = new \MongoBinData($bin, $subtype); $offset += strlen($bin); break; case self::ETYPE_MAXKEY: $value = new \MongoMaxKey(); break; case self::ETYPE_MINKEY: $value = new \MongoMinKey(); break; default: throw new \RuntimeException('Invalid signature: 0x' . dechex($sig)); } return [$name, $value]; }