123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294 |
- <?php namespace Laravel;
- class Redis {
- /**
- * The address for the Redis host.
- *
- * @var string
- */
- protected $host;
- /**
- * The port on which Redis can be accessed on the host.
- *
- * @var int
- */
- protected $port;
- /**
- * The database number the connection selects on load.
- *
- * @var int
- */
- protected $database;
- /**
- * The connection to the Redis database.
- *
- * @var resource
- */
- protected $connection;
- /**
- * The active Redis database instances.
- *
- * @var array
- */
- protected static $databases = array();
- /**
- * Create a new Redis connection instance.
- *
- * @param string $host
- * @param string $port
- * @param int $database
- * @return void
- */
- public function __construct($host, $port, $database = 0)
- {
- $this->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.
- *
- * <code>
- * // Get the default Redis database instance
- * $redis = Redis::db();
- *
- * // Get a specified Redis database instance
- * $reids = Redis::db('redis_2');
- * </code>
- *
- * @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.
- *
- * <code>
- * // 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));
- * </code>
- *
- * @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:
- *
- * *<number of arguments> CR LF
- * $<number of bytes of argument 1> CR LF
- * <argument data> CR LF
- * ...
- * $<number of bytes of argument N> CR LF
- * <argument data> 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);
- }
- }
- }
|