AbstractScanIterator.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. }
  122. /**
  123. * {@inheritdoc}
  124. */
  125. public function current()
  126. {
  127. return $this->current;
  128. }
  129. /**
  130. * {@inheritdoc}
  131. */
  132. public function key()
  133. {
  134. return $this->position;
  135. }
  136. /**
  137. * {@inheritdoc}
  138. */
  139. public function next()
  140. {
  141. if (!$this->elements && $this->scanmore) {
  142. $this->feed();
  143. }
  144. if ($this->elements) {
  145. $this->extractNext();
  146. } else if ($this->cursor) {
  147. $this->next();
  148. } else {
  149. $this->valid = false;
  150. }
  151. }
  152. /**
  153. * {@inheritdoc}
  154. */
  155. public function valid()
  156. {
  157. return $this->valid;
  158. }
  159. }