/etc/psalm/LaminasPriorityQueueStub.php

https://github.com/Sylius/Sylius · PHP · 348 lines · 162 code · 30 blank · 156 comment · 13 complexity · 50ed11ea32e81ef571d14a61705e53da MD5 · raw file

  1. <?php
  2. /**
  3. * @see https://github.com/laminas/laminas-stdlib for the canonical source repository
  4. * @copyright https://github.com/laminas/laminas-stdlib/blob/master/COPYRIGHT.md
  5. * @license https://github.com/laminas/laminas-stdlib/blob/master/LICENSE.md New BSD License
  6. */
  7. namespace Laminas\Stdlib;
  8. use Countable;
  9. use IteratorAggregate;
  10. use Serializable;
  11. use function array_map;
  12. use function count;
  13. use function get_class;
  14. use function serialize;
  15. use function sprintf;
  16. use function unserialize;
  17. /**
  18. * Re-usable, serializable priority queue implementation
  19. *
  20. * SplPriorityQueue acts as a heap; on iteration, each item is removed from the
  21. * queue. If you wish to re-use such a queue, you need to clone it first. This
  22. * makes for some interesting issues if you wish to delete items from the queue,
  23. * or, as already stated, iterate over it multiple times.
  24. *
  25. * This class aggregates items for the queue itself, but also composes an
  26. * "inner" iterator in the form of an SplPriorityQueue object for performing
  27. * the actual iteration.
  28. *
  29. * @psalm-template T
  30. * @implements IteratorAggregate<int, T>
  31. */
  32. class PriorityQueue implements Countable, IteratorAggregate, Serializable
  33. {
  34. const EXTR_DATA = 0x00000001;
  35. const EXTR_PRIORITY = 0x00000002;
  36. const EXTR_BOTH = 0x00000003;
  37. /**
  38. * Inner queue class to use for iteration
  39. * @var string
  40. */
  41. protected $queueClass = SplPriorityQueue::class;
  42. /**
  43. * Actual items aggregated in the priority queue. Each item is an array
  44. * with keys "data" and "priority".
  45. * @var array
  46. */
  47. protected $items = [];
  48. /**
  49. * Inner queue object
  50. * @var SplPriorityQueue
  51. */
  52. protected $queue;
  53. /**
  54. * Insert an item into the queue
  55. *
  56. * Priority defaults to 1 (low priority) if none provided.
  57. *
  58. * @param mixed $data
  59. * @param int $priority
  60. * @return PriorityQueue
  61. *
  62. * @psalm-param T $data
  63. * @psalm-return PriorityQueue<T>
  64. */
  65. public function insert($data, $priority = 1)
  66. {
  67. $priority = (int) $priority;
  68. $this->items[] = [
  69. 'data' => $data,
  70. 'priority' => $priority,
  71. ];
  72. $this->getQueue()->insert($data, $priority);
  73. return $this;
  74. }
  75. /**
  76. * Remove an item from the queue
  77. *
  78. * This is different than {@link extract()}; its purpose is to dequeue an
  79. * item.
  80. *
  81. * This operation is potentially expensive, as it requires
  82. * re-initialization and re-population of the inner queue.
  83. *
  84. * Note: this removes the first item matching the provided item found. If
  85. * the same item has been added multiple times, it will not remove other
  86. * instances.
  87. *
  88. * @param mixed $datum
  89. * @return bool False if the item was not found, true otherwise.
  90. *
  91. * @psalm-param T $datum
  92. */
  93. public function remove($datum)
  94. {
  95. $found = false;
  96. foreach ($this->items as $key => $item) {
  97. if ($item['data'] === $datum) {
  98. $found = true;
  99. break;
  100. }
  101. }
  102. if ($found) {
  103. unset($this->items[$key]);
  104. $this->queue = null;
  105. if (! $this->isEmpty()) {
  106. $queue = $this->getQueue();
  107. foreach ($this->items as $item) {
  108. $queue->insert($item['data'], $item['priority']);
  109. }
  110. }
  111. return true;
  112. }
  113. return false;
  114. }
  115. /**
  116. * Is the queue empty?
  117. *
  118. * @return bool
  119. */
  120. public function isEmpty()
  121. {
  122. return (0 === $this->count());
  123. }
  124. /**
  125. * How many items are in the queue?
  126. *
  127. * @return int
  128. */
  129. public function count()
  130. {
  131. return count($this->items);
  132. }
  133. /**
  134. * Peek at the top node in the queue, based on priority.
  135. *
  136. * @return mixed
  137. */
  138. public function top()
  139. {
  140. return $this->getIterator()->top();
  141. }
  142. /**
  143. * Extract a node from the inner queue and sift up
  144. *
  145. * @return mixed
  146. */
  147. public function extract()
  148. {
  149. $value = $this->getQueue()->extract();
  150. $keyToRemove = null;
  151. $highestPriority = null;
  152. foreach ($this->items as $key => $item) {
  153. if ($item['data'] !== $value) {
  154. continue;
  155. }
  156. if (null === $highestPriority) {
  157. $highestPriority = $item['priority'];
  158. $keyToRemove = $key;
  159. continue;
  160. }
  161. if ($highestPriority >= $item['priority']) {
  162. continue;
  163. }
  164. $highestPriority = $item['priority'];
  165. $keyToRemove = $key;
  166. }
  167. if ($keyToRemove !== null) {
  168. unset($this->items[$keyToRemove]);
  169. }
  170. return $value;
  171. }
  172. /**
  173. * Retrieve the inner iterator
  174. *
  175. * SplPriorityQueue acts as a heap, which typically implies that as items
  176. * are iterated, they are also removed. This does not work for situations
  177. * where the queue may be iterated multiple times. As such, this class
  178. * aggregates the values, and also injects an SplPriorityQueue. This method
  179. * retrieves the inner queue object, and clones it for purposes of
  180. * iteration.
  181. *
  182. * @return SplPriorityQueue
  183. *
  184. * @psalm-return SplPriorityQueue<T>
  185. */
  186. public function getIterator()
  187. {
  188. $queue = $this->getQueue();
  189. return clone $queue;
  190. }
  191. /**
  192. * Serialize the data structure
  193. *
  194. * @return string
  195. */
  196. public function serialize()
  197. {
  198. return serialize($this->items);
  199. }
  200. /**
  201. * Unserialize a string into a PriorityQueue object
  202. *
  203. * Serialization format is compatible with {@link Laminas\Stdlib\SplPriorityQueue}
  204. *
  205. * @param string $data
  206. * @return void
  207. */
  208. public function unserialize($data)
  209. {
  210. foreach (unserialize($data) as $item) {
  211. $this->insert($item['data'], $item['priority']);
  212. }
  213. }
  214. /**
  215. * Serialize to an array
  216. *
  217. * By default, returns only the item data, and in the order registered (not
  218. * sorted). You may provide one of the EXTR_* flags as an argument, allowing
  219. * the ability to return priorities or both data and priority.
  220. *
  221. * @param int $flag
  222. * @return array
  223. */
  224. public function toArray($flag = self::EXTR_DATA)
  225. {
  226. switch ($flag) {
  227. case self::EXTR_BOTH:
  228. return $this->items;
  229. case self::EXTR_PRIORITY:
  230. return array_map(function ($item) {
  231. return $item['priority'];
  232. }, $this->items);
  233. case self::EXTR_DATA:
  234. default:
  235. return array_map(function ($item) {
  236. return $item['data'];
  237. }, $this->items);
  238. }
  239. }
  240. /**
  241. * Specify the internal queue class
  242. *
  243. * Please see {@link getIterator()} for details on the necessity of an
  244. * internal queue class. The class provided should extend SplPriorityQueue.
  245. *
  246. * @param string $class
  247. * @return PriorityQueue
  248. *
  249. * @psalm-return PriorityQueue<T>
  250. */
  251. public function setInternalQueueClass($class)
  252. {
  253. $this->queueClass = (string) $class;
  254. return $this;
  255. }
  256. /**
  257. * Does the queue contain the given datum?
  258. *
  259. * @param mixed $datum
  260. * @return bool
  261. *
  262. * @psalm-param T $datum
  263. */
  264. public function contains($datum)
  265. {
  266. foreach ($this->items as $item) {
  267. if ($item['data'] === $datum) {
  268. return true;
  269. }
  270. }
  271. return false;
  272. }
  273. /**
  274. * Does the queue have an item with the given priority?
  275. *
  276. * @param int $priority
  277. * @return bool
  278. */
  279. public function hasPriority($priority)
  280. {
  281. foreach ($this->items as $item) {
  282. if ($item['priority'] === $priority) {
  283. return true;
  284. }
  285. }
  286. return false;
  287. }
  288. /**
  289. * Get the inner priority queue instance
  290. *
  291. * @throws Exception\DomainException
  292. * @return SplPriorityQueue
  293. */
  294. protected function getQueue()
  295. {
  296. if (null === $this->queue) {
  297. $this->queue = new $this->queueClass();
  298. if (! $this->queue instanceof \SplPriorityQueue) {
  299. throw new Exception\DomainException(sprintf(
  300. 'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"',
  301. get_class($this->queue)
  302. ));
  303. }
  304. }
  305. return $this->queue;
  306. }
  307. /**
  308. * Add support for deep cloning
  309. *
  310. * @return void
  311. */
  312. public function __clone()
  313. {
  314. if (null !== $this->queue) {
  315. $this->queue = clone $this->queue;
  316. }
  317. }
  318. }