TextProtocol.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. /*
  3. * This file is part of the Predis package.
  4. *
  5. * (c) Daniele Alessandri <suppakilla@gmail.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Predis\Protocol\Text;
  11. use Predis\Helpers;
  12. use Predis\ResponseError;
  13. use Predis\ResponseQueued;
  14. use Predis\ServerException;
  15. use Predis\Commands\ICommand;
  16. use Predis\Protocol\IProtocolProcessor;
  17. use Predis\Protocol\ProtocolException;
  18. use Predis\Network\IConnectionComposable;
  19. use Predis\Iterators\MultiBulkResponseSimple;
  20. /**
  21. * Implements a protocol processor for the standard wire protocol defined by Redis.
  22. *
  23. * @link http://redis.io/topics/protocol
  24. * @author Daniele Alessandri <suppakilla@gmail.com>
  25. */
  26. class TextProtocol implements IProtocolProcessor
  27. {
  28. const NEWLINE = "\r\n";
  29. const OK = 'OK';
  30. const ERROR = 'ERR';
  31. const QUEUED = 'QUEUED';
  32. const NULL = 'nil';
  33. const PREFIX_STATUS = '+';
  34. const PREFIX_ERROR = '-';
  35. const PREFIX_INTEGER = ':';
  36. const PREFIX_BULK = '$';
  37. const PREFIX_MULTI_BULK = '*';
  38. const BUFFER_SIZE = 4096;
  39. private $mbiterable;
  40. private $throwErrors;
  41. private $serializer;
  42. /**
  43. *
  44. */
  45. public function __construct()
  46. {
  47. $this->mbiterable = false;
  48. $this->throwErrors = true;
  49. $this->serializer = new TextCommandSerializer();
  50. }
  51. /**
  52. * {@inheritdoc}
  53. */
  54. public function write(IConnectionComposable $connection, ICommand $command)
  55. {
  56. $connection->writeBytes($this->serializer->serialize($command));
  57. }
  58. /**
  59. * {@inheritdoc}
  60. */
  61. public function read(IConnectionComposable $connection)
  62. {
  63. $chunk = $connection->readLine();
  64. $prefix = $chunk[0];
  65. $payload = substr($chunk, 1);
  66. switch ($prefix) {
  67. case '+': // inline
  68. switch ($payload) {
  69. case 'OK':
  70. return true;
  71. case 'QUEUED':
  72. return new ResponseQueued();
  73. default:
  74. return $payload;
  75. }
  76. case '$': // bulk
  77. $size = (int) $payload;
  78. if ($size === -1) {
  79. return null;
  80. }
  81. return substr($connection->readBytes($size + 2), 0, -2);
  82. case '*': // multi bulk
  83. $count = (int) $payload;
  84. if ($count === -1) {
  85. return null;
  86. }
  87. if ($this->mbiterable == true) {
  88. return new MultiBulkResponseSimple($connection, $count);
  89. }
  90. $multibulk = array();
  91. for ($i = 0; $i < $count; $i++) {
  92. $multibulk[$i] = $this->read($connection);
  93. }
  94. return $multibulk;
  95. case ':': // integer
  96. return (int) $payload;
  97. case '-': // error
  98. if ($this->throwErrors) {
  99. throw new ServerException($payload);
  100. }
  101. return new ResponseError($payload);
  102. default:
  103. Helpers::onCommunicationException(new ProtocolException(
  104. $connection, "Unknown prefix: '$prefix'"
  105. ));
  106. }
  107. }
  108. /**
  109. * {@inheritdoc}
  110. */
  111. public function setOption($option, $value)
  112. {
  113. switch ($option) {
  114. case 'iterable_multibulk':
  115. $this->mbiterable = (bool) $value;
  116. break;
  117. case 'throw_errors':
  118. $this->throwErrors = (bool) $value;
  119. break;
  120. }
  121. }
  122. }