4 changed files with 434 additions and 0 deletions
-
55ext/spl/examples/recursivearrayiterator.inc
-
62ext/spl/internal/recursivefilteriterator.inc
-
118ext/spl/internal/splobjectstorage.inc
-
199ext/spl/tests/observer_002.phpt
@ -0,0 +1,55 @@ |
|||
<?php |
|||
|
|||
/** @file recursivearrayiterator.inc |
|||
* @ingroup Examples |
|||
* @brief class RecursiveArrayIterator |
|||
* @author Marcus Boerger |
|||
* @date 2003 - 2005 |
|||
* |
|||
* SPL - Standard PHP Library |
|||
*/ |
|||
|
|||
/** @ingroup Examples |
|||
* @brief A recursive array iterator |
|||
* @author Marcus Boerger |
|||
* @version 1.0 |
|||
* @since PHP 6.0 |
|||
* |
|||
* Passes the RecursiveIterator interface to the inner Iterator and provides |
|||
* the same functionality as FilterIterator. This allows you to skip parents |
|||
* and all their childs before loading them all. You need to care about |
|||
* function getChildren() because it may not always suit your needs. The |
|||
* builtin behavior uses reflection to return a new instance of the exact same |
|||
* class it is called from. That is you extend RecursiveFilterIterator and |
|||
* getChildren() will create instance of that class. The problem is that doing |
|||
* this does not transport any state or control information of your accept() |
|||
* implementation to the new instance. To overcome this problem you might |
|||
* need to overwrite getChildren(), call this implementation and pass the |
|||
* control vaules manually. |
|||
*/ |
|||
class RecursiveArrayIterator extends ArrayIterator implements RecursiveIterator |
|||
{ |
|||
/** @return whether the current element has children |
|||
*/ |
|||
function hasChildren() |
|||
{ |
|||
return is_array($this->current()); |
|||
} |
|||
|
|||
/** @return an iterator for the current elements children |
|||
* |
|||
* @note the returned iterator will be of the same class as $this |
|||
*/ |
|||
function getChildren() |
|||
{ |
|||
if (empty($this->ref)) |
|||
{ |
|||
$this->ref = new ReflectionClass($this); |
|||
} |
|||
return $this->ref->newInstance($this->current()); |
|||
} |
|||
|
|||
private $ref; |
|||
} |
|||
|
|||
?> |
|||
@ -0,0 +1,62 @@ |
|||
<?php |
|||
|
|||
/** @file recursivefilteriterator.inc |
|||
* @ingroup SPL |
|||
* @brief class RecursiveFilterIterator |
|||
* @author Marcus Boerger |
|||
* @date 2003 - 2005 |
|||
* |
|||
* SPL - Standard PHP Library |
|||
*/ |
|||
|
|||
/** @ingroup SPL |
|||
* @brief Iterator to filter recursive iterators |
|||
* @author Marcus Boerger |
|||
* @version 1.0 |
|||
* @since PHP 5.1 |
|||
* |
|||
* Passes the RecursiveIterator interface to the inner Iterator and provides |
|||
* the same functionality as FilterIterator. This allows you to skip parents |
|||
* and all their childs before loading them all. You need to care about |
|||
* function getChildren() because it may not always suit your needs. The |
|||
* builtin behavior uses reflection to return a new instance of the exact same |
|||
* class it is called from. That is you extend RecursiveFilterIterator and |
|||
* getChildren() will create instance of that class. The problem is that doing |
|||
* this does not transport any state or control information of your accept() |
|||
* implementation to the new instance. To overcome this problem you might |
|||
* need to overwrite getChildren(), call this implementation and pass the |
|||
* control vaules manually. |
|||
*/ |
|||
abstract class RecursiveFilterIterator extends FilterIterator implements RecursiveIterator |
|||
{ |
|||
/** @param $it the RecursiveIterator to filter |
|||
*/ |
|||
function __construct(RecursiveIterator $it) |
|||
{ |
|||
parent::__construct($it); |
|||
} |
|||
|
|||
/** @return whether the current element has children |
|||
*/ |
|||
function hasChildren() |
|||
{ |
|||
return $this->getInnerIterator()->hasChildren(); |
|||
} |
|||
|
|||
/** @return an iterator for the current elements children |
|||
* |
|||
* @note the returned iterator will be of the same class as $this |
|||
*/ |
|||
function getChildren() |
|||
{ |
|||
if (empty($this->ref)) |
|||
{ |
|||
$this->ref = new ReflectionClass($this); |
|||
} |
|||
return $this->ref->newInstance($this->getInnerIterator()->getChildren()); |
|||
} |
|||
|
|||
private $ref; |
|||
} |
|||
|
|||
?> |
|||
@ -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); |
|||
} |
|||
|
|||
/** @param 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 SplObserver |
|||
{ |
|||
protected $name = ''; |
|||
|
|||
function __construct($name = 'obj') |
|||
{ |
|||
$this->name = '$' . $name; |
|||
} |
|||
|
|||
function update(SplSubject $subject) |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . '(' . $subject->getName() . ");\n"; |
|||
} |
|||
|
|||
function getName() |
|||
{ |
|||
return $this->name; |
|||
} |
|||
} |
|||
|
|||
class SubjectImpl implements SplSubject |
|||
{ |
|||
protected $name = ''; |
|||
protected $observers; |
|||
|
|||
function __construct($name = 'sub') |
|||
{ |
|||
$this->observers = new MyObjectStorage; |
|||
$this->name = '$' . $name; |
|||
} |
|||
|
|||
function attach(SplObserver $observer) |
|||
{ |
|||
echo $this->name . '->' . __METHOD__ . '(' . $observer->getName() . ");\n"; |
|||
$this->observers->attach($observer); |
|||
} |
|||
|
|||
function detach(SplObserver $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