From 3fb1c65a41f6d934aa6e3568a5b94055a22d623e Mon Sep 17 00:00:00 2001 From: Marcus Boerger Date: Tue, 8 Feb 2005 20:42:48 +0000 Subject: [PATCH] - Initial Observer implementation --- ext/spl/config.m4 | 2 +- ext/spl/config.w32 | 2 +- ext/spl/php_spl.c | 4 ++ ext/spl/spl.php | 50 +++++++++++++- ext/spl/spl_observer.c | 88 ++++++++++++++++++++++++ ext/spl/spl_observer.h | 41 +++++++++++ ext/spl/tests/observer_001.phpt | 116 ++++++++++++++++++++++++++++++++ 7 files changed, 298 insertions(+), 5 deletions(-) create mode 100755 ext/spl/spl_observer.c create mode 100755 ext/spl/spl_observer.h create mode 100755 ext/spl/tests/observer_001.phpt diff --git a/ext/spl/config.m4 b/ext/spl/config.m4 index 4760a14203..eea8092166 100755 --- a/ext/spl/config.m4 +++ b/ext/spl/config.m4 @@ -9,6 +9,6 @@ if test "$PHP_SPL" != "no"; then AC_MSG_ERROR(Cannot build SPL as a shared module) fi AC_DEFINE(HAVE_SPL, 1, [Whether you want SPL (Standard PHP Library) support]) - PHP_NEW_EXTENSION(spl, php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_sxe.c spl_exceptions.c, $ext_shared) + PHP_NEW_EXTENSION(spl, php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_sxe.c spl_exceptions.c spl_observer.c, $ext_shared) PHP_ADD_EXTENSION_DEP(spl, simplexml) fi diff --git a/ext/spl/config.w32 b/ext/spl/config.w32 index 255a2b4eab..931a6704c7 100644 --- a/ext/spl/config.w32 +++ b/ext/spl/config.w32 @@ -7,6 +7,6 @@ if (PHP_SPL != "no") { if (PHP_SPL_SHARED) { ERROR("SPL cannot be compiled as a shared ext"); } - EXTENSION("spl", "php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_sxe.c spl_exceptions.c"); + EXTENSION("spl", "php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_sxe.c spl_exceptions.c spl_observer.c"); AC_DEFINE('HAVE_SPL', 1); } diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 2486f8d3bb..d6304635c7 100755 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -33,6 +33,7 @@ #include "spl_iterators.h" #include "spl_sxe.h" #include "spl_exceptions.h" +#include "spl_observer.h" #ifdef COMPILE_DL_SPL ZEND_GET_MODULE(spl) @@ -95,6 +96,7 @@ PHP_MINIT_FUNCTION(spl) PHP_MINIT(spl_directory)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_sxe)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU); + PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU); return SUCCESS; } @@ -181,6 +183,7 @@ PHP_FUNCTION(class_implements) SPL_ADD_CLASS(LimitIterator, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(LogicException, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(NoRewindIterator, z_list, sub, allow, ce_flags); \ + SPL_ADD_CLASS(Observer, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(OuterIterator, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(OutOfRangeException, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(OutOfBoundsException, z_list, sub, allow, ce_flags); \ @@ -193,6 +196,7 @@ PHP_FUNCTION(class_implements) SPL_ADD_CLASS(RuntimeException, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(SeekableIterator, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(SimpleXMLIterator, z_list, sub, allow, ce_flags); \ + SPL_ADD_CLASS(Subject, z_list, sub, allow, ce_flags); \ SPL_ADD_CLASS(UnderflowException, z_list, sub, allow, ce_flags); \ /* {{{ spl_classes */ diff --git a/ext/spl/spl.php b/ext/spl/spl.php index c54c22a21a..49309bdf11 100755 --- a/ext/spl/spl.php +++ b/ext/spl/spl.php @@ -66,6 +66,9 @@ * * - class ArrayObject implements IteratorAggregate * - class ArrayIterator implements Iterator + * + * As the above suggest an ArrayObject creates an ArrayIterator when it comes to + * iteration (e.g. ArrayObject instance used inside foreach). * * 5) Counting * @@ -88,9 +91,13 @@ * - class OverflowException extends RuntimeException * - class RangeException extends RuntimeException * - class UnderflowException extends RuntimeException - * - * As the above suggest an ArrayObject creates an ArrayIterator when it comes to - * iteration (e.g. ArrayObject instance used inside foreach). + * + * 7) Observer + * + * SPL suggests a standard way of implementing the observer pattern. + * + * - interface Observer + * - interface Subject * * A nice article about SPL can be found * here. @@ -657,4 +664,41 @@ class SimpleXMLIterator extends SimpleXMLElement implements RecursiveIterator function getChildren(); } +/** @ingroup SPL + * @brief observer of the observer pattern + * + * For a detailed explanation see Observer pattern in + * + * Gamma, Helm, Johnson, Vlissides
+ * Design Patterns + *
+ */ +interface Observer +{ + /** Called from the subject (i.e. when it's value has changed). + * @param $subject the callee + */ + function update(Subject $subject); +} + +/** @ingroup SPL + * @brief ubject to the observer pattern + * @see Observer + */ +interface Subject +{ + /** @param $observer new observer to attach + */ + function attach(Observer $observer); + + /** @param $observer existing observer to detach + * @note a non attached observer shouldn't result in a warning or similar + */ + function detach(Observer $observer); + + /** @param $ignore optional observer that should not be notified + */ + function notify([Observer $ignore = NULL]); +} + ?> diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c new file mode 100755 index 0000000000..9c12eb4673 --- /dev/null +++ b/ext/spl/spl_observer.c @@ -0,0 +1,88 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "php_ini.h" +#include "ext/standard/info.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" + +#include "php_spl.h" +#include "spl_functions.h" +#include "spl_engine.h" +#include "spl_observer.h" + +SPL_METHOD(Observer, update); +SPL_METHOD(Subject, attach); +SPL_METHOD(Subject, detach); +SPL_METHOD(Subject, notify); + +static +ZEND_BEGIN_ARG_INFO(arginfo_Observer_update, 0) + ZEND_ARG_OBJ_INFO(0, subject, Subject, 0) +ZEND_END_ARG_INFO(); + +static zend_function_entry spl_funcs_Observer[] = { + SPL_ABSTRACT_ME(Observer, update, arginfo_Observer_update) + {NULL, NULL, NULL} +}; + +static +ZEND_BEGIN_ARG_INFO(arginfo_Subject_attach, 0) + ZEND_ARG_OBJ_INFO(0, observer, Observer, 0) +ZEND_END_ARG_INFO(); + +/*static +ZEND_BEGIN_ARG_INFO_EX(arginfo_Subject_notify, 0, 0, 1) + ZEND_ARG_OBJ_INFO(0, ignore, Observer, 1) +ZEND_END_ARG_INFO();*/ + +static zend_function_entry spl_funcs_Subject[] = { + SPL_ABSTRACT_ME(Subject, attach, arginfo_Subject_attach) + SPL_ABSTRACT_ME(Subject, detach, arginfo_Subject_attach) + SPL_ABSTRACT_ME(Subject, notify, NULL) + {NULL, NULL, NULL} +}; + +PHPAPI zend_class_entry *spl_ce_Observer; +PHPAPI zend_class_entry *spl_ce_Subject; + +/* {{{ PHP_MINIT_FUNCTION(spl_observer) */ +PHP_MINIT_FUNCTION(spl_observer) +{ + REGISTER_SPL_INTERFACE(Observer); + REGISTER_SPL_INTERFACE(Subject); + + return SUCCESS; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ diff --git a/ext/spl/spl_observer.h b/ext/spl/spl_observer.h new file mode 100755 index 0000000000..857116ec4d --- /dev/null +++ b/ext/spl/spl_observer.h @@ -0,0 +1,41 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2004 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Marcus Boerger | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +#ifndef SPL_OBSERVER_H +#define SPL_OBSERVER_H + +#include "php.h" +#include "php_spl.h" + +extern PHPAPI zend_class_entry *spl_ce_Observer; +extern PHPAPI zend_class_entry *spl_ce_Subject; + +PHP_MINIT_FUNCTION(spl_observer); + +#endif /* SPL_OBSERVER_H */ + +/* + * Local Variables: + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ diff --git a/ext/spl/tests/observer_001.phpt b/ext/spl/tests/observer_001.phpt new file mode 100755 index 0000000000..3b20e25bdc --- /dev/null +++ b/ext/spl/tests/observer_001.phpt @@ -0,0 +1,116 @@ +--TEST-- +SPL: Observer and Subject (empty notify) +--FILE-- +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 = array(); + + function __construct($name = 'sub') + { + $this->name = '$' . $name; + } + + function attach(Observer $observer) + { + echo '$sub->' . __METHOD__ . '(' . $observer->getName() . ");\n"; + if (!in_array($observer, $this->observers)) + { + $this->observers[] = $observer; + } + } + + function detach(Observer $observer) + { + echo '$sub->' . __METHOD__ . '(' . $observer->getName() . ");\n"; + $idx = array_search($observer, $this->observers); + if ($idx !== false) + { + unset($this->observers[$idx]); + } + } + + function notify() + { + echo '$sub->' . __METHOD__ . "();\n"; + foreach($this->observers as $observer) + { + $observer->update($this); + } + } + + function getName() + { + return $this->name; + } +} + +$sub = new SubjectImpl; + +$ob1 = new ObserverImpl("ob1"); +$ob2 = new ObserverImpl("ob2"); +$ob3 = new ObserverImpl("ob3"); + +$sub->attach($ob1); +$sub->attach($ob1); +$sub->attach($ob2); +$sub->attach($ob3); + +$sub->notify(); + +$sub->detach($ob3); + +$sub->notify(); + +$sub->detach($ob2); +$sub->detach($ob1); + +$sub->notify(); + +$sub->attach($ob3); + +$sub->notify(); +?> +===DONE=== +--EXPECT-- +$sub->SubjectImpl::attach($ob1); +$sub->SubjectImpl::attach($ob1); +$sub->SubjectImpl::attach($ob2); +$sub->SubjectImpl::attach($ob3); +$sub->SubjectImpl::notify(); +$ob1->ObserverImpl::update($sub); +$ob2->ObserverImpl::update($sub); +$ob3->ObserverImpl::update($sub); +$sub->SubjectImpl::detach($ob3); +$sub->SubjectImpl::notify(); +$ob1->ObserverImpl::update($sub); +$ob2->ObserverImpl::update($sub); +$sub->SubjectImpl::detach($ob2); +$sub->SubjectImpl::detach($ob1); +$sub->SubjectImpl::notify(); +$sub->SubjectImpl::attach($ob3); +$sub->SubjectImpl::notify(); +$ob3->ObserverImpl::update($sub); +===DONE=== -- 2.40.0