2024 日本 PHP 研討會

SplObjectStorage 類別

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

簡介

SplObjectStorage 類別提供從物件到資料的映射,或者透過忽略資料來提供物件集合。這種雙重用途在許多需要唯一識別物件的情況下非常有用。

類別概要

類別 SplObjectStorage 實作 Countable, SeekableIterator, Serializable, ArrayAccess {
/* 方法 */
公開 addAll(SplObjectStorage $storage): int
公開 附加(物件 $object, 混合 $info = 空值):
公開 包含(物件 $object): 布林值
公開 計數(整數 $mode = COUNT_NORMAL): 整數
公開 目前(): 物件
公開 分離(物件 $object):
公開 取得雜湊值(物件 $object): 字串
公開 鍵值(): 整數
公開 下一個():
公開 取得偏移量(物件 $object): 混合
公開 設定偏移量(物件 $object, 混合 $info = 空值):
公開 offsetUnset(物件 $object):
公開 rewind():
公開 seek(整數 $offset):
公開 serialize(): 字串
公開 setInfo(混合 $info):
公開 unserialize(字串 $data):
公開 valid(): 布林值
}

範例

範例 #1 SplObjectStorage 作為集合

<?php
// 作為一個物件集合
$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)

範例 #2 以映射 (Map) 方式使用 SplObjectStorage

<?php
// 以物件到資料的映射
$s = new SplObjectStorage();

$o1 = new stdClass;
$o2 = new stdClass;
$o3 = new stdClass;

$s[$o1] = "物件 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)
}

更新日誌

版本 說明
8.4.0 實作 SeekableIterator 介面,先前只有實作 Iterator 介面。

目錄

新增註記

使用者貢獻的註記 11 則註記

Adam Monsen
10 年前
請注意 SplObjectStorage 中一些不一致/令人驚訝的行為,以保持向下相容性。您無法使用鍵/值語法正確地使用 foreach。

<?php
$spl
= new SplObjectStorage ();
$keyForA = new StdClass();
$keyForB = new StdClass();
$spl[$keyForA] = 'value a';
$spl[$keyForB] = 'value b';
foreach (
$spl as $key => $value)
{
// $key 不是物件,$value 是!
// 必須使用標準陣列存取來獲取字串。
echo $spl[$value] . "\n"; // 輸出 "value a",然後 "value b"
}
// 使用這種形式的 foreach 可能更清楚:
foreach ($spl as $key)
{
// $key 是一個物件。
// 使用標準陣列存取來獲取值。
echo $spl[$key] . "\n"; // 輸出 "value a",然後 "value b"
}
?>

參見 https://bugs.php.net/bug.php?id=49967
rafal at pawlukiewicz dot com
5 年前
SplObjectStorage 類別可以很好地用於觀察者模式,例如

<?php
類別 Subject 實作 \SplSubject
{
私有
$observers;

公開函式
__construct()
{
$this->observers = new \SplObjectStorage;
}

公開函式
attach(\SplObserver $observer)
{
$this->observers->attach($observer);
}

公開函式
detach(\SplObserver $observer)
{
$this->observers->detach($observer);
}

公開函式
notify()
{
foreach (
$this->observers as $observer) {
$observer->update($this);
}
}
}
?>
frame86 at live dot com
8 年前
請記住,foreach() 會在迭代之前複製你的陣列,而 SplObjectStorage 不會。如果你在迭代中有一個子呼叫,它也再次在物件儲存上呼叫 foreach(),迭代器位置會衝突!

為了安全起見,請使用
<?php
foreach(clone $myStorage as $obj) {

}
?>
m dot drewek at smf dot de
9 年前
請注意,SplObjectStorage 在 5.4.0 引入了一個錯誤,會破壞覆寫 getHash() 的衍生類別之複製實例中的物件查找。

這是一個已確認的錯誤: https://bugs.php.net/bug.php?id=67582

範例
<?php
類別 MyObjectStorage 繼承 SplObjectStorage {
// 覆寫 getHash(),只用一些(可行的)測試方法
公開函式 getHash($object) { return get_class($object); }
}

類別
TestObject {}

$list = new MyObjectStorage(); // 如果使用 "new SplObjectStorage()" 就沒有問題
$list->attach(new TestObject());

foreach(
$list as $x) var_dump($list->offsetExists($x)); // TRUE

$list2 = clone $list;
foreach(
$list2 as $x) var_dump($list2->offsetExists($x)); // FALSE
?>
grzeniufication
7 年前
<?php
/**
* 對於簡單的使用案例(您想將物件保存在映射中)
* 我建議堅持使用普通的舊陣列。只需使用物件
* 雜湊作為陣列鍵。
*/
$entity1 = new stdClass();
$entity2 = new stdClass();
$entities = [];
$entities[spl_object_hash($entity1)] = $entity1;
$entities[spl_object_hash($entity2)] = $entity2;

// 物件雜湊難以區分,因此您可以對其執行雜湊函數
// 以提高可讀性。
$entities[md5(spl_object_hash($entity1))] = $entity1;
$entities[md5(spl_object_hash($entity2))] = $entity2;

print_r($entities);
kris dot lamote at it-kitchen dot be
9 年前
對於任何在垃圾回收後 SplObjectStorage 包含損壞成員變數的人(序列化後出現 FatalErrorException):我們使用以下修復方法,效果很好
<?php

class FixedSplObjectStorage extends SplObjectStorage
{

public function
serialize()
{
$goodPortion = 'N;;m:a:0:{}';
$startKey = 'N;;m:a:';

$serialized = parent::serialize();

$startPos = strpos($serialized, $startKey);

if (
$startPos !== false) {
$serialized = substr_replace($serialized, $goodPortion, $startPos, -1);

}

return
$serialized;

}
}

?>
inwebo at gmail dot fr
12 年前
我需要合併 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();
}
?>
將輸出
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"
}

我的淺見。
Marius
10 年前
在使用 foreach 遍歷 SplObjectStorage 中的項目時,請勿使用 SplObjectStorage::detach,因為這會跳過第二個(且僅跳過第二個)元素。

範例

<?php

class A {
public
$i;
public function
__construct($i) {
$this->i = $i;
}
}

$container = new \SplObjectStorage();

$container->attach(new A(1));
$container->attach(new A(2));
$container->attach(new A(3));
$container->attach(new A(4));
$container->attach(new A(5));

foreach (
$container as $item) {
echo
$item->i . "\n";
$container->detach($item);
}
echo
"== 儲存器中剩餘的項目 ==\n";
foreach (
$container as $item) {
echo
$item->i . "\n";
}
/* 輸出:
1
3
4
5
== 儲存器中剩餘的項目 ==
2
*/
?>
jan at odvarko dot cz
3 年前
如果您在 SplObjectStorage 中將 array() 指派給一個物件,然後嘗試修改它的個別元素,您可能會發現它無法運作。
您可以改用 ArrayObject(),它會模擬陣列行為。

<?php

$storage
= new SplObjectStorage();

$obj1 = new StdClass();
$obj2 = new StdClass();

$storage[$obj1] = array();
$storage[$obj2] = new ArrayObject();

$storage[$obj1]['person'] = 'Jana'; // 無法作用 (PHP 通知:間接修改 SplObjectStorage 的重載元素無效)
$storage[$obj2]['person'] = 'Jana'; // 可以作用

var_dump($storage[$obj1]['person']); // NULL (PHP 通知:未定義索引:person)
var_dump($storage[$obj2]['person']); // string(4) "Jana"

?>
divinity76 at gmail dot com
7 年前
如果您正在尋找 ResourceStorage,請查看 https://gist.github.com/divinity76/b8041e073b74bdeab562a075fc94217f

(我需要它來用 socket_select() 進行 Socket 程式設計)
Jan Walther
13 年前
我重寫了一些腳本,並將使用陣列的物件儲存更改為 SplObjectStorage。在某些時候,我需要 array_rand() 的支援,但我沒有找到一個函式來返回 SplObjectStorage 物件的隨機附加物件。

所以這是我的解決方案,用於隨機存取 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());
?>
To Top