PageRenderTime 66ms CodeModel.GetById 38ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Rediska/Connection/Exec.php

http://github.com/Shumkov/Rediska
PHP | 305 lines | 150 code | 41 blank | 114 comment | 23 complexity | f101fb3dab5f1f5164c9334c47e27f5f MD5 | raw file
  1. <?php
  2. /**
  3. * Rediska connection exec
  4. *
  5. * @author Ivan Shumkov
  6. * @package Rediska
  7. * @subpackage Connection
  8. * @version @package_version@
  9. * @link http://rediska.geometria-lab.net
  10. * @license http://www.opensource.org/licenses/bsd-license.php
  11. */
  12. class Rediska_Connection_Exec
  13. {
  14. const REPLY_STATUS = '+';
  15. const REPLY_ERROR = '-';
  16. const REPLY_INTEGER = ':';
  17. const REPLY_BULK = '$';
  18. const REPLY_MULTY_BULK = '*';
  19. /**
  20. * Rediska connection
  21. *
  22. * @var Rediska_Connection
  23. */
  24. protected $_connection;
  25. /**
  26. * Cloned connection for iterator
  27. *
  28. * @var Rediska_Connection
  29. */
  30. protected $_connectionClone;
  31. /**
  32. * Command
  33. *
  34. * @var array|string
  35. */
  36. protected $_command;
  37. /**
  38. * Is writed
  39. *
  40. * @var $_isWritten boolean
  41. */
  42. protected $_isWritten = false;
  43. /**
  44. * Response callback
  45. *
  46. * @var callback
  47. */
  48. protected $_responseCallback;
  49. /**
  50. * Retrun iterator as response
  51. *
  52. * @var mixin
  53. */
  54. protected $_responseIterator;
  55. /**
  56. * Constructor
  57. *
  58. * @param Rediska_Connection $connection Connection
  59. * @param array|string $command Command
  60. */
  61. public function __construct(Rediska_Connection $connection, $command)
  62. {
  63. if (is_array($command)) {
  64. $command = self::transformMultiBulkCommand($command);
  65. }
  66. $this->_connection = $connection;
  67. $this->_command = $command;
  68. }
  69. /**
  70. * Write command to connection
  71. *
  72. * @return boolean
  73. */
  74. public function write()
  75. {
  76. $result = $this->getConnection()->write($this->getCommand());
  77. $this->_isWritten = true;
  78. return $result;
  79. }
  80. /**
  81. * Is writed?
  82. *
  83. * @return boolean
  84. */
  85. public function isWritten()
  86. {
  87. return $this->_isWritten;
  88. }
  89. /**
  90. * Read response from connection
  91. *
  92. * @return array|string
  93. */
  94. public function read()
  95. {
  96. if (!$this->isWritten()) {
  97. throw new Rediska_Connection_Exec_Exception('You must write command before read');
  98. }
  99. $this->_isWritten = false;
  100. if ($this->getResponseIterator() !== null) {
  101. if ($this->getResponseIterator() === true) {
  102. $className = 'Rediska_Connection_Exec_MultiBulkIterator';
  103. } else {
  104. $className = $this->getResponseIterator();
  105. }
  106. $response = new $className($this->getConnection(), $this->getResponseCallback());
  107. } else {
  108. $response = self::readResponseFromConnection($this->getConnection());
  109. if ($this->_responseCallback !== null) {
  110. $response = call_user_func($this->getResponseCallback(), $response);
  111. }
  112. }
  113. return $response;
  114. }
  115. /**
  116. * Execute command
  117. *
  118. * @return array|string
  119. */
  120. public function execute()
  121. {
  122. $this->write();
  123. return $this->read();
  124. }
  125. /**
  126. * Magic method for execute
  127. *
  128. * @return array|string
  129. */
  130. public function __invoke()
  131. {
  132. return $this->execute();
  133. }
  134. /**
  135. * Get connection
  136. *
  137. * @return Rediska_Connection
  138. */
  139. public function getConnection()
  140. {
  141. if ($this->_responseIterator === null) {
  142. return $this->_connection;
  143. } else {
  144. if ($this->_connectionClone === null) {
  145. $this->_connectionClone = clone $this->_connection;
  146. }
  147. return $this->_connectionClone;
  148. }
  149. return $this->_connection;
  150. }
  151. /**
  152. * Get command
  153. *
  154. * @return string
  155. */
  156. public function getCommand()
  157. {
  158. return $this->_command;
  159. }
  160. /**
  161. * Set response callback
  162. *
  163. * @param mixin $callback
  164. * @return Rediska_Connection_Exec
  165. */
  166. public function setResponseCallback($callback)
  167. {
  168. if ($callback !== null && !is_callable($callback)) {
  169. throw new Rediska_Connection_Exec_Exception('Bad callback');
  170. }
  171. $this->_responseCallback = $callback;
  172. return $this;
  173. }
  174. /**
  175. * Get response callback
  176. *
  177. * @return mixin
  178. */
  179. public function getResponseCallback()
  180. {
  181. return $this->_responseCallback;
  182. }
  183. /**
  184. * Set response iterator
  185. *
  186. * @param boolean $enable
  187. */
  188. public function setResponseIterator($responseIterator)
  189. {
  190. $this->_responseIterator = $responseIterator;
  191. return $this;
  192. }
  193. /**
  194. * Is enabled response iterator
  195. *
  196. * @return boolean
  197. */
  198. public function getResponseIterator()
  199. {
  200. return $this->_responseIterator;
  201. }
  202. /**
  203. * Transfrom Multi Bulk command to string
  204. *
  205. * @param array $command
  206. * @return string
  207. */
  208. public static function transformMultiBulkCommand(array $command)
  209. {
  210. $commandString = self::REPLY_MULTY_BULK . count($command) . Rediska::EOL;
  211. foreach($command as $argument) {
  212. $commandString .= self::REPLY_BULK . strlen($argument) . Rediska::EOL . $argument . Rediska::EOL;
  213. }
  214. return $commandString;
  215. }
  216. /**
  217. * Read response from connection
  218. *
  219. * @param Rediska_Connection $connection
  220. * @return mixed
  221. */
  222. public static function readResponseFromConnection(Rediska_Connection $connection)
  223. {
  224. $reply = $connection->readLine();
  225. if ($reply === null) {
  226. return $reply;
  227. }
  228. $type = substr($reply, 0, 1);
  229. $data = substr($reply, 1);
  230. switch ($type) {
  231. case self::REPLY_STATUS:
  232. if ($data == 'OK') {
  233. return true;
  234. } else {
  235. return $data;
  236. }
  237. case self::REPLY_ERROR:
  238. throw new Rediska_Connection_Exec_Exception($data);
  239. case self::REPLY_INTEGER:
  240. if (strpos($data, '.') !== false) {
  241. $number = (integer)$data;
  242. } else {
  243. $number = (float)$data;
  244. }
  245. return $number;
  246. case self::REPLY_BULK:
  247. if ($data == '-1') {
  248. return null;
  249. } else {
  250. $length = (integer)$data;
  251. return $connection->read($length);
  252. }
  253. case self::REPLY_MULTY_BULK:
  254. $count = (integer)$data;
  255. $replies = array();
  256. for ($i = 0; $i < $count; $i++) {
  257. $replies[] = self::readResponseFromConnection($connection);
  258. }
  259. return $replies;
  260. default:
  261. throw new Rediska_Connection_Exec_Exception("Invalid reply type: '$type'");
  262. }
  263. }
  264. }