File indexing completed on 2025-01-26 05:25:32
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Stdlib 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 */ 0020 0021 if (version_compare(PHP_VERSION, '5.3.0', '<')) { 0022 /** 0023 * SplPriorityQueue 0024 * 0025 * PHP 5.2.X userland implementation of PHP's SplPriorityQueue 0026 */ 0027 class SplPriorityQueue implements Iterator , Countable 0028 { 0029 /** 0030 * Extract data only 0031 */ 0032 const EXTR_DATA = 0x00000001; 0033 0034 /** 0035 * Extract priority only 0036 */ 0037 const EXTR_PRIORITY = 0x00000002; 0038 0039 /** 0040 * Extract an array of ('data' => $value, 'priority' => $priority) 0041 */ 0042 const EXTR_BOTH = 0x00000003; 0043 0044 /** 0045 * Count of items in the queue 0046 * @var int 0047 */ 0048 protected $count = 0; 0049 0050 /** 0051 * Flag indicating what should be returned when iterating or extracting 0052 * @var int 0053 */ 0054 protected $extractFlags = self::EXTR_DATA; 0055 0056 /** 0057 * @var bool|array 0058 */ 0059 protected $preparedQueue = false; 0060 0061 /** 0062 * All items in the queue 0063 * @var array 0064 */ 0065 protected $queue = array(); 0066 0067 /** 0068 * Constructor 0069 * 0070 * Creates a new, empty queue 0071 * 0072 * @return void 0073 */ 0074 public function __construct() 0075 { 0076 } 0077 0078 /** 0079 * Compare two priorities 0080 * 0081 * Returns positive integer if $priority1 is greater than $priority2, 0 0082 * if equal, negative otherwise. 0083 * 0084 * Unused internally, and only included in order to retain the same 0085 * interface as PHP's SplPriorityQueue. 0086 * 0087 * @param mixed $priority1 0088 * @param mixed $priority2 0089 * @return int 0090 */ 0091 public function compare($priority1, $priority2) 0092 { 0093 if ($priority1 > $priority2) { 0094 return 1; 0095 } 0096 if ($priority1 == $priority2) { 0097 return 0; 0098 } 0099 0100 return -1; 0101 } 0102 0103 /** 0104 * Countable: return number of items composed in the queue 0105 * 0106 * @return int 0107 */ 0108 public function count() 0109 { 0110 return $this->count; 0111 } 0112 0113 /** 0114 * Iterator: return current item 0115 * 0116 * @return mixed 0117 */ 0118 public function current() 0119 { 0120 if (!$this->preparedQueue) { 0121 $this->rewind(); 0122 } 0123 if (!$this->count) { 0124 throw new OutOfBoundsException('Cannot iterate SplPriorityQueue; no elements present'); 0125 } 0126 0127 if (!is_array($this->preparedQueue)) { 0128 throw new DomainException(sprintf( 0129 "Queue was prepared, but is empty?\n PreparedQueue: %s\n Internal Queue: %s\n", 0130 var_export($this->preparedQueue, 1), 0131 var_export($this->queue, 1) 0132 )); 0133 } 0134 0135 $return = array_shift($this->preparedQueue); 0136 $priority = $return['priority']; 0137 $priorityKey = $return['priorityKey']; 0138 $key = $return['key']; 0139 unset($return['key']); 0140 unset($return['priorityKey']); 0141 unset($this->queue[$priorityKey][$key]); 0142 0143 switch ($this->extractFlags) { 0144 case self::EXTR_DATA: 0145 return $return['data']; 0146 case self::EXTR_PRIORITY: 0147 return $return['priority']; 0148 case self::EXTR_BOTH: 0149 default: 0150 return $return; 0151 }; 0152 } 0153 0154 /** 0155 * Extract a node from top of the heap and sift up 0156 * 0157 * Returns either the value, the priority, or both, depending on the extract flag. 0158 * 0159 * @return mixed; 0160 */ 0161 public function extract() 0162 { 0163 if (!$this->count) { 0164 return null; 0165 } 0166 0167 if (!$this->preparedQueue) { 0168 $this->prepareQueue(); 0169 } 0170 0171 if (empty($this->preparedQueue)) { 0172 return null; 0173 } 0174 0175 $return = array_shift($this->preparedQueue); 0176 $priority = $return['priority']; 0177 $priorityKey = $return['priorityKey']; 0178 $key = $return['key']; 0179 unset($return['key']); 0180 unset($return['priorityKey']); 0181 unset($this->queue[$priorityKey][$key]); 0182 $this->count--; 0183 0184 switch ($this->extractFlags) { 0185 case self::EXTR_DATA: 0186 return $return['data']; 0187 case self::EXTR_PRIORITY: 0188 return $return['priority']; 0189 case self::EXTR_BOTH: 0190 default: 0191 return $return; 0192 }; 0193 } 0194 0195 /** 0196 * Insert a value into the heap, at the specified priority 0197 * 0198 * @param mixed $value 0199 * @param mixed $priority 0200 * @return void 0201 */ 0202 public function insert($value, $priority) 0203 { 0204 if (!is_scalar($priority)) { 0205 $priority = serialize($priority); 0206 } 0207 if (!isset($this->queue[$priority])) { 0208 $this->queue[$priority] = array(); 0209 } 0210 $this->queue[$priority][] = $value; 0211 $this->count++; 0212 $this->preparedQueue = false; 0213 } 0214 0215 /** 0216 * Is the queue currently empty? 0217 * 0218 * @return bool 0219 */ 0220 public function isEmpty() 0221 { 0222 return (0 == $this->count); 0223 } 0224 0225 /** 0226 * Iterator: return current key 0227 * 0228 * @return mixed Usually an int or string 0229 */ 0230 public function key() 0231 { 0232 return $this->count; 0233 } 0234 0235 /** 0236 * Iterator: Move pointer forward 0237 * 0238 * @return void 0239 */ 0240 public function next() 0241 { 0242 $this->count--; 0243 } 0244 0245 /** 0246 * Recover from corrupted state and allow further actions on the queue 0247 * 0248 * Unimplemented, and only included in order to retain the same interface as PHP's 0249 * SplPriorityQueue. 0250 * 0251 * @return void 0252 */ 0253 public function recoverFromCorruption() 0254 { 0255 } 0256 0257 /** 0258 * Iterator: Move pointer to first item 0259 * 0260 * @return void 0261 */ 0262 public function rewind() 0263 { 0264 if (!$this->preparedQueue) { 0265 $this->prepareQueue(); 0266 } 0267 } 0268 0269 /** 0270 * Set the extract flags 0271 * 0272 * Defines what is extracted by SplPriorityQueue::current(), 0273 * SplPriorityQueue::top() and SplPriorityQueue::extract(). 0274 * 0275 * - SplPriorityQueue::EXTR_DATA (0x00000001): Extract the data 0276 * - SplPriorityQueue::EXTR_PRIORITY (0x00000002): Extract the priority 0277 * - SplPriorityQueue::EXTR_BOTH (0x00000003): Extract an array containing both 0278 * 0279 * The default mode is SplPriorityQueue::EXTR_DATA. 0280 * 0281 * @param int $flags 0282 * @return void 0283 */ 0284 public function setExtractFlags($flags) 0285 { 0286 $expected = array( 0287 self::EXTR_DATA => true, 0288 self::EXTR_PRIORITY => true, 0289 self::EXTR_BOTH => true, 0290 ); 0291 if (!isset($expected[$flags])) { 0292 throw new InvalidArgumentException(sprintf('Expected an EXTR_* flag; received %s', $flags)); 0293 } 0294 $this->extractFlags = $flags; 0295 } 0296 0297 /** 0298 * Return the value or priority (or both) of the top node, depending on 0299 * the extract flag 0300 * 0301 * @return mixed 0302 */ 0303 public function top() 0304 { 0305 $this->sort(); 0306 $keys = array_keys($this->queue); 0307 $key = array_shift($keys); 0308 if (preg_match('/^(a|O):/', $key)) { 0309 $key = unserialize($key); 0310 } 0311 0312 if ($this->extractFlags == self::EXTR_PRIORITY) { 0313 return $key; 0314 } 0315 0316 if ($this->extractFlags == self::EXTR_DATA) { 0317 return $this->queue[$key][0]; 0318 } 0319 0320 return array( 0321 'data' => $this->queue[$key][0], 0322 'priority' => $key, 0323 ); 0324 } 0325 0326 /** 0327 * Iterator: is the current position valid for the queue 0328 * 0329 * @return bool 0330 */ 0331 public function valid() 0332 { 0333 return (bool) $this->count; 0334 } 0335 0336 /** 0337 * Sort the queue 0338 * 0339 * @return void 0340 */ 0341 protected function sort() 0342 { 0343 krsort($this->queue); 0344 } 0345 0346 /** 0347 * Prepare the queue for iteration and/or extraction 0348 * 0349 * @return void 0350 */ 0351 protected function prepareQueue() 0352 { 0353 $this->sort(); 0354 $count = $this->count; 0355 $queue = array(); 0356 foreach ($this->queue as $priority => $values) { 0357 $priorityKey = $priority; 0358 if (preg_match('/^(a|O):/', $priority)) { 0359 $priority = unserialize($priority); 0360 } 0361 foreach ($values as $key => $value) { 0362 $queue[$count] = array( 0363 'data' => $value, 0364 'priority' => $priority, 0365 'priorityKey' => $priorityKey, 0366 'key' => $key, 0367 ); 0368 $count--; 0369 } 0370 } 0371 $this->preparedQueue = $queue; 0372 } 0373 } 0374 } 0375 0376 /** 0377 * Serializable version of SplPriorityQueue 0378 * 0379 * Also, provides predictable heap order for datums added with the same priority 0380 * (i.e., they will be emitted in the same order they are enqueued). 0381 * 0382 * @category Zend 0383 * @package Zend_Stdlib 0384 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0385 * @license http://framework.zend.com/license/new-bsd New BSD License 0386 */ 0387 class Zend_Stdlib_SplPriorityQueue extends SplPriorityQueue implements Serializable 0388 { 0389 /** 0390 * @var int Seed used to ensure queue order for items of the same priority 0391 */ 0392 protected $serial = PHP_INT_MAX; 0393 0394 /** 0395 * @var bool 0396 */ 0397 protected $isPhp53; 0398 0399 /** 0400 * Constructor 0401 * 0402 * @return void 0403 */ 0404 public function __construct() 0405 { 0406 $this->isPhp53 = version_compare(PHP_VERSION, '5.3', '>='); 0407 } 0408 0409 /** 0410 * Insert a value with a given priority 0411 * 0412 * Utilizes {@var $serial} to ensure that values of equal priority are 0413 * emitted in the same order in which they are inserted. 0414 * 0415 * @param mixed $datum 0416 * @param mixed $priority 0417 * @return void 0418 */ 0419 public function insert($datum, $priority) 0420 { 0421 // If using the native PHP SplPriorityQueue implementation, we need to 0422 // hack around it to ensure that items registered at the same priority 0423 // return in the order registered. In the userland version, this is not 0424 // necessary. 0425 if ($this->isPhp53) { 0426 if (!is_array($priority)) { 0427 $priority = array($priority, $this->serial--); 0428 } 0429 } 0430 parent::insert($datum, $priority); 0431 } 0432 0433 /** 0434 * Serialize to an array 0435 * 0436 * Array will be priority => data pairs 0437 * 0438 * @return array 0439 */ 0440 public function toArray() 0441 { 0442 $this->setExtractFlags(self::EXTR_BOTH); 0443 $array = array(); 0444 while ($this->valid()) { 0445 $array[] = $this->current(); 0446 $this->next(); 0447 } 0448 $this->setExtractFlags(self::EXTR_DATA); 0449 0450 // Iterating through a priority queue removes items 0451 foreach ($array as $item) { 0452 $this->insert($item['data'], $item['priority']); 0453 } 0454 0455 // Return only the data 0456 $return = array(); 0457 foreach ($array as $item) { 0458 $return[] = $item['data']; 0459 } 0460 0461 return $return; 0462 } 0463 0464 /** 0465 * Serialize 0466 * 0467 * @return string 0468 */ 0469 public function serialize() 0470 { 0471 $data = array(); 0472 $this->setExtractFlags(self::EXTR_BOTH); 0473 while ($this->valid()) { 0474 $data[] = $this->current(); 0475 $this->next(); 0476 } 0477 $this->setExtractFlags(self::EXTR_DATA); 0478 0479 // Iterating through a priority queue removes items 0480 foreach ($data as $item) { 0481 $this->insert($item['data'], $item['priority']); 0482 } 0483 0484 return serialize($data); 0485 } 0486 0487 /** 0488 * Deserialize 0489 * 0490 * @param string $data 0491 * @return void 0492 */ 0493 public function unserialize($data) 0494 { 0495 foreach (unserialize($data) as $item) { 0496 $this->insert($item['data'], $item['priority']); 0497 } 0498 } 0499 }