(PHP 5 >= 5.3.0)
The SplObjectStorage class provides a map from objects to data or, by ignoring data, an object set. This dual purpose can be useful in many cases involving the need to uniquely identify objects.
Example #1 SplObjectStorage as a set
<?php
// As an object set
$s = new SplObjectStorage();
$o1 = new StdClass;
$o2 = new StdClass;
$o3 = new StdClass;
$s->attach($o1);
$s->attach($o2);
var_dump($s->contains($o1));
var_dump($s->contains($o2));
var_dump($s->contains($o3));
$s->detach($o2);
var_dump($s->contains($o1));
var_dump($s->contains($o2));
var_dump($s->contains($o3));
?>
以上例程会输出:
bool(true) bool(true) bool(false) bool(true) bool(false) bool(false)
Example #2 SplObjectStorage as a map
<?php
// As a map from objects to data
$s = new SplObjectStorage();
$o1 = new StdClass;
$o2 = new StdClass;
$o3 = new StdClass;
$s[$o1] = "data for object 1";
$s[$o2] = array(1,2,3);
if (isset($s[$o2])) {
var_dump($s[$o2]);
}
?>
以上例程会输出:
array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
inwebo at gmail dot fr (2012-02-28 23:01:00)
I needed to merge SplObjectStorages.
<?php
// As an object set
$SplObjectStorage_1 = new SplObjectStorage();
$object1 = new StdClass;
$object1->attr = 'obj 1';
$object2 = new StdClass;
$object2->attr = 'obj 2';
$object3 = new StdClass;
$object3->attr = 'obj 3';
$SplObjectStorage_1->attach($object1);
$SplObjectStorage_1->attach($object2);
$SplObjectStorage_1->attach($object3);
// Another one object set
$SplObjectStorage_2 = new SplObjectStorage();
$object4 = new StdClass;
$object4->attr = 'obj 4';
$object5 = new StdClass;
$object5->attr = 'obj 5';
$object6 = new StdClass;
$object6->attr = 'obj 6';
$SplObjectStorage_2->attach($object4);
$SplObjectStorage_2->attach($object5);
$SplObjectStorage_2->attach($object6);
/**
* Merge SplObjectStorage
*
* @param how many SplObjectStorage params as you want
* @return SplObjectStorage
*/
function mergeSplObjectStorage() {
$buffer = new SplObjectStorage();
if( func_num_args() > 0 ) {
$args = func_get_args();
foreach ($args as $objectStorage) {
foreach($objectStorage as $object) {
if(is_object( $object ) ) {
$buffer->attach($object);
}
}
}
}
else{
return FALSE;
}
return $buffer;
}
$merge = mergeSplObjectStorage($SplObjectStorage_1, $SplObjectStorage_2);
?>
<?php
echo $merge->count();
?>
Will output :
6
<?php
$merge->rewind();
while($merge->valid()) {
$object = $merge->current();
var_dump($object);
$merge->next();
}
?>
Will ouput :
object(stdClass)#2 (1) {
["attr"]=>
string(5) "obj 1"
}
object(stdClass)#3 (1) {
["attr"]=>
string(5) "obj 2"
}
object(stdClass)#4 (1) {
["attr"]=>
string(5) "obj 3"
}
object(stdClass)#6 (1) {
["attr"]=>
string(5) "obj 4"
}
object(stdClass)#7 (1) {
["attr"]=>
string(5) "obj 5"
}
object(stdClass)#8 (1) {
["attr"]=>
string(5) "obj 6"
}
My two cents.
Hayley Watson (2012-02-04 12:16:05)
As an iterator, SplObjectStorage traverses the _keys_ of the map (the $o1, $o2 of Example 2) rather than the values. To retrieve each of those in turn requires an additional lookup:
<?php
// As a map from objects to data
$s = new SplObjectStorage();
$o1 = (object)array('a'=>1);
$o2 = (object)array('b'=>2);
$o3 = (object)array('c'=>3);
$s[$o1] = "data for object 1";
$s[$o2] = array(1,2,3);
foreach($s as $i => $key)
{
echo "Entry $i:\n"; // You get a numeric index
var_dump($key, $s[$key]);
echo "\n";
}
?>
This makes sense when you use an SplObjectStorage as a set, since in that case all of the "values" are null.
Jan Walther (2011-06-30 06:57:03)
I rewrote some scripts and changed object storage with arrays to SplObjectStorage. At some point I needed support of array_rand() but I did not find a function to return a random attached object of an SplObjectStorage object.
So here is my solution for random access to SplObjectStorage:
<?php
$o1 = new StdClass;
$o2 = new StdClass;
$s = new SplObjectStorage;
$s->attach($o1);
$s->attach($o2);
$random = rand(0,$s->count()-1);
$s->rewind();
for($i=0;$i<$random;$i++) {
$s->next();
}
var_dump($s->current());
?>
Robertas at pobox com (2010-01-31 11:13:53)
PHP 5.2.x and lower doesn't implement ArrayAccess in SplObjectStorage - it is only implemented starting from PHP 5.3
randallgirard at hotmail dot com (2009-12-14 12:01:35)
I have two things to note about SplObjectStorage:
#1: A reference to the object itself is stored (not just a hash to compare against the object) and it must be removed before the object is destroyed and the destructor is executed.
#2: SplObjectStorage::rewind() MUST be called to initiate the iterator and before SplObjectStorage::current() will return an object (and I think the only way to retrieve an object?) rather than automatically starting at the first element as I expected it to, like an array for example. This assumption is based on SplObjectStorage::current() returning NULL until SplObjectStorage::rewind() is called once the objects are contained. As a note, always use REWIND before iterating through or fetching objects.
<?php
class foo {
public function __destruct() {
print("--- DESTRUCTOR FIRED!!<br />\r\n");
}
}
# Create object and storage
$bar = new foo();
$s = new SplObjectStorage();
# Rewind early just as a test
$s->rewind();
# attach the object
$s->attach($bar, array('test'));
# Unset the object; destructor does NOT fire
unset($bar);
print("Object has been unset<br />\r\n");
# First demonstrate that REWIND must be called to initialize the iterator
$obj = $s->current();
var_dump($obj);
print("- Note the NULL (from \$s->current())<br />\r\n");
# Initialize, and then detach the current (and only) object
$s->rewind();
$s->detach( $s->current() );
# The destructor should NOW execute
?>
Output:
Object has been unset
NULL - Note the NULL (from $s->current())
--- DESTRUCTOR FIRED!!