redis.php 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php namespace Laravel;
  2. class Redis {
  3. /**
  4. * The address for the Redis host.
  5. *
  6. * @var string
  7. */
  8. protected $host;
  9. /**
  10. * The port on which Redis can be accessed on the host.
  11. *
  12. * @var int
  13. */
  14. protected $port;
  15. /**
  16. * The connection to the Redis database.
  17. *
  18. * @var resource
  19. */
  20. protected $connection;
  21. /**
  22. * The active Redis database instances.
  23. *
  24. * @var array
  25. */
  26. protected static $databases = array();
  27. /**
  28. * Create a new Redis connection instance.
  29. *
  30. * @param string $host
  31. * @param string $port
  32. * @return void
  33. */
  34. public function __construct($host, $port)
  35. {
  36. $this->host = $host;
  37. $this->port = $port;
  38. }
  39. /**
  40. * Get a Redis database connection instance.
  41. *
  42. * The given name should correspond to a Redis database in the configuration file.
  43. *
  44. * <code>
  45. * // Get the default Redis database instance
  46. * $redis = Redis::db();
  47. *
  48. * // Get a specified Redis database instance
  49. * $reids = Redis::db('redis_2');
  50. * </code>
  51. *
  52. * @param string $name
  53. * @return Redis
  54. */
  55. public static function db($name = 'default')
  56. {
  57. if ( ! isset(static::$databases[$name]))
  58. {
  59. if (is_null($config = Config::get("database.redis.{$name}")))
  60. {
  61. throw new \Exception("Redis database [$name] is not defined.");
  62. }
  63. static::$databases[$name] = new static($config['host'], $config['port']);
  64. }
  65. return static::$databases[$name];
  66. }
  67. /**
  68. * Execute a command against the Redis database.
  69. *
  70. * <code>
  71. * // Execute the GET command for the "name" key
  72. * $name = Redis::db()->run('get', array('name'));
  73. *
  74. * // Execute the LRANGE command for the "list" key
  75. * $list = Redis::db()->run('lrange', array(0, 5));
  76. * </code>
  77. *
  78. * @param string $method
  79. * @param array $parameters
  80. * @return mixed
  81. */
  82. public function run($method, $parameters)
  83. {
  84. fwrite($this->connect(), $this->command($method, (array) $parameters));
  85. $response = trim(fgets($this->connection, 512));
  86. switch (substr($response, 0, 1))
  87. {
  88. case '-':
  89. throw new \Exception('Redis error: '.substr(trim($response), 4));
  90. case '+':
  91. case ':':
  92. return $this->inline($response);
  93. case '$':
  94. return $this->bulk($response);
  95. case '*':
  96. return $this->multibulk($response);
  97. default:
  98. throw new \Exception("Unknown Redis response: ".substr($response, 0, 1));
  99. }
  100. }
  101. /**
  102. * Establish the connection to the Redis database.
  103. *
  104. * @return resource
  105. */
  106. protected function connect()
  107. {
  108. if ( ! is_null($this->connection)) return $this->connection;
  109. $this->connection = @fsockopen($this->host, $this->port, $error, $message);
  110. if ($this->connection === false)
  111. {
  112. throw new \Exception("Error making Redis connection: {$error} - {$message}");
  113. }
  114. return $this->connection;
  115. }
  116. /**
  117. * Build the Redis command based from a given method and parameters.
  118. *
  119. * Redis protocol states that a command should conform to the following format:
  120. *
  121. * *<number of arguments> CR LF
  122. * $<number of bytes of argument 1> CR LF
  123. * <argument data> CR LF
  124. * ...
  125. * $<number of bytes of argument N> CR LF
  126. * <argument data> CR LF
  127. *
  128. * More information regarding the Redis protocol: http://redis.io/topics/protocol
  129. *
  130. * @param string $method
  131. * @param array $parameters
  132. * @return string
  133. */
  134. protected function command($method, $parameters)
  135. {
  136. $command = '*'.(count($parameters) + 1).CRLF;
  137. $command .= '$'.strlen($method).CRLF;
  138. $command .= strtoupper($method).CRLF;
  139. foreach ($parameters as $parameter)
  140. {
  141. $command .= '$'.strlen($parameter).CRLF.$parameter.CRLF;
  142. }
  143. return $command;
  144. }
  145. /**
  146. * Parse and handle an inline response from the Redis database.
  147. *
  148. * @param string $response
  149. * @return string
  150. */
  151. protected function inline($response)
  152. {
  153. return substr(trim($response), 1);
  154. }
  155. /**
  156. * Parse and handle a bulk response from the Redis database.
  157. *
  158. * @param string $head
  159. * @return string
  160. */
  161. protected function bulk($head)
  162. {
  163. if ($head == '$-1') return;
  164. list($read, $response, $size) = array(0, '', substr($head, 1));
  165. do
  166. {
  167. // Calculate and read the appropriate bytes off of the Redis response.
  168. // We'll read off the response in 1024 byte chunks until the entire
  169. // response has been read from the database.
  170. $block = (($remaining = $size - $read) < 1024) ? $remaining : 1024;
  171. $response .= fread($this->connection, $block);
  172. $read += $block;
  173. } while ($read < $size);
  174. // The response ends with a trailing CRLF. So, we need to read that off
  175. // of the end of the file stream to get it out of the way of the next
  176. // command that is issued to the database.
  177. fread($this->connection, 2);
  178. return $response;
  179. }
  180. /**
  181. * Parse and handle a multi-bulk reply from the Redis database.
  182. *
  183. * @param string $head
  184. * @return array
  185. */
  186. protected function multibulk($head)
  187. {
  188. if (($count = substr($head, 1)) == '-1') return;
  189. $response = array();
  190. // Iterate through each bulk response in the multi-bulk and parse it out
  191. // using the "bulk" method since a multi-bulk response is just a list of
  192. // plain old bulk responses.
  193. for ($i = 0; $i < $count; $i++)
  194. {
  195. $response[] = $this->bulk(trim(fgets($this->connection, 512)));
  196. }
  197. return $response;
  198. }
  199. /**
  200. * Dynamically make calls to the Redis database.
  201. */
  202. public function __call($method, $parameters)
  203. {
  204. return $this->run($method, $parameters);
  205. }
  206. /**
  207. * Dynamically pass static method calls to the Redis instance.
  208. */
  209. public static function __callStatic($method, $parameters)
  210. {
  211. return static::db()->run($method, $parameters);
  212. }
  213. /**
  214. * Close the connection to the Redis database.
  215. *
  216. * @return void
  217. */
  218. public function __destruct()
  219. {
  220. if ($this->connection)
  221. {
  222. fclose($this->connection);
  223. }
  224. }
  225. }