RedisCollectionIterator.php 4.3 KB

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