2015-08-17 17:00:26 -07:00
< ? php
/**
* Zend Framework ( http :// framework . zend . com / )
*
* @ link http :// github . com / zendframework / zf2 for the canonical source repository
* @ copyright Copyright ( c ) 2005 - 2015 Zend Technologies USA Inc . ( http :// www . zend . com )
* @ license http :// framework . zend . com / license / new - bsd New BSD License
*/
namespace Zend\Stdlib ;
use Countable ;
use IteratorAggregate ;
use Serializable ;
/**
* Re - usable , serializable priority queue implementation
*
* SplPriorityQueue acts as a heap ; on iteration , each item is removed from the
* queue . If you wish to re - use such a queue , you need to clone it first . This
* makes for some interesting issues if you wish to delete items from the queue ,
* or , as already stated , iterate over it multiple times .
*
* This class aggregates items for the queue itself , but also composes an
* " inner " iterator in the form of an SplPriorityQueue object for performing
* the actual iteration .
*/
class PriorityQueue implements Countable , IteratorAggregate , Serializable
{
const EXTR_DATA = 0x00000001 ;
const EXTR_PRIORITY = 0x00000002 ;
const EXTR_BOTH = 0x00000003 ;
/**
* Inner queue class to use for iteration
* @ var string
*/
protected $queueClass = 'Zend\Stdlib\SplPriorityQueue' ;
/**
* Actual items aggregated in the priority queue . Each item is an array
* with keys " data " and " priority " .
* @ var array
*/
2015-10-08 11:40:12 -07:00
protected $items = [];
2015-08-17 17:00:26 -07:00
/**
* Inner queue object
* @ var SplPriorityQueue
*/
protected $queue ;
/**
* Insert an item into the queue
*
* Priority defaults to 1 ( low priority ) if none provided .
*
* @ param mixed $data
* @ param int $priority
* @ return PriorityQueue
*/
public function insert ( $data , $priority = 1 )
{
$priority = ( int ) $priority ;
2015-10-08 11:40:12 -07:00
$this -> items [] = [
2015-08-17 17:00:26 -07:00
'data' => $data ,
'priority' => $priority ,
2015-10-08 11:40:12 -07:00
];
2015-08-17 17:00:26 -07:00
$this -> getQueue () -> insert ( $data , $priority );
return $this ;
}
/**
* Remove an item from the queue
*
* This is different than { @ link extract ()}; its purpose is to dequeue an
* item .
*
* This operation is potentially expensive , as it requires
* re - initialization and re - population of the inner queue .
*
* Note : this removes the first item matching the provided item found . If
* the same item has been added multiple times , it will not remove other
* instances .
*
* @ param mixed $datum
* @ return bool False if the item was not found , true otherwise .
*/
public function remove ( $datum )
{
$found = false ;
foreach ( $this -> items as $key => $item ) {
if ( $item [ 'data' ] === $datum ) {
$found = true ;
break ;
}
}
if ( $found ) {
unset ( $this -> items [ $key ]);
$this -> queue = null ;
if ( ! $this -> isEmpty ()) {
$queue = $this -> getQueue ();
foreach ( $this -> items as $item ) {
$queue -> insert ( $item [ 'data' ], $item [ 'priority' ]);
}
}
return true ;
}
return false ;
}
/**
* Is the queue empty ?
*
* @ return bool
*/
public function isEmpty ()
{
return ( 0 === $this -> count ());
}
/**
* How many items are in the queue ?
*
* @ return int
*/
public function count ()
{
return count ( $this -> items );
}
/**
* Peek at the top node in the queue , based on priority .
*
* @ return mixed
*/
public function top ()
{
return $this -> getIterator () -> top ();
}
/**
* Extract a node from the inner queue and sift up
*
* @ return mixed
*/
public function extract ()
{
return $this -> getQueue () -> extract ();
}
/**
* Retrieve the inner iterator
*
* SplPriorityQueue acts as a heap , which typically implies that as items
* are iterated , they are also removed . This does not work for situations
* where the queue may be iterated multiple times . As such , this class
* aggregates the values , and also injects an SplPriorityQueue . This method
* retrieves the inner queue object , and clones it for purposes of
* iteration .
*
* @ return SplPriorityQueue
*/
public function getIterator ()
{
$queue = $this -> getQueue ();
return clone $queue ;
}
/**
* Serialize the data structure
*
* @ return string
*/
public function serialize ()
{
return serialize ( $this -> items );
}
/**
* Unserialize a string into a PriorityQueue object
*
* Serialization format is compatible with { @ link Zend\Stdlib\SplPriorityQueue }
*
* @ param string $data
* @ return void
*/
public function unserialize ( $data )
{
foreach ( unserialize ( $data ) as $item ) {
$this -> insert ( $item [ 'data' ], $item [ 'priority' ]);
}
}
/**
* Serialize to an array
*
* By default , returns only the item data , and in the order registered ( not
* sorted ) . You may provide one of the EXTR_ * flags as an argument , allowing
* the ability to return priorities or both data and priority .
*
* @ param int $flag
* @ return array
*/
public function toArray ( $flag = self :: EXTR_DATA )
{
switch ( $flag ) {
case self :: EXTR_BOTH :
return $this -> items ;
case self :: EXTR_PRIORITY :
return array_map ( function ( $item ) {
return $item [ 'priority' ];
}, $this -> items );
case self :: EXTR_DATA :
default :
return array_map ( function ( $item ) {
return $item [ 'data' ];
}, $this -> items );
}
}
/**
* Specify the internal queue class
*
* Please see { @ link getIterator ()} for details on the necessity of an
* internal queue class . The class provided should extend SplPriorityQueue .
*
* @ param string $class
* @ return PriorityQueue
*/
public function setInternalQueueClass ( $class )
{
$this -> queueClass = ( string ) $class ;
return $this ;
}
/**
* Does the queue contain the given datum ?
*
* @ param mixed $datum
* @ return bool
*/
public function contains ( $datum )
{
foreach ( $this -> items as $item ) {
if ( $item [ 'data' ] === $datum ) {
return true ;
}
}
return false ;
}
/**
* Does the queue have an item with the given priority ?
*
* @ param int $priority
* @ return bool
*/
public function hasPriority ( $priority )
{
foreach ( $this -> items as $item ) {
if ( $item [ 'priority' ] === $priority ) {
return true ;
}
}
return false ;
}
/**
* Get the inner priority queue instance
*
* @ throws Exception\DomainException
* @ return SplPriorityQueue
*/
protected function getQueue ()
{
if ( null === $this -> queue ) {
$this -> queue = new $this -> queueClass ();
if ( ! $this -> queue instanceof \SplPriorityQueue ) {
throw new Exception\DomainException ( sprintf (
'PriorityQueue expects an internal queue of type SplPriorityQueue; received "%s"' ,
get_class ( $this -> queue )
));
}
}
return $this -> queue ;
}
/**
* Add support for deep cloning
*
* @ return void
*/
public function __clone ()
{
if ( null !== $this -> queue ) {
$this -> queue = clone $this -> queue ;
}
}
}