PriorityList.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php
  2. declare(strict_types=1);
  3. namespace Laminas\Stdlib;
  4. use Countable;
  5. use Exception;
  6. use Iterator;
  7. use ReturnTypeWillChange;
  8. use function array_map;
  9. use function current;
  10. use function key;
  11. use function next;
  12. use function reset;
  13. use function uasort;
  14. /**
  15. * @template TKey of string
  16. * @template TValue of mixed
  17. * @template-implements Iterator<TKey, TValue>
  18. */
  19. class PriorityList implements Iterator, Countable
  20. {
  21. public const EXTR_DATA = 0x00000001;
  22. public const EXTR_PRIORITY = 0x00000002;
  23. public const EXTR_BOTH = 0x00000003;
  24. /**
  25. * Internal list of all items.
  26. *
  27. * @var array<TKey, array{data: TValue, priority: int, serial: positive-int|0}>
  28. */
  29. protected $items = [];
  30. /**
  31. * Serial assigned to items to preserve LIFO.
  32. *
  33. * @var positive-int|0
  34. */
  35. protected $serial = 0;
  36. // phpcs:disable WebimpressCodingStandard.NamingConventions.ValidVariableName.NotCamelCapsProperty
  37. /**
  38. * Serial order mode
  39. *
  40. * @var integer
  41. */
  42. protected $isLIFO = 1;
  43. // phpcs:enable
  44. /**
  45. * Internal counter to avoid usage of count().
  46. *
  47. * @var int
  48. */
  49. protected $count = 0;
  50. /**
  51. * Whether the list was already sorted.
  52. *
  53. * @var bool
  54. */
  55. protected $sorted = false;
  56. /**
  57. * Insert a new item.
  58. *
  59. * @param TKey $name
  60. * @param TValue $value
  61. * @param int $priority
  62. * @return void
  63. */
  64. public function insert($name, mixed $value, $priority = 0)
  65. {
  66. if (! isset($this->items[$name])) {
  67. $this->count++;
  68. }
  69. $this->sorted = false;
  70. $this->items[$name] = [
  71. 'data' => $value,
  72. 'priority' => (int) $priority,
  73. 'serial' => $this->serial++,
  74. ];
  75. }
  76. /**
  77. * @param TKey $name
  78. * @param int $priority
  79. * @return $this
  80. * @throws Exception
  81. */
  82. public function setPriority($name, $priority)
  83. {
  84. if (! isset($this->items[$name])) {
  85. throw new Exception("item $name not found");
  86. }
  87. $this->items[$name]['priority'] = (int) $priority;
  88. $this->sorted = false;
  89. return $this;
  90. }
  91. /**
  92. * Remove a item.
  93. *
  94. * @param TKey $name
  95. * @return void
  96. */
  97. public function remove($name)
  98. {
  99. if (isset($this->items[$name])) {
  100. $this->count--;
  101. }
  102. unset($this->items[$name]);
  103. }
  104. /**
  105. * Remove all items.
  106. *
  107. * @return void
  108. */
  109. public function clear()
  110. {
  111. $this->items = [];
  112. $this->serial = 0;
  113. $this->count = 0;
  114. $this->sorted = false;
  115. }
  116. /**
  117. * Get a item.
  118. *
  119. * @param TKey $name
  120. * @return TValue|null
  121. */
  122. public function get($name)
  123. {
  124. if (! isset($this->items[$name])) {
  125. return;
  126. }
  127. return $this->items[$name]['data'];
  128. }
  129. /**
  130. * Sort all items.
  131. *
  132. * @return void
  133. */
  134. protected function sort()
  135. {
  136. if (! $this->sorted) {
  137. uasort($this->items, [$this, 'compare']);
  138. $this->sorted = true;
  139. }
  140. }
  141. /**
  142. * Compare the priority of two items.
  143. *
  144. * @param array $item1,
  145. * @return int
  146. */
  147. protected function compare(array $item1, array $item2)
  148. {
  149. return $item1['priority'] === $item2['priority']
  150. ? ($item1['serial'] > $item2['serial'] ? -1 : 1) * $this->isLIFO
  151. : ($item1['priority'] > $item2['priority'] ? -1 : 1);
  152. }
  153. /**
  154. * Get/Set serial order mode
  155. *
  156. * @param bool|null $flag
  157. * @return bool
  158. */
  159. public function isLIFO($flag = null)
  160. {
  161. if ($flag !== null) {
  162. $isLifo = $flag === true ? 1 : -1;
  163. if ($isLifo !== $this->isLIFO) {
  164. $this->isLIFO = $isLifo;
  165. $this->sorted = false;
  166. }
  167. }
  168. return 1 === $this->isLIFO;
  169. }
  170. /**
  171. * {@inheritDoc}
  172. */
  173. #[ReturnTypeWillChange]
  174. public function rewind()
  175. {
  176. $this->sort();
  177. reset($this->items);
  178. }
  179. /**
  180. * {@inheritDoc}
  181. */
  182. #[ReturnTypeWillChange]
  183. public function current()
  184. {
  185. $this->sorted || $this->sort();
  186. $node = current($this->items);
  187. return $node ? $node['data'] : false;
  188. }
  189. /**
  190. * {@inheritDoc}
  191. */
  192. #[ReturnTypeWillChange]
  193. public function key()
  194. {
  195. $this->sorted || $this->sort();
  196. return key($this->items);
  197. }
  198. /**
  199. * {@inheritDoc}
  200. */
  201. #[ReturnTypeWillChange]
  202. public function next()
  203. {
  204. $node = next($this->items);
  205. return $node ? $node['data'] : false;
  206. }
  207. /**
  208. * {@inheritDoc}
  209. */
  210. #[ReturnTypeWillChange]
  211. public function valid()
  212. {
  213. return current($this->items) !== false;
  214. }
  215. /**
  216. * @return self
  217. */
  218. public function getIterator()
  219. {
  220. return clone $this;
  221. }
  222. /**
  223. * {@inheritDoc}
  224. */
  225. #[ReturnTypeWillChange]
  226. public function count()
  227. {
  228. return $this->count;
  229. }
  230. /**
  231. * Return list as array
  232. *
  233. * @param int $flag
  234. * @return array
  235. */
  236. public function toArray($flag = self::EXTR_DATA)
  237. {
  238. $this->sort();
  239. if ($flag === self::EXTR_BOTH) {
  240. return $this->items;
  241. }
  242. return array_map(
  243. static fn($item) => $flag === self::EXTR_PRIORITY ? $item['priority'] : $item['data'],
  244. $this->items
  245. );
  246. }
  247. }