$value) { $output .= self::encode((string)$key, $depth - 1); $output .= self::encode($value, $depth - 1); } } return $output . 'e'; case 'object': if($input instanceof IBencodeSerialisable) return self::encode($input->bencodeSerialise(), $depth - 1); $input = get_object_vars($input); $output = 'd'; foreach($input as $key => $value) { $output .= self::encode((string)$key, $depth - 1); $output .= self::encode($value, $depth - 1); } return $output . 'e'; default: return ''; } } public static function decode(Stream|string $input, int $depth = self::DEFAULT_DEPTH, bool $dictAsObject = false): mixed { if(!($input instanceof Stream)) { $input = TempFileStream::fromString($input); $input->seek(0); } if($depth < 1) throw new RuntimeException('Maximum depth reached, structure is too dense.'); $char = $input->readChar(); if($char === null) throw new RuntimeException('Unexpected end of stream in $input.'); switch($char) { case 'i': $number = ''; for(;;) { $char = $input->readChar(); if($char === null) throw new RuntimeException('Unexpected end of stream while parsing integer.'); if($char === 'e') break; if($char === '-' && $number !== '') throw new RuntimeException('Unexpected - (minus) while parsing integer.'); if($char === '0' && $number === '-') throw new RuntimeException('Negative integer zero.'); if($char === '0' && $number === '0') throw new RuntimeException('Integer double zero.'); if(!ctype_digit($char)) throw new RuntimeException('Unexpected character while parsing integer.'); $number .= $char; } return (int)$number; case 'l': $list = []; for(;;) { $char = $input->readChar(); if($char === null) throw new RuntimeException('Unexpected end of stream while parsing list.'); if($char === 'e') break; $input->seek(-1, Stream::CURRENT); $list[] = self::decode($input, $depth - 1, $dictAsObject); } return $list; case 'd': $dict = []; for(;;) { $char = $input->readChar(); if($char === null) throw new RuntimeException('Unexpected end of stream while parsing dictionary'); if($char === 'e') break; if(!ctype_digit($char)) throw new RuntimeException('Unexpected dictionary key type, expected a string.'); $input->seek(-1, Stream::CURRENT); $dict[self::decode($input, $depth - 1, $dictAsObject)] = self::decode($input, $depth - 1, $dictAsObject); } if($dictAsObject) $dict = (object)$dict; return $dict; default: if(!ctype_digit($char)) throw new RuntimeException('Unexpected character while parsing string.'); $length = $char; for(;;) { $char = $input->readChar(); if($char === null) throw new RuntimeException('Unexpected end of character while parsing string length.'); if($char === ':') break; if($char === '0' && $length === '0') throw new RuntimeException('Integer double zero while parsing string length.'); if(!ctype_digit($char)) throw new RuntimeException('Unexpected character while parsing string length.'); $length .= $char; } return $input->read((int)$length); } } }