--- /dev/null
+<?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;
+ }
+ }
+ }
+ }
+}
+
+?>
\ No newline at end of file
#include "spl_functions.h"
#include "spl_engine.h"
#include "spl_observer.h"
+#include "spl_iterators.h"
+#include "spl_array.h"
SPL_METHOD(Observer, update);
SPL_METHOD(Subject, attach);
{NULL, NULL, NULL}
};
-PHPAPI zend_class_entry *spl_ce_Observer;
-PHPAPI zend_class_entry *spl_ce_Subject;
+PHPAPI zend_class_entry *spl_ce_Observer;
+PHPAPI zend_class_entry *spl_ce_Subject;
+PHPAPI zend_class_entry *spl_ce_SplObjectStorage;
+PHPAPI zend_object_handlers spl_handler_SplObjectStorage;
+
+typedef struct _spl_SplObjectStorage {
+ zend_object std;
+ HashTable storage;
+ long index;
+ HashPosition pos;
+} spl_SplObjectStorage;
+
+/* storage is an assoc aray of [zend_object_value]=>[zval*] */
+
+void spl_SplOjectStorage_free_storage(void *object TSRMLS_DC) /* {{{ */
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage *)object;
+
+ zend_hash_destroy(intern->std.properties);
+ FREE_HASHTABLE(intern->std.properties);
+
+ zend_hash_destroy(&intern->storage);
+
+ efree(object);
+} /* }}} */
+
+static zend_object_value spl_object_storage_new_ex(zend_class_entry *class_type, spl_SplObjectStorage **obj, zval *orig TSRMLS_DC) /* {{{ */
+{
+ zend_object_value retval;
+ spl_SplObjectStorage *intern;
+ zval *tmp;
+
+ intern = emalloc(sizeof(spl_SplObjectStorage));
+ memset(intern, 0, sizeof(spl_SplObjectStorage));
+ intern->std.ce = class_type;
+ *obj = intern;
+
+ ALLOC_HASHTABLE(intern->std.properties);
+ zend_hash_init(intern->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
+
+ zend_hash_init(&intern->storage, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+ retval.handle = zend_objects_store_put(intern, NULL, (zend_objects_free_object_storage_t) spl_SplOjectStorage_free_storage, NULL TSRMLS_CC);
+ retval.handlers = &spl_handler_SplObjectStorage;
+ return retval;
+}
+/* }}} */
+
+/* {{{ spl_array_object_new */
+static zend_object_value spl_SplObjectStorage_new(zend_class_entry *class_type TSRMLS_DC)
+{
+ spl_SplObjectStorage *tmp;
+ return spl_object_storage_new_ex(class_type, &tmp, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto void SplObjectStorage::attach($obj)
+ Attaches an object to the storage if not yet contained */
+SPL_METHOD(SplObjectStorage, attach)
+{
+ zval *obj;
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+ return;
+ }
+
+ zend_hash_update(&intern->storage, (char*)&obj->value.obj, sizeof(obj->value.obj), &obj, sizeof(zval**), NULL);
+ obj->refcount++;
+} /* }}} */
+
+/* {{{ proto void SplObjectStorage::detach($obj)
+ Detaches an object from the storage */
+SPL_METHOD(SplObjectStorage, detach)
+{
+ zval *obj;
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+ return;
+ }
+
+ zend_hash_del(&intern->storage, (char*)&obj->value.obj, sizeof(obj->value.obj));
+ zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+ intern->index = 0;
+} /* }}} */
+
+/* {{{ proto bool SplObjectStorage::contains($obj)
+ Determine whethe an object is contained in the storage */
+SPL_METHOD(SplObjectStorage, contains)
+{
+ zval *obj;
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+ return;
+ }
+
+ RETURN_BOOL(zend_hash_exists(&intern->storage, (char*)&obj->value.obj, sizeof(obj->value.obj)));
+} /* }}} */
+
+/* {{{ proto int SplObjectStorage::count()
+ Determine number of objects in storage */
+SPL_METHOD(SplObjectStorage, count)
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ RETURN_LONG(zend_hash_num_elements(&intern->storage));
+} /* }}} */
+
+/* {{{ proto void SplObjectStorage::rewind()
+ */
+SPL_METHOD(SplObjectStorage, rewind)
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ zend_hash_internal_pointer_reset_ex(&intern->storage, &intern->pos);
+ intern->index = 0;
+} /* }}} */
+
+/* {{{ proto bool SplObjectStorage::valid()
+ */
+SPL_METHOD(SplObjectStorage, valid)
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ RETURN_BOOL(zend_hash_has_more_elements_ex(&intern->storage, &intern->pos) == SUCCESS);
+} /* }}} */
+
+/* {{{ proto mixed SplObjectStorage::key()
+ */
+SPL_METHOD(SplObjectStorage, key)
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ RETURN_LONG(intern->index);
+} /* }}} */
+
+/* {{{ proto mixed SplObjectStorage::current()
+ */
+SPL_METHOD(SplObjectStorage, current)
+{
+ zval **entry;
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (zend_hash_get_current_data_ex(&intern->storage, (void**)&entry, &intern->pos) == FAILURE) {
+ return;
+ }
+ RETVAL_ZVAL(*entry, 1, 0);
+} /* }}} */
+
+/* {{{ proto void SplObjectStorage::next()
+ */
+SPL_METHOD(SplObjectStorage, next)
+{
+ spl_SplObjectStorage *intern = (spl_SplObjectStorage*)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ zend_hash_move_forward_ex(&intern->storage, &intern->pos);
+ intern->index++;
+} /* }}} */
+
+static
+ZEND_BEGIN_ARG_INFO(arginfo_Object, 0)
+ ZEND_ARG_INFO(0, object 0)
+ZEND_END_ARG_INFO();
+
+static zend_function_entry spl_funcs_SplObjectStorage[] = {
+ SPL_ME(SplObjectStorage, attach, arginfo_Object, 0)
+ SPL_ME(SplObjectStorage, detach, arginfo_Object, 0)
+ SPL_ME(SplObjectStorage, contains, arginfo_Object, 0)
+ SPL_ME(SplObjectStorage, count, NULL, 0)
+ SPL_ME(SplObjectStorage, rewind, NULL, 0)
+ SPL_ME(SplObjectStorage, valid, NULL, 0)
+ SPL_ME(SplObjectStorage, key, NULL, 0)
+ SPL_ME(SplObjectStorage, current, NULL, 0)
+ SPL_ME(SplObjectStorage, next, NULL, 0)
+ {NULL, NULL, NULL}
+};
/* {{{ PHP_MINIT_FUNCTION(spl_observer) */
PHP_MINIT_FUNCTION(spl_observer)
{
REGISTER_SPL_INTERFACE(Observer);
REGISTER_SPL_INTERFACE(Subject);
+
+ REGISTER_SPL_STD_CLASS_EX(SplObjectStorage, spl_SplObjectStorage_new, spl_funcs_SplObjectStorage);
+ memcpy(&spl_handler_SplObjectStorage, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+
+ REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Countable);
+ REGISTER_SPL_IMPLEMENTS(SplObjectStorage, Iterator);
return SUCCESS;
}
--- /dev/null
+--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===