Browse Source
- Implement SplObjectStorage as announced during OSCON
- Implement SplObjectStorage as announced during OSCON
# This class starts naming of new classes in Spl by prefix Spl as dicussed. # The class reduces object storage complexity from O(n) to O(1) which is # not possible in user space.migration/RELEASE_1_0_0
3 changed files with 504 additions and 2 deletions
-
118ext/spl/internal/splobjectstorage.inc
-
189ext/spl/spl_observer.c
-
199ext/spl/tests/observer_002.phpt
@ -0,0 +1,118 @@ |
|||
<?php |
|||
|
|||
/** @file splobjectstorage.inc |
|||
* @ingroup SPL |
|||
* @brief class SplObjectStorage |
|||
* @author Marcus Boerger |
|||
* @date 2003 - 2005 |
|||
* |
|||
* SPL - Standard PHP Library |
|||
*/ |
|||
|
|||
/** |
|||
* @brief Object storage |
|||
* @author Marcus Boerger |
|||
* @version 1.0 |
|||
* @since PHP 6.0 |
|||
* |
|||
* This container allows to store objects uniquly without the need to compare |
|||
* them one by one. This is only possible internally. The code represenation |
|||
* here therefore has a complexity of O(n) while the actual implementation has |
|||
* complexity O(1). |
|||
*/ |
|||
class SplObjectStorage implements Iterator, Countable |
|||
{ |
|||
private $storage = array(); |
|||
private $index = 0; |
|||
|
|||
/** Rewind to top iterator as set in constructor |
|||
*/ |
|||
function rewind() |
|||
{ |
|||
rewind($this->storage); |
|||
} |
|||
|
|||
/** @return whether iterator is valid |
|||
*/ |
|||
function valid() |
|||
{ |
|||
return key($this->storage) !== false; |
|||
} |
|||
|
|||
/** @return current key |
|||
*/ |
|||
function key() |
|||
{ |
|||
return $this->index; |
|||
} |
|||
|
|||
/** @return current object |
|||
*/ |
|||
function current() |
|||
{ |
|||
return current($this->storage); |
|||
} |
|||
|
|||
/** Forward to next element |
|||
*/ |
|||
function next() |
|||
{ |
|||
next($this->storage); |
|||
$this->index++; |
|||
} |
|||
|
|||
/** @return number of objects in storage |
|||
*/ |
|||
function count() |
|||
{ |
|||
return count($this->storage); |
|||
} |
|||
|
|||
/** @obj object to look for |
|||
* @return whether $obj is contained in storage |
|||
*/ |
|||
function contains($obj) |
|||
{ |
|||
if (is_object($obj)) |
|||
{ |
|||
foreach($this->storage as $object) |
|||
{ |
|||
if ($object === $obj) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** @param $obj new object to attach to storage if not yet contained |
|||
*/ |
|||
function attach($obj) |
|||
{ |
|||
if (is_object($obj) && !$this->contains($obj)) |
|||
{ |
|||
$this->storage[] = $obj; |
|||
} |
|||
} |
|||
|
|||
/** @param $obj object to remove from storage |
|||
*/ |
|||
function detach($obj) |
|||
{ |
|||
if (is_object($obj)) |
|||
{ |
|||
foreach($this->storage as $idx => $object) |
|||
{ |
|||
if ($object === $obj) |
|||
{ |
|||
unset($this->storage[$idx]); |
|||
$this->rewind(); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
?> |
|||
@ -0,0 +1,199 @@ |
|||
--TEST-- |
|||
SPL: SplObjectStorage |
|||
--FILE-- |
|||
<?php |
|||
|
|||
class MyObjectStorage extends SplObjectStorage |
|||
{ |
|||
function rewind() |
|||
{ |
|||
echo __METHOD__ . "()\n"; |
|||
parent::rewind(); |
|||
} |
|||
|
|||
function valid() |
|||
{ |
|||
echo __METHOD__ . "(" . (parent::valid() ? 1 : 0) . ")\n"; |
|||
return parent::valid(); |
|||
} |
|||
|
|||
function key() |
|||
{ |
|||
echo __METHOD__ . "(" . parent::key() . ")\n"; |
|||
return parent::key(); |
|||
} |
|||
|
|||
function current() |
|||
{ |
|||
echo __METHOD__ . "(" . parent::current()->getName() . ")\n"; |
|||
return parent::current(); |
|||
} |
|||
|
|||
function next() |
|||
{ |
|||
echo __METHOD__ . "()\n"; |
|||
parent::next(); |
|||
} |
|||
} |
|||
|
|||
class ObserverImpl implements Observer |
|||
{ |
|||
protected $name = ''; |
|||
|
|||
function __construct($name = 'obj') |
|||
{ |
|||
$this->name = '$' . $name; |
|||
} |
|||
|
|||
function update(Subject $subject) |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . '(' . $subject->getName() . ");\n"; |
|||
} |
|||
|
|||
function getName() |
|||
{ |
|||
return $this->name; |
|||
} |
|||
} |
|||
|
|||
class SubjectImpl implements Subject |
|||
{ |
|||
protected $name = ''; |
|||
protected $observers; |
|||
|
|||
function __construct($name = 'sub') |
|||
{ |
|||
$this->observers = new MyObjectStorage; |
|||
$this->name = '$' . $name; |
|||
} |
|||
|
|||
function attach(Observer $observer) |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . '(' . $observer->getName() . ");\n"; |
|||
$this->observers->attach($observer); |
|||
} |
|||
|
|||
function detach(Observer $observer) |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . '(' . $observer->getName() . ");\n"; |
|||
$this->observers->detach($observer); |
|||
} |
|||
|
|||
function count() |
|||
{ |
|||
return $this->observers->count(); |
|||
} |
|||
|
|||
function notify() |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . "();\n"; |
|||
foreach($this->observers as $key => $observer) |
|||
{ |
|||
$observer->update($this); |
|||
} |
|||
} |
|||
|
|||
function getName() |
|||
{ |
|||
return $this->name; |
|||
} |
|||
|
|||
function contains($obj) |
|||
{ |
|||
return $this->observers->contains($obj); |
|||
} |
|||
} |
|||
|
|||
$sub = new SubjectImpl; |
|||
|
|||
$ob1 = new ObserverImpl("ob1"); |
|||
$ob2 = new ObserverImpl("ob2"); |
|||
$ob3 = new ObserverImpl("ob3"); |
|||
|
|||
var_dump($sub->contains($ob1)); |
|||
$sub->attach($ob1); |
|||
var_dump($sub->contains($ob1)); |
|||
$sub->attach($ob1); |
|||
$sub->attach($ob2); |
|||
$sub->attach($ob3); |
|||
var_dump($sub->count()); |
|||
|
|||
$sub->notify(); |
|||
|
|||
$sub->detach($ob3); |
|||
var_dump($sub->count()); |
|||
|
|||
$sub->notify(); |
|||
|
|||
$sub->detach($ob2); |
|||
$sub->detach($ob1); |
|||
var_dump($sub->count()); |
|||
|
|||
$sub->notify(); |
|||
|
|||
$sub->attach($ob3); |
|||
var_dump($sub->count()); |
|||
|
|||
$sub->notify(); |
|||
|
|||
?> |
|||
===DONE=== |
|||
<?php exit(0); ?> |
|||
--EXPECT-- |
|||
bool(false) |
|||
$sub->SubjectImpl::attach($ob1); |
|||
bool(true) |
|||
$sub->SubjectImpl::attach($ob1); |
|||
$sub->SubjectImpl::attach($ob2); |
|||
$sub->SubjectImpl::attach($ob3); |
|||
int(3) |
|||
$sub->SubjectImpl::notify(); |
|||
MyObjectStorage::rewind() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob1) |
|||
MyObjectStorage::key(0) |
|||
$ob1->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob2) |
|||
MyObjectStorage::key(1) |
|||
$ob2->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob3) |
|||
MyObjectStorage::key(2) |
|||
$ob3->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(0) |
|||
$sub->SubjectImpl::detach($ob3); |
|||
int(2) |
|||
$sub->SubjectImpl::notify(); |
|||
MyObjectStorage::rewind() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob1) |
|||
MyObjectStorage::key(0) |
|||
$ob1->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob2) |
|||
MyObjectStorage::key(1) |
|||
$ob2->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(0) |
|||
$sub->SubjectImpl::detach($ob2); |
|||
$sub->SubjectImpl::detach($ob1); |
|||
int(0) |
|||
$sub->SubjectImpl::notify(); |
|||
MyObjectStorage::rewind() |
|||
MyObjectStorage::valid(0) |
|||
$sub->SubjectImpl::attach($ob3); |
|||
int(1) |
|||
$sub->SubjectImpl::notify(); |
|||
MyObjectStorage::rewind() |
|||
MyObjectStorage::valid(1) |
|||
MyObjectStorage::current($ob3) |
|||
MyObjectStorage::key(0) |
|||
$ob3->ObserverImpl::update($sub); |
|||
MyObjectStorage::next() |
|||
MyObjectStorage::valid(0) |
|||
===DONE=== |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue