fread fails to return full string (but fgets works)
Data is php serialized string. EG 'a:6:{s:7:"success";b:1;s:5:"total";s:1:"8";s:4:"data";a:8:{i:0;a:17..." etc
Data is 7169 bytes long. Returned as "RESP Bulk String"."
$line = fread($this->getSocket(), $result + 2); // FAILS TO RETURN 7171 bytes
Ask to read 7171 but get back ony 2888 bytes. So php unserialize fails.
Verify no timeout or blocking:
017-12-17 17:10:37
'META'
array (
'stream_type' => 'tcp_socket/ssl',
'mode' => 'r+',
'unread_bytes' => 0,
'seekable' => false,
'timed_out' => false, OK
'blocked' => true, OK
'eof' => false,
)
// This works. I get the full string (7169 bytes)
$line = fgets($this->getSocket());
So now I think this is ok provided I only use it for serialized string (never contain CRLF).
But I dunno why this is necessary.
If value is JSON-encoded string, fails too (((
In case you need to deal with CRLF in the returned data, here's an alternative:
Change line 56 and 57 from
$line = fread($this->getSocket(), $result + 2);
$result = substr($line, 0, strlen($line) - 2);
To
$line="";
for($i=0;$i<1000 && $result > 0;$i++) {
$chunk = fread($this->getSocket(), $result+2);
$result -= strlen($chunk);
$line .= $chunk;
}
$result = substr($line, 0, strlen($line) - 2);
From the php documentation for read:
if the stream is read buffered and it does not represent a plain file, at most one read of up to a number of bytes equal to the chunk size (usually 8192) is made; depending on the previously buffered data, the size of the returned data may be larger than the chunk size.
To see how the patch is working:
class TinyRedisClient
{
/** @var string */
private $server;
/** @var resource */
private $socket;
public function __construct($server = 'localhost:6379')
{
$this->server = $server;
}
public function __call($method, array $args)
{
array_unshift($args, $method);
$cmd = '*' . count($args) . "\r\n";
foreach ($args as $item) {
$cmd .= '$' . strlen($item) . "\r\n" . $item . "\r\n";
}
fwrite($this->getSocket(), $cmd);
return $this->parseResponse();
}
private function getSocket()
{
return $this->socket
? $this->socket
: ($this->socket = stream_socket_client($this->server));
}
private function parseResponse()
{
$line = fgets($this->getSocket());
list($type, $result) = array($line[0], substr($line, 1, strlen($line) - 3));
if ($type == '-') { // error message
throw new Exception($result);
} elseif ($type == '$') { // bulk reply
if ($result == -1) {
$result = null;
} else {
$line="";
for($i=0;$i<1000 && $result > 0;$i++) {
$chunk = fread($this->getSocket(), $result+2);
$result -= strlen($chunk);
$line .= $chunk;
echo "Chunk $i is: $chunk \n";
echo "Have $result bytes left to fetch \n";
ob_flush();
}
$result = substr($line, 0, strlen($line) - 2);
}
} elseif ($type == '*') { // multi-bulk reply
$count = ( int ) $result;
for ($i = 0, $result = array(); $i < $count; $i++) {
$result[] = $this->parseResponse();
}
}
return $result;
}
}