From 20de5cc0913c56cb6519072cb8b99fe6d776efce Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Wed, 10 Aug 2005 21:56:01 +0000 Subject: [PATCH] - 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. --- ext/spl/internal/splobjectstorage.inc | 118 +++++++++++++++ ext/spl/spl_observer.c | 189 +++++++++++++++++++++++- ext/spl/tests/observer_002.phpt | 199 ++++++++++++++++++++++++++ 3 files changed, 504 insertions(+), 2 deletions(-) create mode 100755 ext/spl/internal/splobjectstorage.inc create mode 100755 ext/spl/tests/observer_002.phpt diff --git a/ext/spl/internal/splobjectstorage.inc b/ext/spl/internal/splobjectstorage.inc new file mode 100755 index 0000000000..bb4ca58ee0 --- /dev/null +++ b/ext/spl/internal/splobjectstorage.inc @@ -0,0 +1,118 @@ +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 diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index 51baca8c15..9975032e65 100755 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -32,6 +32,8 @@ #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); @@ -65,14 +67,197 @@ static zend_function_entry spl_funcs_Subject[] = { {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; } diff --git a/ext/spl/tests/observer_002.phpt b/ext/spl/tests/observer_002.phpt new file mode 100755 index 0000000000..3d2d930a1d --- /dev/null +++ b/ext/spl/tests/observer_002.phpt @@ -0,0 +1,199 @@ +--TEST-- +SPL: SplObjectStorage +--FILE-- +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=== + +--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=== -- 2.40.0