Reputation: 5201
I have an SplObjectStorage instance that stores element objects to be rendered in a container. I would like the ability to efficiently add and remove objects from any random position in the store.
Example:
<?php
$store = new SplObjectStorageWrapper;
$obj1 = new Obj;
$obj2 = new Obj;
$obj3 = new Obj;
$store->attach($obj1);
$store->attach($obj2);
$store->insertAtIndex($obj3, 1);
//Storage should now be organized as $obj1, $obj3, $obj2
How would I go about implementing the insertAtIndex
method? Do I use a LimitIterator
to detach and reattach children after a certain position? Using an array-based object storage has proven to be much slower than an SplObjectStorage
instance.
Other methods that I would like to implement include removeAtIndex(integer)
and indexOf(object)
Upvotes: 4
Views: 1173
Reputation: 5201
It turns out the easiest (and apparently most efficient) way to do this is to extend SplObjectStorage
and to make use of LimitIterator
. Code example below:
<?php
/**
* Extends the SplObjectStorage class to provide index functions
*/
class ObjectStorage extends SplObjectStorage {
/**
* Returns the index of a given object, or false if not found
* @param object $object
*/
function indexOf($object){
if(!$this->contains($object)) return false;
foreach($this as $index => $obj) if($obj === $object) return $index;
}
/**
* Returns the object at the given index
*/
function itemAtIndex($index){
$it = new LimitIterator($this, $index, 1);
foreach($it as $obj) return $obj;
}
/**
* Returns the sequence of objects as specified by the offset and length
* @param int $offset
* @param int $length
*/
function slice($offset, $length){
$out = array();
$it = new LimitIterator($this, $offset, $length);
foreach($it as $obj) $out[] = $obj;
return $out;
}
/**
* Inserts an object (or an array of objects) at a certain point
* @param mixed $object A single object or an array of objects
* @param integer $index
*/
function insertAt($object, $index){
if(!is_array($object)) $object = array($object);
//Check to ensure that objects don't already exist in the collection
foreach($object as $k => $obj):
if($this->contains($obj)) unset($object[$k]);
endforeach;
//Do we have any objects left?
if(!$object) return;
//Detach any objects at or past this index
$remaining = array();
if($index < $this->count()):
$remaining = $this->slice($index, $this->count() - $index);
foreach($remaining as $obj) $this->detach($obj);
endif;
//Add the new objects we're splicing in
foreach($object as $obj) $this->attach($obj);
//Attach the objects we previously detached
foreach($remaining as $obj) $this->attach($obj);
}
/**
* Removes the object at the given index
* @param integer $index
*/
function removeAt($index){
$this->detach($this->itemAtIndex($index));
}
}
Upvotes: 3