host = $host;
$this->port = $port;
$this->database = $database;
}
/**
* Get a Redis database connection instance.
*
* The given name should correspond to a Redis database in the configuration file.
*
*
* // Get the default Redis database instance
* $redis = Redis::db();
*
* // Get a specified Redis database instance
* $reids = Redis::db('redis_2');
*
*
* @param string $name
* @return Redis
*/
public static function db($name = 'default')
{
if ( ! isset(static::$databases[$name]))
{
if (is_null($config = Config::get("database.redis.{$name}")))
{
throw new \Exception("Redis database [$name] is not defined.");
}
extract($config);
static::$databases[$name] = new static($host, $port, $database);
}
return static::$databases[$name];
}
/**
* Execute a command against the Redis database.
*
*
* // Execute the GET command for the "name" key
* $name = Redis::db()->run('get', array('name'));
*
* // Execute the LRANGE command for the "list" key
* $list = Redis::db()->run('lrange', array(0, 5));
*
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function run($method, $parameters)
{
fwrite($this->connect(), $this->command($method, (array) $parameters));
$response = trim(fgets($this->connection, 512));
return $this->parse($response);
}
/**
* Parse and return the response from the Redis database.
*
* @param string $response
* @return mixed
*/
protected function parse($response)
{
switch (substr($response, 0, 1))
{
case '-':
throw new \Exception('Redis error: '.substr(trim($response), 4));
case '+':
case ':':
return $this->inline($response);
case '$':
return $this->bulk($response);
case '*':
return $this->multibulk($response);
default:
throw new \Exception("Unknown Redis response: ".substr($response, 0, 1));
}
}
/**
* Establish the connection to the Redis database.
*
* @return resource
*/
protected function connect()
{
if ( ! is_null($this->connection)) return $this->connection;
$this->connection = @fsockopen($this->host, $this->port, $error, $message);
if ($this->connection === false)
{
throw new \Exception("Error making Redis connection: {$error} - {$message}");
}
$this->select($this->database);
return $this->connection;
}
/**
* Build the Redis command based from a given method and parameters.
*
* Redis protocol states that a command should conform to the following format:
*
* * CR LF
* $ CR LF
* CR LF
* ...
* $ CR LF
* CR LF
*
* More information regarding the Redis protocol: http://redis.io/topics/protocol
*
* @param string $method
* @param array $parameters
* @return string
*/
protected function command($method, $parameters)
{
$command = '*'.(count($parameters) + 1).CRLF;
$command .= '$'.strlen($method).CRLF;
$command .= strtoupper($method).CRLF;
foreach ($parameters as $parameter)
{
$command .= '$'.strlen($parameter).CRLF.$parameter.CRLF;
}
return $command;
}
/**
* Parse and handle an inline response from the Redis database.
*
* @param string $response
* @return string
*/
protected function inline($response)
{
return substr(trim($response), 1);
}
/**
* Parse and handle a bulk response from the Redis database.
*
* @param string $head
* @return string
*/
protected function bulk($head)
{
if ($head == '$-1') return;
list($read, $response, $size) = array(0, '', substr($head, 1));
if ($size > 0)
{
do
{
// Calculate and read the appropriate bytes off of the Redis response.
// We'll read off the response in 1024 byte chunks until the entire
// response has been read from the database.
$block = (($remaining = $size - $read) < 1024) ? $remaining : 1024;
$response .= fread($this->connection, $block);
$read += $block;
} while ($read < $size);
}
// The response ends with a trailing CRLF. So, we need to read that off
// of the end of the file stream to get it out of the way of the next
// command that is issued to the database.
fread($this->connection, 2);
return $response;
}
/**
* Parse and handle a multi-bulk reply from the Redis database.
*
* @param string $head
* @return array
*/
protected function multibulk($head)
{
if (($count = substr($head, 1)) == '-1') return;
$response = array();
// Iterate through each bulk response in the multi-bulk and parse it out
// using the "parse" method since a multi-bulk response is just a list
// of plain old Redis database responses.
for ($i = 0; $i < $count; $i++)
{
$response[] = $this->parse(trim(fgets($this->connection, 512)));
}
return $response;
}
/**
* Dynamically make calls to the Redis database.
*/
public function __call($method, $parameters)
{
return $this->run($method, $parameters);
}
/**
* Dynamically pass static method calls to the Redis instance.
*/
public static function __callStatic($method, $parameters)
{
return static::db()->run($method, $parameters);
}
/**
* Close the connection to the Redis database.
*
* @return void
*/
public function __destruct()
{
if ($this->connection)
{
fclose($this->connection);
}
}
}