您的编码数据格式有误。我建议您使用适当的编码例程将数据结构转换为编码格式,而不是通过连接手动构造字符串。这是一个处理编码和解码的类:
/**
* Encodes and decodes bencode formatted strings
*/
class BEncoder
{
/**
* Encode a value using the bencode format
*
* @param mixed $data The value to encode
*
* @return string The encoded value
*
* @throws \InvalidArgumentException When an unencodable value is encountered
*/
public function encode($data)
{
if (is_resource($data)) {
throw new \InvalidArgumentException('Resources cannot be bencoded');
}
// convert objects to arrays of public properties
// this makes it possible to sort dictionaries as required
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_string($data)) {
// byte sequence
$result = strlen($data) . ':' . $data;
} else if (is_int($data) || is_float($data) || is_bool($data)) {
// integer
$result = 'i' . round($data, 0) . 'e';
} else if (is_array($data)) {
if (array_values($data) === $data) {
// list
$result = 'l';
foreach ($data as $value) {
$result .= $this->encode($value);
}
} else if (is_array($data)) {
// dictionary
ksort($data);
$result = 'd';
foreach ($data as $key => $value) {
$result .= $this->encode((string) $key) . $this->encode($value);
}
}
$result .= 'e';
}
return $result;
}
/**
* Decode a value using the bencode format
*
* @param string $data The value to decode
*
* @return mixed The decoded value
*
* @throws \InvalidArgumentException When an undecodable value is encountered
*/
public function decode($data)
{
if (!is_string($data)) {
throw new \InvalidArgumentException('Data is not a valid bencoded string');
}
$data = trim($data);
try {
$result = $this->decodeComponent($data, $position);
if ($data !== '') {
throw new \InvalidArgumentException('Data found after end of value');
}
} catch (\InvalidArgumentException $e) {
$err = 'Syntax error at character ' . $position . ' "' . substr($data, 0, 7) . '..."): ' . $e->getMessage();
throw new \InvalidArgumentException($err);
}
return $result;
}
/**
* Move the pointer in the data currently being decoded
*
* @param string $data The data being decoded
* @param int $position The position pointer
* @param int $count The number of bytes to move the pointer
*/
private function movePointer(&$data, &$position, $count)
{
$data = (string) substr($data, $count);
$position += $count;
}
/**
* Recursively decode a structure from a data string
*
* @param string $data The data being decoded
* @param int $position The position pointer
*
* @return mixed The decoded value
*
* @throws \InvalidArgumentException When an undecodable value is encountered
*/
private function decodeComponent(&$data, &$position = 0)
{
switch ($data[0]) {
case 'i':
if (!preg_match('/^i(-?\d+)e/', $data, $matches)) {
throw new \InvalidArgumentException('Invalid integer');
}
$this->movePointer($data, $position, strlen($matches[0]));
return (int) $matches[1];
case 'l':
$this->movePointer($data, $position, 1);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of list');
}
$result = array();
while ($data[0] !== 'e') {
$value = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of list');
}
$result[] = $value;
}
$this->movePointer($data, $position, 1);
return $result;
case 'd':
$this->movePointer($data, $position, 1);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$result = array();
while ($data[0] !== 'e') {
$key = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$value = $this->decodeComponent($data, $position);
if ($data === '') {
throw new \InvalidArgumentException('Unexpected end of dictionary');
}
$result[$key] = $value;
}
$this->movePointer($data, $position, 1);
return $result;
default:
if (!preg_match('/^(\d+):/', $data, $matches)) {
throw new \InvalidArgumentException('Unknown data type');
}
$this->movePointer($data, $position, strlen($matches[0]));
if (strlen($data) < $matches[1]) {
$this->movePointer($data, $position, strlen($data));
throw new \InvalidArgumentException('Unexpected end of byte string');
}
$result = substr($data, 0, $matches[1]);
$this->movePointer($data, $position, $matches[1]);
return $result;
}
}
}
要将它与您的代码一起使用,您可以执行以下操作:
class scrapement extends Core
{
private $bencoder;
public function __construct($bencoder)
{
// inject an instance of the BEncoder class into this object
$this->bencoder = $bencoder;
}
public function renderPage()
{
if (!isset($_GET['info_hash']) || (strlen($_GET['info_hash']) != 20)) {
$this->error('Invalid hash');
}
$query = $this->query("
SELECT `info_hash`, `seeders`, `leechers`, `times_completed`
FROM `torrents`
WHERE `info_hash` = '" . $this->checkValues($_GET['info_hash'], 0) . "'
");
if (!mysql_num_rows($query)) {
$this->error('No torrent with that hash found');
}
$data = array(
'flags' => array(
'min_request_interval' => 1800
),
'files' => array()
);
while ($row = $this->fetch($query)) {
$hash = str_pad($row["info_hash"], 20);
$data['files'][$hash] = array(
'complete' => $row['seeders'],
'incomplete' => $row['leechers'],
'downloaded' => $row['times_completed']
);
}
$data = $this->bencoder->encode($data);
$this->getLog($data);
header("Content-Type: text/plain");
header("Pragma: no-cache");
echo $data;
}
private function error($err)
{
$data = array(
'flags' => array(
'min_request_interval' => 1800
),
'failure reason' => $err
);
$data = $this->bencoder->encode($data);
$this->getLog($data);
header('Content-Type: text/plain; charset=UTF-8');
header('Pragma: no-cache');
echo $data;
}
}