AbstractScanIterator.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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\Iterator\Scan;
  11. use Iterator;
  12. use Countable;
  13. use Predis\ClientInterface;
  14. use Predis\NotSupportedException;
  15. /**
  16. * This class provides the base implementation for a fully-rewindable
  17. * PHP iterator that can incrementally iterate over collections stored
  18. * on Redis by leveraging the SCAN family of commands (Redis >= 2.8).
  19. *
  20. * @author Daniele Alessandri <suppakilla@gmail.com>
  21. * @link http://redis.io/commands/scan
  22. */
  23. abstract class AbstractScanIterator implements Iterator
  24. {
  25. protected $client;
  26. protected $match;
  27. protected $count;
  28. protected $valid;
  29. protected $scanmore;
  30. protected $elements;
  31. protected $cursor;
  32. protected $position;
  33. protected $current;
  34. /**
  35. * @param ClientInterface $client Client connected to Redis.
  36. * @param string $match Pattern to match during the server-side iteration.
  37. * @param int $count Hints used by Redis to compute the number of results per iteration.
  38. */
  39. public function __construct(ClientInterface $client, $match = null, $count = null)
  40. {
  41. $this->client = $client;
  42. $this->match = $match;
  43. $this->count = $count;
  44. $this->reset();
  45. }
  46. /**
  47. * Ensures that the client instance supports the specified
  48. * Redis command required to perform the server-side iteration.
  49. *
  50. * @param ClientInterface Client connected to Redis.
  51. * @param string $commandID Command ID (e.g. `SCAN`).
  52. */
  53. protected function requiredCommand(ClientInterface $client, $commandID)
  54. {
  55. if (!$client->getProfile()->supportsCommand($commandID)) {
  56. throw new NotSupportedException("The specified server profile does not support the $commandID command.");
  57. }
  58. }
  59. /**
  60. * Resets the inner state of the iterator.
  61. */
  62. protected function reset()
  63. {
  64. $this->valid = true;
  65. $this->scanmore = true;
  66. $this->elements = array();
  67. $this->cursor = 0;
  68. $this->position = -1;
  69. $this->current = null;
  70. }
  71. /**
  72. * Returns an array of options for the SCAN command.
  73. *
  74. * @return array
  75. */
  76. protected function getScanOptions()
  77. {
  78. $options = array();
  79. if (strlen($this->match) > 0) {
  80. $options['MATCH'] = $this->match;
  81. }
  82. if ($this->count > 0) {
  83. $options['COUNT'] = $this->count;
  84. }
  85. return $options;
  86. }
  87. /**
  88. * Performs a new SCAN to fetch new elements in the collection from
  89. * Redis, effectively advancing the iteration process.
  90. *
  91. * @return array
  92. */
  93. protected abstract function executeScanCommand();
  94. /**
  95. * Populates the local buffer of elements fetched from the server
  96. * during the iteration.
  97. */
  98. protected function feed()
  99. {
  100. list($cursor, $elements) = $this->executeScanCommand();
  101. if (!$cursor) {
  102. $this->scanmore = false;
  103. }
  104. $this->cursor = $cursor;
  105. $this->elements = $elements;
  106. }
  107. /**
  108. * Extracts next values for key() and current().
  109. */
  110. protected function extractNext()
  111. {
  112. $this->position++;
  113. $this->current = array_shift($this->elements);
  114. }
  115. /**
  116. * {@inheritdoc}
  117. */
  118. public function rewind()
  119. {
  120. $this->reset();
  121. $this->next();
  122. }
  123. /**
  124. * {@inheritdoc}
  125. */
  126. public function current()
  127. {
  128. return $this->current;
  129. }
  130. /**
  131. * {@inheritdoc}
  132. */
  133. public function key()
  134. {
  135. return $this->position;
  136. }
  137. /**
  138. * {@inheritdoc}
  139. */
  140. public function next()
  141. {
  142. if (!$this->elements && $this->scanmore) {
  143. $this->feed();
  144. }
  145. if ($this->elements) {
  146. $this->extractNext();
  147. } else {
  148. $this->valid = false;
  149. }
  150. }
  151. /**
  152. * {@inheritdoc}
  153. */
  154. public function valid()
  155. {
  156. return $this->valid;
  157. }
  158. }