diff --git a/.travis.yml b/.travis.yml index 2f81d24..93ceef1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,6 @@ env: before_install: - phpize && ./configure && make -install: - script: - sh -c "make test | tee result.txt" - sh test-report.sh @@ -32,7 +30,3 @@ addons: apt: packages: - valgrind - -#notifications: -# email: -# - zaq178miami@gmail.com diff --git a/README.md b/README.md index 72f2261..d76b506 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,9 @@ [![Windows Build status](https://ci.appveyor.com/api/projects/status/7r07eydi6c3lj36a/branch/master?svg=true)](https://ci.appveyor.com/project/pinepain/php-weak) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/pinepain/php-weak/master/LICENSE) -This extension provides [weak references](https://en.wikipedia.org/wiki/Weak_reference) support for PHP 7 and serves -as a ground for other weak data structures. +This extension adds [Soft Reference](https://en.wikipedia.org/wiki/Weak_reference) and +[Weak References](https://en.wikipedia.org/wiki/Weak_reference) to PHP 7 and may serve as a ground for other +data structures that require advanced referencing model. ## Usage @@ -14,12 +15,18 @@ as a ground for other weak data structures. $weak_reference) { +foreach($soft_references as $soft_ref_object_handle => $soft_reference) { if (is_array($weak_reference->notifier)) { - $weak_reference->notifier[] = $weak_reference; - } elseif (is_callable($weak_reference->notifier)) { + $soft_reference->notifier[] = $weak_reference; + } elseif (is_callable($soft_reference->notifier)) { try { - $weak_reference->notifier($weak_reference); + $soft_reference->notifier($weak_reference); } catch(Throwable $e) { $exceptions[] = $e; } @@ -182,11 +196,38 @@ foreach($weak_references as $weak_ref_object_handle => $weak_reference) { if ($exceptions) { throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions); } + +if (refcount($object) == 1) { + try { + run_original_dtor_obj($object); + } catch(Throwable $e) { + $exceptions[] = $e; + } + + foreach($weak_references as $weak_ref_object_handle => $weak_reference) { + if (is_array($weak_reference->notifier)) { + $weak_reference->notifier[] = $weak_reference; + } elseif (is_callable($weak_reference->notifier)) { + try { + $weak_reference->notifier($weak_reference); + } catch(Throwable $e) { + $exceptions[] = $e; + } + } + } + + if ($exceptions) { + throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions); + } +} else { + // required while internally PHP GC mark object as it dtor was called before calling dtor + mark_object_as_no_destructor_was_called($object); +} ``` ## Development and testing -This extension shipped with Vagrant file which provides basic environment for development and testing purposes. +This extension shipped with Vagrant file which provides basic environment for development and testing purposes. To start it, just type `vagrant up` and then `vagrant ssh` in php-weak directory. Services available out of the box are: @@ -204,12 +245,14 @@ between large variety of PHP versions. ## Reference: + [Soft reference on Wikipedia](https://en.wikipedia.org/wiki/Soft_reference) [Weak reference on Wikipedia](https://en.wikipedia.org/wiki/Weak_reference) #### In other languages: ##### Java: + - [Class `SoftReference`](https://docs.oracle.com/javase/7/docs/api/java/lang/ref/SoftReference.html) - [Class `WeakReference`](https://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html) - [Guidelines for using the Java 2 reference classes](http://www.ibm.com/developerworks/library/j-refs/) - [Strong, Soft, Weak and Phantom References](http://neverfear.org/blog/view/150/Strong_Soft_Weak_and_Phantom_References_Java) diff --git a/php_weak_functions.c b/php_weak_functions.c index d9d1bb2..2c3b02a 100644 --- a/php_weak_functions.c +++ b/php_weak_functions.c @@ -42,6 +42,77 @@ PHP_FUNCTION(refcount) RETURN_LONG(0); } /* }}} */ +PHP_FUNCTION(softrefcounted) /* {{{ */ +{ + zval *zv; + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { + return; + } + + if (IS_OBJECT == Z_TYPE_P(zv)) { + php_weak_referent_t *referent = php_weak_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); + + RETURN_BOOL(NULL != referent && zend_hash_num_elements(&referent->soft_references)); + } + + RETURN_BOOL(0); +} /* }}} */ + +PHP_FUNCTION(softrefcount) /* {{{ */ +{ + zval *zv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { + return; + } + + if (IS_OBJECT == Z_TYPE_P(zv)) { + + php_weak_referent_t *referent = php_weak_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); + + if (NULL == referent) { + RETURN_LONG(0); + } + + RETURN_LONG(zend_hash_num_elements(&referent->soft_references)); + } + + RETURN_LONG(0); +} /* }}} */ + +PHP_FUNCTION(softrefs) /* {{{ */ +{ + zval *zv; + zval softrefs; + + php_weak_reference_t *reference; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zv) == FAILURE) { + return; + } + + ZVAL_UNDEF(&softrefs); + + if (IS_OBJECT == Z_TYPE_P(zv)) { + php_weak_referent_t *referent = php_weak_referent_find_ptr((zend_ulong)Z_OBJ_HANDLE_P(zv)); + + if (NULL != referent) { + array_init_size(&softrefs, zend_hash_num_elements(&referent->soft_references)); + + ZEND_HASH_FOREACH_PTR(&referent->soft_references, reference) { + add_next_index_zval(&softrefs, &reference->this_ptr); + Z_ADDREF(reference->this_ptr); + } ZEND_HASH_FOREACH_END(); + } + } + + if (IS_UNDEF == Z_TYPE(softrefs)) { + array_init_size(&softrefs, 0); + } + + RETURN_ZVAL(&softrefs, 1, 1); +} /* }}} */ + PHP_FUNCTION(weakrefcounted) /* {{{ */ { zval *zv; @@ -118,12 +189,27 @@ PHP_FUNCTION(object_handle) /* {{{ */ zval *zv; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zv) == FAILURE) { - RETURN_NULL(); + return; } RETURN_LONG((uint32_t)Z_OBJ_HANDLE_P(zv)); } /* }}} */ +PHP_FUNCTION(is_obj_destructor_called) /* {{{ */ +{ + zval *zv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &zv) == FAILURE) { + return; + } + + zend_object *obj = Z_OBJ_P(zv); + + uint32_t flags = GC_FLAGS(obj); + + RETURN_BOOL(flags & IS_OBJ_DESTRUCTOR_CALLED); +} /* }}} */ + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(refcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, NULL, 0) ZEND_ARG_INFO(0, value) @@ -133,6 +219,18 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(refcount_arg, ZEND_RETURN_VALUE, 1, IS_L ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, NULL, 0) + ZEND_ARG_INFO(0, object) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefcount_arg, ZEND_RETURN_VALUE, 1, IS_LONG, NULL, 0) + ZEND_ARG_INFO(0, object) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(softrefs_arg, ZEND_RETURN_VALUE, 1, IS_ARRAY, NULL, 0) + ZEND_ARG_INFO(0, object) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(weakrefcounted_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, NULL, 0) ZEND_ARG_INFO(0, object) ZEND_END_ARG_INFO() @@ -149,16 +247,24 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(object_handle_arg, ZEND_RETURN_VALUE, 1, ZEND_ARG_INFO(0, object) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(is_obj_destructor_called_arg, ZEND_RETURN_VALUE, 1, _IS_BOOL, NULL, 0) + ZEND_ARG_INFO(0, object) +ZEND_END_ARG_INFO() const zend_function_entry php_weak_functions[] = { /* {{{ */ ZEND_NS_FE(PHP_WEAK_NS, refcounted, refcounted_arg) ZEND_NS_FE(PHP_WEAK_NS, refcount, refcount_arg) + ZEND_NS_FE(PHP_WEAK_NS, softrefcounted, softrefcounted_arg) + ZEND_NS_FE(PHP_WEAK_NS, softrefcount, softrefcount_arg) + ZEND_NS_FE(PHP_WEAK_NS, softrefs, softrefs_arg) + ZEND_NS_FE(PHP_WEAK_NS, weakrefcounted, weakrefcounted_arg) ZEND_NS_FE(PHP_WEAK_NS, weakrefcount, weakrefcount_arg) ZEND_NS_FE(PHP_WEAK_NS, weakrefs, weakrefs_arg) ZEND_NS_FE(PHP_WEAK_NS, object_handle, object_handle_arg) + ZEND_NS_FE(PHP_WEAK_NS, is_obj_destructor_called, is_obj_destructor_called_arg) PHP_FE_END }; /* }}} */ diff --git a/php_weak_reference.c b/php_weak_reference.c index 8ad9ef9..6fb3f1d 100644 --- a/php_weak_reference.c +++ b/php_weak_reference.c @@ -22,8 +22,11 @@ #include "ext/spl/spl_observer.h" #endif -zend_class_entry *php_weak_reference_class_entry; -#define this_ce php_weak_reference_class_entry +zend_class_entry *php_weak_abstract_reference_class_entry; +zend_class_entry *php_weak_soft_reference_class_entry; +zend_class_entry *php_weak_weak_reference_class_entry; + +#define this_ce php_weak_abstract_reference_class_entry zend_object_handlers php_weak_reference_object_handlers; @@ -199,33 +202,30 @@ void php_weak_reference_call_notifier(zval *reference, zval *notifier) /* {{{ */ } /* }}} */ - -void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ +static void php_weak_nullify_referents(HashTable *references) /* {{{ */ { - php_weak_referent_t *referent = php_weak_referent_find_ptr(object->handle); - - zval exceptions; - zval tmp; + zend_ulong handle; + php_weak_reference_t *reference; - ZVAL_UNDEF(&exceptions); + ZEND_HASH_REVERSE_FOREACH_PTR(references, reference) { + handle = _p->h; + reference->referent = NULL; - assert(NULL != referent); - assert(NULL != PHP_WEAK_G(referents)); + zend_hash_index_del(references, handle); + } ZEND_HASH_FOREACH_END(); +} /* }}} */ +static void php_weak_call_notifiers(HashTable *references, zval *exceptions, zval *tmp, zend_bool nullify_referent) /* {{{ */ +{ zend_ulong handle; php_weak_reference_t *reference; - if (referent->original_handlers->dtor_obj) { - referent->original_handlers->dtor_obj(object); + ZEND_HASH_REVERSE_FOREACH_PTR(references, reference) { + handle = _p->h; - if (EG(exception)) { - php_weak_store_exceptions(&exceptions, &tmp); + if (nullify_referent) { + reference->referent = NULL; } - } - - ZEND_HASH_REVERSE_FOREACH_PTR(&referent->weak_references, reference) { - handle = _p->h; - reference->referent = NULL; switch (reference->notifier_type) { case PHP_WEAK_NOTIFIER_ARRAY: @@ -238,17 +238,52 @@ void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ php_weak_reference_call_notifier(&reference->this_ptr, &reference->notifier); if (EG(exception)) { - php_weak_store_exceptions(&exceptions, &tmp); + php_weak_store_exceptions(exceptions, tmp); } break; default: break; } - zend_hash_index_del(&referent->weak_references, handle); + if (nullify_referent) { + zend_hash_index_del(references, handle); + } } ZEND_HASH_FOREACH_END(); - zend_hash_index_del(PHP_WEAK_G(referents), referent->handle); +} /* }}} */ + +void php_weak_referent_object_dtor_obj(zend_object *object) /* {{{ */ +{ + php_weak_referent_t *referent = php_weak_referent_find_ptr(object->handle); + + zval exceptions; + zval tmp; + + ZVAL_UNDEF(&exceptions); + + assert(NULL != referent); + assert(NULL != PHP_WEAK_G(referents)); + + php_weak_call_notifiers(&referent->soft_references, &exceptions, &tmp, 0); + + if (Z_REFCOUNT(referent->this_ptr) == 1) { + if (referent->original_handlers->dtor_obj) { + referent->original_handlers->dtor_obj(object); + + if (EG(exception)) { + php_weak_store_exceptions(&exceptions, &tmp); + } + } + + php_weak_nullify_referents(&referent->soft_references); + + php_weak_call_notifiers(&referent->weak_references, &exceptions, &tmp, 1); + + zend_hash_index_del(PHP_WEAK_G(referents), referent->handle); + } else { + zend_object *obj = Z_OBJ(referent->this_ptr); + GC_FLAGS(obj) = GC_FLAGS(obj) & ~IS_OBJ_DESTRUCTOR_CALLED; + } if (!Z_ISUNDEF(exceptions)) { zval exception; @@ -264,7 +299,9 @@ void php_weak_globals_referents_ht_dtor(zval *zv) /* {{{ */ assert(NULL != referent); + zend_hash_destroy(&referent->soft_references); zend_hash_destroy(&referent->weak_references); + Z_OBJ(referent->this_ptr)->handlers = referent->original_handlers; efree(referent); @@ -292,7 +329,9 @@ php_weak_referent_t *php_weak_referent_get_or_create(zval *referent_zv) /* {{{ * referent = (php_weak_referent_t *) ecalloc(1, sizeof(php_weak_referent_t)); + zend_hash_init(&referent->soft_references, 0, NULL, NULL, 0); zend_hash_init(&referent->weak_references, 0, NULL, php_weak_referent_weak_references_ht_dtor, 0); + referent->original_handlers = Z_OBJ_P(referent_zv)->handlers; ZVAL_COPY_VALUE(&referent->this_ptr, referent_zv); @@ -313,6 +352,26 @@ php_weak_referent_t *php_weak_referent_get_or_create(zval *referent_zv) /* {{{ * return referent; } /* }}} */ +void php_weak_soft_reference_attach(php_weak_reference_t *reference, php_weak_referent_t *referent) /* {{{ */ +{ + reference->referent = referent; + zend_hash_index_add_ptr(&referent->soft_references, (zend_ulong) Z_OBJ_HANDLE_P(&reference->this_ptr), reference); +} /* }}} */ + +void php_weak_soft_reference_unregister(php_weak_reference_t *reference) /* {{{ */ +{ + zend_hash_index_del(&reference->referent->soft_references, (zend_ulong) Z_OBJ_HANDLE_P(&reference->this_ptr)); +} /* }}} */ + +void php_weak_soft_reference_maybe_unregister(php_weak_reference_t *reference) /* {{{ */ +{ + if (NULL == reference->referent) { + return; + } + + php_weak_soft_reference_unregister(reference); +} /* }}} */ + void php_weak_reference_attach(php_weak_reference_t *reference, php_weak_referent_t *referent) /* {{{ */ { reference->referent = referent; @@ -349,7 +408,7 @@ php_weak_reference_t *php_weak_reference_init(zval *this_ptr, zval *referent_zv, referent = php_weak_referent_get_or_create(referent_zv); - php_weak_reference_attach(reference, referent); + reference->register_reference(reference, referent); if (NULL != notifier_zv) { ZVAL_COPY(&reference->notifier, notifier_zv); @@ -420,7 +479,9 @@ static void php_weak_reference_free(zend_object *object) /* {{{ */ php_weak_reference_t *reference = php_weak_reference_fetch_object(object); /* unregister weak reference from tracking object, if not done already before at some place (e.g. obj dtored) */ - php_weak_reference_maybe_unregister(reference); + if (reference->unregister_reference) { + reference->unregister_reference(reference); + } zval_ptr_dtor(&reference->notifier); ZVAL_UNDEF(&reference->notifier); @@ -434,7 +495,9 @@ static void php_weak_reference_dtor(zend_object *object) /* {{{ */ php_weak_reference_t *reference = php_weak_reference_fetch_object(object); /* unregister weak reference from tracking object, if not done already before at some place (e.g. obj dtored) */ - php_weak_reference_maybe_unregister(reference); + if (reference->unregister_reference) { + reference->unregister_reference(reference); + } zval_ptr_dtor(&reference->notifier); ZVAL_UNDEF(&reference->notifier); @@ -443,7 +506,26 @@ static void php_weak_reference_dtor(zend_object *object) /* {{{ */ zend_objects_destroy_object(object); } /* }}} */ -static zend_object *php_weak_reference_ctor(zend_class_entry *ce) /* {{{ */ +static zend_object *php_weak_abstract_reference_ctor(zend_class_entry *ce) /* {{{ */ +{ + php_weak_reference_t *reference; + + reference = (php_weak_reference_t *) ecalloc(1, sizeof(php_weak_reference_t) + zend_object_properties_size(ce)); + + zend_object_std_init(&reference->std, ce); + object_properties_init(&reference->std, ce); + + reference->std.handlers = &php_weak_reference_object_handlers; + + reference->register_reference = NULL; + reference->unregister_reference = NULL; + + zend_throw_error(NULL, "%s class may not be subclassed directly", ZSTR_VAL(this_ce->name)); + + return &reference->std; +} /* }}} */ + +static zend_object *php_weak_soft_reference_ctor(zend_class_entry *ce) /* {{{ */ { php_weak_reference_t *reference; @@ -454,6 +536,26 @@ static zend_object *php_weak_reference_ctor(zend_class_entry *ce) /* {{{ */ reference->std.handlers = &php_weak_reference_object_handlers; + reference->register_reference = php_weak_soft_reference_attach; + reference->unregister_reference = php_weak_soft_reference_maybe_unregister; + + return &reference->std; +} /* }}} */ + +static zend_object *php_weak_weak_reference_ctor(zend_class_entry *ce) /* {{{ */ +{ + php_weak_reference_t *reference; + + reference = (php_weak_reference_t *) ecalloc(1, sizeof(php_weak_reference_t) + zend_object_properties_size(ce)); + + zend_object_std_init(&reference->std, ce); + object_properties_init(&reference->std, ce); + + reference->std.handlers = &php_weak_reference_object_handlers; + + reference->register_reference = php_weak_reference_attach; + reference->unregister_reference = php_weak_reference_maybe_unregister; + return &reference->std; } /* }}} */ @@ -464,7 +566,7 @@ static zend_object *php_weak_reference_clone_obj(zval *object) /* {{{ */ old_object = Z_OBJ_P(object); - new_object = php_weak_reference_ctor(old_object->ce); + new_object = old_object->ce->create_object(old_object->ce); php_weak_reference_t *old_reference = php_weak_reference_fetch_object(old_object); php_weak_reference_t *new_reference = php_weak_reference_fetch_object(new_object); @@ -474,7 +576,7 @@ static zend_object *php_weak_reference_clone_obj(zval *object) /* {{{ */ new_reference->notifier_type = old_reference->notifier_type; if (old_reference->referent) { - php_weak_reference_attach(new_reference, old_reference->referent); + old_reference->register_reference(new_reference, old_reference->referent); } zend_objects_clone_members(new_object, old_object); @@ -646,7 +748,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_weak_reference_notifier, ZEND_SEND_BY_VAL, ZEND_R ZEND_END_ARG_INFO() -static const zend_function_entry php_weak_reference_methods[] = { /* {{{ */ +static const zend_function_entry php_weak_abstract_reference_methods[] = { /* {{{ */ PHP_ME(WeakReference, __construct, arginfo_weak_reference___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) PHP_ME(WeakReference, get, arginfo_weak_reference_get, ZEND_ACC_PUBLIC) @@ -656,14 +758,23 @@ static const zend_function_entry php_weak_reference_methods[] = { /* {{{ */ PHP_FE_END }; /* }}} */ +static const zend_function_entry php_weak_weak_reference_methods[] = { /* {{{ */ + PHP_FE_END +}; /* }}} */ + +static const zend_function_entry php_weak_soft_reference_methods[] = { /* {{{ */ + PHP_FE_END +}; /* }}} */ + PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */ { zend_class_entry ce; - INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_reference_methods); + INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "AbstractReference", php_weak_abstract_reference_methods); this_ce = zend_register_internal_class(&ce); - this_ce->create_object = php_weak_reference_ctor; + this_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; + this_ce->create_object = php_weak_abstract_reference_ctor; this_ce->serialize = zend_class_serialize_deny; this_ce->unserialize = zend_class_unserialize_deny; @@ -677,6 +788,14 @@ PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */ php_weak_reference_object_handlers.get_debug_info = php_weak_get_debug_info; php_weak_reference_object_handlers.compare_objects = php_weak_compare_objects; + INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "SoftReference", php_weak_weak_reference_methods); + php_weak_soft_reference_class_entry = zend_register_internal_class_ex(&ce, php_weak_abstract_reference_class_entry); + php_weak_soft_reference_class_entry->create_object = php_weak_soft_reference_ctor; + + INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_weak_reference_methods); + php_weak_weak_reference_class_entry = zend_register_internal_class_ex(&ce, php_weak_abstract_reference_class_entry); + php_weak_weak_reference_class_entry->create_object = php_weak_weak_reference_ctor; + return SUCCESS; } /* }}} */ diff --git a/php_weak_reference.h b/php_weak_reference.h index 7e33bc0..a0c7934 100644 --- a/php_weak_reference.h +++ b/php_weak_reference.h @@ -21,12 +21,16 @@ #include "TSRM.h" #endif -extern zend_class_entry *php_weak_reference_class_entry; +extern zend_class_entry *php_weak_abstract_reference_class_entry; +extern zend_class_entry *php_weak_soft_reference_class_entry; +extern zend_class_entry *php_weak_weak_reference_class_entry; typedef struct _php_weak_referent_t php_weak_referent_t; typedef struct _php_weak_reference_t php_weak_reference_t; +typedef void (*php_weak_register)(php_weak_reference_t *reference, php_weak_referent_t *referent); +typedef void (*php_weak_unregister)(php_weak_reference_t *reference); extern php_weak_reference_t *php_weak_reference_fetch_object(zend_object *obj); extern php_weak_referent_t *php_weak_referent_find_ptr(zend_ulong h); @@ -48,6 +52,7 @@ struct _php_weak_referent_t { zend_object_handlers custom_handlers; const zend_object_handlers *original_handlers; + HashTable soft_references; HashTable weak_references; }; @@ -57,6 +62,9 @@ struct _php_weak_reference_t { zval notifier; int notifier_type; + php_weak_register register_reference; + php_weak_unregister unregister_reference; + zval this_ptr; zend_object std; }; diff --git a/stubs/weak/AbstractReference.php b/stubs/weak/AbstractReference.php new file mode 100644 index 0000000..4914376 --- /dev/null +++ b/stubs/weak/AbstractReference.php @@ -0,0 +1,75 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Weak; + + +/** + * Class AbstractReference + * + * Abstract base class for reference objects. This class defines the operations common to all reference objects. + * Because of reference objects implementation details which depends on how GC in PHP works, this class may not be + * subclassed directly. + */ +abstract class AbstractReference +{ + /** + * @param object $referent Referent object + * @param callable | array | null $notify Optional notifier to signal when referent object destroyed. + * + * If notifier is callback, it will be called with a current reference object as a first argument. + * + * For SoftReference, notifiers are called before object will (or will not) be destructed. If referent object + * prevented from being destroyed (regular reference to it created during SoftReference notifiers calling), original + * object's destructor will not be called and next time referent object refcount will reach 0 it will be a subject of + * full destructing cycle again. For WeakReference, referent object will be already destroyed at the time of + * notifiers calling. + * + * If notifier is array, current reference object will be appended to it regardless any exception thrown by + * referent object destructor or previous notify callback. + */ + public function __construct(object $referent, $notify = null) + { + } + + /** + * Get referent object + * + * @return object | null + */ + public function get() + { + } + + /** + * Whether referent object exists + * + * @return bool + */ + public function valid() : bool + { + } + + /** + * Get notifier + * + * @param callable | array | null $notify Notifier to replace existent notifier with. Same as in constructor. + * + * If any value provided, any existent notifier will be replaced and returned. + * + * @return callable | array | null Current notifier or the old one when replacing it with provided value. + */ + public function notifier($notify = null) + { + } +} diff --git a/stubs/weak/NotifierException.php b/stubs/weak/NotifierException.php index 8afedce..5791100 100644 --- a/stubs/weak/NotifierException.php +++ b/stubs/weak/NotifierException.php @@ -1,10 +1,22 @@ + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + namespace Weak; use Exception; +use Throwable; class NotifierException extends Exception @@ -12,9 +24,9 @@ class NotifierException extends Exception private $exceptions = []; /** - * Get exceptions thrown from notifiers + * Get exceptions that were thrown from notifiers * - * @return array + * @return Throwable[] */ public function getExceptions() : array { diff --git a/stubs/weak/Reference.php b/stubs/weak/Reference.php index 00331bd..0ea0beb 100644 --- a/stubs/weak/Reference.php +++ b/stubs/weak/Reference.php @@ -13,51 +13,6 @@ namespace Weak; -class Reference +class Reference extends AbstractReference { - /** - * @param object $referent Referent object - * @param callable | array | null $notify Optional notifier to signal when referent object destroyed. - * - * If notifier is callback, it will be called with a current Weak\Reference object as a first argument. Note, that - * referent object at that time will be already destroyed. Callback will not be called if referent object destructor - * or previous notify callback throws exception. - * - * If notifier is array, current Weak\Reference object will be appended to it regardless any exception thrown by - * referent object destructor or previous notify callback. - */ - public function __construct(object $referent, $notify = null) - { - } - - /** - * Get referent object - * - * @return object | null - */ - public function get() - { - } - - /** - * Whether referent object exists - * - * @return bool - */ - public function valid() : bool - { - } - - /** - * Get notifier - * - * @param callable | array | null $notify Notifier to replace existent notifier with. Same as in constructor. - * - * If any value provided, any existent notifier will be replaced and returned. - * - * @return callable | array | null Current notifier or the old one when replacing it with provided value. - */ - public function notifier($notify = null) - { - } } diff --git a/stubs/weak/SoftReference.php b/stubs/weak/SoftReference.php new file mode 100644 index 0000000..e0b5648 --- /dev/null +++ b/stubs/weak/SoftReference.php @@ -0,0 +1,18 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Weak; + +class SoftReference extends AbstractReference +{ +} diff --git a/stubs/weak/functions.php b/stubs/weak/functions.php index a813af1..5471c29 100644 --- a/stubs/weak/functions.php +++ b/stubs/weak/functions.php @@ -32,6 +32,33 @@ function refcounted(mixed $value) : bool {} */ function refcount(mixed $value) : int {} +/** + * Check whether object has soft references + * + * @param object $value + * + * @return bool + */ +function softrefcounted(object $value) : bool {} + +/** + * Get object's soft references count + + * @param object $value + * + * @return int + */ +function softrefcount(object $value) : int {} + +/** + * Get object's soft references + * + * @param object $value + * + * @return mixed + */ +function softrefs(object $value) : array {} + /** * Check whether object has weak references * @@ -67,3 +94,11 @@ function weakrefs(object $value) : array {} * @return int */ function object_handle(object $value) : int {} + +/** + * Check whether object's destructor was called + * @param object $value + * + * @return bool + */ +function is_obj_destructor_called(object $value) : bool {} diff --git a/tests/.stubs.php b/tests/.stubs.php index 236bc37..58c86d4 100644 --- a/tests/.stubs.php +++ b/tests/.stubs.php @@ -83,3 +83,16 @@ public function __construct($referent, $notify, $test) $this->test = $test; } } + +class ExtendedSoftReference extends \Weak\SoftReference { + /** + * @var + */ + private $test = []; + + public function __construct($referent, $notify, $test) + { + parent::__construct($referent, $notify); + $this->test = $test; + } +} diff --git a/tests/002-AbstracReference-may-not-be-subclassed-directly.phpt b/tests/002-AbstracReference-may-not-be-subclassed-directly.phpt new file mode 100644 index 0000000..a32a4bf --- /dev/null +++ b/tests/002-AbstracReference-may-not-be-subclassed-directly.phpt @@ -0,0 +1,33 @@ +--TEST-- +Weak\AbstractReference - may not be subclassed directly +--SKIPIF-- + +--FILE-- +exception_export($e); +} + + +try { + new class($obj) extends Weak\AbstractReference + { + }; +} catch (Throwable $e) { + $helper->exception_export($e); +} + + +?> +--EXPECT-- +Error: Cannot instantiate abstract class Weak\AbstractReference +Error: Weak\AbstractReference class may not be subclassed directly diff --git a/tests/002-AbstracReference-may-not-be-subclassed-indirectly.phpt b/tests/002-AbstracReference-may-not-be-subclassed-indirectly.phpt new file mode 100644 index 0000000..ef7cc15 --- /dev/null +++ b/tests/002-AbstracReference-may-not-be-subclassed-indirectly.phpt @@ -0,0 +1,35 @@ +--TEST-- +Weak\AbstractReference - may not be subclassed indirectly +--SKIPIF-- + +--FILE-- +newInstanceWithoutConstructor(); + +} catch (Throwable $e) { + $helper->exception_export($e); +} + + +try { + new class($obj) extends Weak\AbstractReference + { + }; +} catch (Throwable $e) { + $helper->exception_export($e); +} + + +?> +--EXPECT-- +Error: Cannot instantiate abstract class Weak\AbstractReference +Error: Weak\AbstractReference class may not be subclassed directly diff --git a/tests/002-reference-basic.phpt b/tests/002-reference-basic.phpt index 8ef7014..0e6954c 100644 --- a/tests/002-reference-basic.phpt +++ b/tests/002-reference-basic.phpt @@ -34,10 +34,10 @@ When referent object alive: Referent object alive: ok Referent object valid: ok object(Weak\Reference)#3 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } @@ -47,9 +47,9 @@ When referent object dead: Referent object dead: ok Referent object invalid: ok object(Weak\Reference)#3 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } diff --git a/tests/002-reference-clone.phpt b/tests/002-reference-clone.phpt index a8ec5d2..b7d8f32 100644 --- a/tests/002-reference-clone.phpt +++ b/tests/002-reference-clone.phpt @@ -52,10 +52,10 @@ EOF --EXPECT-- weakrefcount($obj): integer: 1 object(Weak\Reference)#4 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (2) refcount(3){ ["static"]=> array(1) refcount(1){ @@ -76,10 +76,10 @@ Cloned weak reference does not match original weak reference strictly: ok weakrefcount($obj): integer: 2 object(Weak\Reference)#5 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (2) refcount(4){ ["static"]=> array(1) refcount(1){ @@ -98,9 +98,9 @@ object(Weak\Reference)#5 (2) refcount(3){ Weak references reported with cloned reference: ok Notified: object(Weak\Reference)#5 (2) refcount(6){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (2) refcount(5){ ["static"]=> array(1) refcount(1){ @@ -116,9 +116,9 @@ Notified: object(Weak\Reference)#5 (2) refcount(6){ } } Notified: object(Weak\Reference)#4 (2) refcount(6){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (2) refcount(5){ ["static"]=> array(1) refcount(1){ diff --git a/tests/002-reference-clone_extended.phpt b/tests/002-reference-clone_extended.phpt index ecbb108..2a51f0e 100644 --- a/tests/002-reference-clone_extended.phpt +++ b/tests/002-reference-clone_extended.phpt @@ -45,10 +45,10 @@ object(WeakTests\ExtendedReference)#4 (3) { [0]=> int(42) } - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) { } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (1) { ["parameter"]=> array(1) { @@ -64,10 +64,10 @@ object(WeakTests\ExtendedReference)#5 (3) { [0]=> int(42) } - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) { } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#3 (1) { ["parameter"]=> array(1) { diff --git a/tests/002-reference-closure.phpt b/tests/002-reference-closure.phpt index 50181da..cee92b0 100644 --- a/tests/002-reference-closure.phpt +++ b/tests/002-reference-closure.phpt @@ -34,7 +34,7 @@ When referent object alive: --------------------------- Referent object alive: ok object(Weak\Reference)#3 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(Closure)#2 (1) refcount(2){ ["parameter"]=> array(1) refcount(1){ @@ -42,7 +42,7 @@ object(Weak\Reference)#3 (2) refcount(3){ string(10) "" refcount(1) } } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#4 (1) refcount(2){ ["parameter"]=> array(1) refcount(1){ @@ -57,9 +57,9 @@ When referent object dead: -------------------------- Referent object dead: ok object(Weak\Reference)#3 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#4 (1) refcount(2){ ["parameter"]=> array(1) refcount(1){ diff --git a/tests/002-reference-dump_extended.phpt b/tests/002-reference-dump_extended.phpt index a79bacb..43a3306 100644 --- a/tests/002-reference-dump_extended.phpt +++ b/tests/002-reference-dump_extended.phpt @@ -32,10 +32,10 @@ object(WeakTests\ExtendedReference)#3 (3) { [0]=> int(42) } - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) { } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#4 (1) { ["parameter"]=> array(1) { @@ -51,9 +51,9 @@ object(WeakTests\ExtendedReference)#3 (3) { [0]=> int(42) } - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#4 (1) { ["parameter"]=> array(1) { diff --git a/tests/002-reference-exception_in_callback.phpt b/tests/002-reference-exception_in_callback.phpt index 46d7fb6..6031683 100644 --- a/tests/002-reference-exception_in_callback.phpt +++ b/tests/002-reference-exception_in_callback.phpt @@ -10,7 +10,7 @@ $helper = require '.testsuite.php'; require '.stubs.php'; -$obj = new \WeakTests\TrackingDtor(0); +$obj = new \WeakTests\TrackingDtor(); $callback = function (Weak\Reference $reference) { throw new \Exception('Test exception from callback'); diff --git a/tests/002-reference-notified.phpt b/tests/002-reference-notified.phpt index fd1005d..6311226 100644 --- a/tests/002-reference-notified.phpt +++ b/tests/002-reference-notified.phpt @@ -14,7 +14,8 @@ $wr = new Weak\Reference($obj, function ($reference = null) use ($helper, &$obj) $helper->assert('Notifier called', true); $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); $helper->assert('Notifier get weak reference as it argument', $reference instanceof Weak\Reference); - $helper->assert('Weak reference in notifier points to null', null === $reference->get()); + $helper->assert('Original object is null', null === $obj); + $helper->assert('Weak reference in notifier is null', null === $reference->get()); }); $obj = null; @@ -25,5 +26,6 @@ EOF Notifier called: ok Notifier get 1 argument: ok Notifier get weak reference as it argument: ok -Weak reference in notifier points to null: ok +Original object is null: ok +Weak reference in notifier is null: ok EOF diff --git a/tests/002-reference-notifier_array.phpt b/tests/002-reference-notifier_array.phpt index d8bbde1..a2cbbed 100644 --- a/tests/002-reference-notifier_array.phpt +++ b/tests/002-reference-notifier_array.phpt @@ -33,9 +33,9 @@ array(0) { array(1) { [0]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(1) { [0]=> *RECURSION* @@ -45,9 +45,9 @@ array(1) { array(1) { [0]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(1) { [0]=> *RECURSION* diff --git a/tests/002-reference-notifier_array_clone.phpt b/tests/002-reference-notifier_array_clone.phpt index 8749c2e..5a7e627 100644 --- a/tests/002-reference-notifier_array_clone.phpt +++ b/tests/002-reference-notifier_array_clone.phpt @@ -34,32 +34,32 @@ array(0) { array(2) { [0]=> object(Weak\Reference)#4 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> *RECURSION* [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } } } [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> object(Weak\Reference)#4 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } [1]=> @@ -70,32 +70,32 @@ array(2) { array(2) { [0]=> object(Weak\Reference)#4 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> *RECURSION* [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } } } [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> object(Weak\Reference)#4 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } [1]=> diff --git a/tests/002-reference-notifier_array_reliability.phpt b/tests/002-reference-notifier_array_reliability.phpt index 727031e..3f5505d 100644 --- a/tests/002-reference-notifier_array_reliability.phpt +++ b/tests/002-reference-notifier_array_reliability.phpt @@ -43,32 +43,32 @@ array(0) { array(2) { [0]=> object(Weak\Reference)#6 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> *RECURSION* [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } } } [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> object(Weak\Reference)#6 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } [1]=> @@ -79,32 +79,32 @@ array(2) { array(2) { [0]=> object(Weak\Reference)#6 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> *RECURSION* [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } } } [1]=> object(Weak\Reference)#3 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> array(2) { [0]=> object(Weak\Reference)#6 (2) { - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> NULL - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> *RECURSION* } [1]=> diff --git a/tests/002-reference-serialize_not_allowed.phpt b/tests/002-reference-serialize_not_allowed.phpt index 44f375a..721e9ba 100644 --- a/tests/002-reference-serialize_not_allowed.phpt +++ b/tests/002-reference-serialize_not_allowed.phpt @@ -26,10 +26,10 @@ $helper->line(); EOF --EXPECT-- object(Weak\Reference)#3 (2) refcount(3){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> object(Closure)#4 (1) refcount(2){ ["parameter"]=> array(1) refcount(1){ diff --git a/tests/003-functions-soft-and-weak.phpt b/tests/003-functions-soft-and-weak.phpt new file mode 100644 index 0000000..f617f56 --- /dev/null +++ b/tests/003-functions-soft-and-weak.phpt @@ -0,0 +1,202 @@ +--TEST-- +Weak\functions - test soft* and weak* functions together +--SKIPIF-- + +--FILE-- +header('Test Weak\softrefcounted'); +$helper->export_annotated('softrefcounted($obj1)', softrefcounted($obj1)); +$helper->export_annotated('softrefcounted($obj2)', softrefcounted($obj2)); +$helper->export_annotated('softrefcounted($obj3)', softrefcounted($obj3)); +$helper->line(); + + +$helper->header('Test Weak\softrefcount'); +$helper->export_annotated('softrefcount($obj1)', softrefcount($obj1)); +$helper->export_annotated('softrefcount($obj2)', softrefcount($obj2)); +$helper->export_annotated('softrefcount($obj3)', softrefcount($obj3)); +$helper->line(); + + +$helper->header('Test Weak\softrefs'); + +$helper->assert('Multiple soft refs reported for object with softrefs()', softrefs($obj1), [$soft_ref1a, $soft_ref1b]); +$helper->dump(softrefs($obj1)); +$helper->line(); + +$helper->assert('Single soft ref reported for object with softrefs()', softrefs($obj2), [$soft_ref2]); +$helper->dump(softrefs($obj2)); +$helper->line(); + +$helper->assert('No soft refs reported for object with softrefs()', softrefs($obj3), []); +$helper->dump(softrefs($obj3)); +$helper->line(); + + + +$weak_ref1a = new Reference($obj1); +$weak_ref1b = new Reference($obj1); +$weak_ref2 = new Reference($obj2); + + +$helper->header('Test Weak\weakrefcounted'); +$helper->export_annotated('weakrefcounted($obj1)', weakrefcounted($obj1)); +$helper->export_annotated('weakrefcounted($obj2)', weakrefcounted($obj2)); +$helper->export_annotated('weakrefcounted($obj3)', weakrefcounted($obj3)); +$helper->line(); + + +$helper->header('Test Weak\weakrefcount'); +$helper->export_annotated('weakrefcount($obj1)', weakrefcount($obj1)); +$helper->export_annotated('weakrefcount($obj2)', weakrefcount($obj2)); +$helper->export_annotated('weakrefcount($obj3)', weakrefcount($obj3)); +$helper->line(); + + +$helper->header('Test Weak\weakrefs'); + +$helper->assert('Multiple weak refs reported for object with weakrefs()', weakrefs($obj1), [$weak_ref1a, $weak_ref1b]); +$helper->dump(weakrefs($obj1)); +$helper->line(); + +$helper->assert('Single weak ref reported for object with weakrefs()', weakrefs($obj2), [$weak_ref2]); +$helper->dump(weakrefs($obj2)); +$helper->line(); + +$helper->assert('No weak refs reported for object with weakrefs()', weakrefs($obj3), []); +$helper->dump(weakrefs($obj3)); +$helper->line(); + +?> +--EXPECT-- +Test Weak\softrefcounted: +------------------------- +softrefcounted($obj1): boolean: true +softrefcounted($obj2): boolean: true +softrefcounted($obj3): boolean: false + +Test Weak\softrefcount: +----------------------- +softrefcount($obj1): integer: 2 +softrefcount($obj2): integer: 1 +softrefcount($obj3): integer: 0 + +Test Weak\softrefs: +------------------- +Multiple soft refs reported for object with softrefs(): ok +array(2) refcount(2){ + [0]=> + object(Weak\SoftReference)#5 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } + [1]=> + object(Weak\SoftReference)#6 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +Single soft ref reported for object with softrefs(): ok +array(1) refcount(2){ + [0]=> + object(Weak\SoftReference)#7 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#3 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +No soft refs reported for object with softrefs(): ok +array(0) refcount(2){ +} + +Test Weak\weakrefcounted: +------------------------- +weakrefcounted($obj1): boolean: true +weakrefcounted($obj2): boolean: true +weakrefcounted($obj3): boolean: false + +Test Weak\weakrefcount: +----------------------- +weakrefcount($obj1): integer: 2 +weakrefcount($obj2): integer: 1 +weakrefcount($obj3): integer: 0 + +Test Weak\weakrefs: +------------------- +Multiple weak refs reported for object with weakrefs(): ok +array(2) refcount(2){ + [0]=> + object(Weak\Reference)#8 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } + [1]=> + object(Weak\Reference)#9 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +Single weak ref reported for object with weakrefs(): ok +array(1) refcount(2){ + [0]=> + object(Weak\Reference)#10 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#3 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +No weak refs reported for object with weakrefs(): ok +array(0) refcount(2){ +} diff --git a/tests/003-functions-soft.phpt b/tests/003-functions-soft.phpt new file mode 100644 index 0000000..e2a8c3e --- /dev/null +++ b/tests/003-functions-soft.phpt @@ -0,0 +1,114 @@ +--TEST-- +Weak\functions - test functions +--SKIPIF-- + +--FILE-- +header('Test Weak\softrefcounted'); +$helper->export_annotated('softrefcounted($obj1)', softrefcounted($obj1)); +$helper->export_annotated('softrefcounted($obj2)', softrefcounted($obj2)); +$helper->export_annotated('softrefcounted($obj3)', softrefcounted($obj3)); +$helper->line(); + +$helper->header('Test Weak\softrefcount'); +$helper->export_annotated('softrefcount($obj1)', softrefcount($obj1)); +$helper->export_annotated('softrefcount($obj2)', softrefcount($obj2)); +$helper->export_annotated('softrefcount($obj3)', softrefcount($obj3)); +$helper->line(); + +$helper->header('Test Weak\softrefs'); + +$helper->assert('Multiple soft refs reported for object with softrefs()', softrefs($obj1), [$soft_ref1a, $soft_ref1b]); +$helper->dump(softrefs($obj1)); +$helper->line(); + +$helper->assert('Single soft ref reported for object with softrefs()', softrefs($obj2), [$soft_ref2]); +$helper->dump(softrefs($obj2)); +$helper->line(); + +$helper->assert('No soft refs reported for object with softrefs()', softrefs($obj3), []); +$helper->dump(softrefs($obj3)); +$helper->line(); + +?> +--EXPECT-- +Test Weak\softrefcounted: +------------------------- +softrefcounted($obj1): boolean: true +softrefcounted($obj2): boolean: true +softrefcounted($obj3): boolean: false + +Test Weak\softrefcount: +----------------------- +softrefcount($obj1): integer: 2 +softrefcount($obj2): integer: 1 +softrefcount($obj3): integer: 0 + +Test Weak\softrefs: +------------------- +Multiple soft refs reported for object with softrefs(): ok +array(2) refcount(2){ + [0]=> + object(Weak\SoftReference)#5 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } + [1]=> + object(Weak\SoftReference)#6 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(3){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +Single soft ref reported for object with softrefs(): ok +array(1) refcount(2){ + [0]=> + object(Weak\SoftReference)#7 (2) refcount(2){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#3 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL + } +} + +No soft refs reported for object with softrefs(): ok +array(0) refcount(2){ +} diff --git a/tests/003-functions-weakrefcounted_after_all_refs_died.phpt b/tests/003-functions-weakrefcounted_after_all_refs_died.phpt index e4a9ff5..96073c8 100644 --- a/tests/003-functions-weakrefcounted_after_all_refs_died.phpt +++ b/tests/003-functions-weakrefcounted_after_all_refs_died.phpt @@ -61,10 +61,10 @@ weakrefcount($obj): int(1) weakrefs($obj): array(1) refcount(3){ [0]=> object(Weak\Reference)#3 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } } diff --git a/tests/003-functions.phpt b/tests/003-functions.phpt index 256c4be..6e6c8bd 100644 --- a/tests/003-functions.phpt +++ b/tests/003-functions.phpt @@ -8,13 +8,18 @@ Weak\functions - test functions use function Weak\{ refcounted, refcount, + softrefcounted, + softrefcount, + softrefs, weakrefcounted, weakrefcount, weakrefs, - object_handle + object_handle, + is_obj_destructor_called }; use Weak\Reference; +use Weak\SoftReference; /** @var \Testsuite $helper */ $helper = require '.testsuite.php'; @@ -45,9 +50,9 @@ $helper->line(); -$ref1a = new Reference($obj1); -$ref1b = new Reference($obj1); -$ref2 = new Reference($obj2); +$weak_ref1a = new Reference($obj1); +$weak_ref1b = new Reference($obj1); +$weak_ref2 = new Reference($obj2); $helper->header('Test Weak\weakrefcounted'); @@ -65,11 +70,11 @@ $helper->line(); $helper->header('Test Weak\weakrefs'); -$helper->assert('Multiple weak refs reported for object with weakrefs()', weakrefs($obj1), [$ref1a, $ref1b]); +$helper->assert('Multiple weak refs reported for object with weakrefs()', weakrefs($obj1), [$weak_ref1a, $weak_ref1b]); $helper->dump(weakrefs($obj1)); $helper->line(); -$helper->assert('Single weak refs reported for object with weakrefs()', weakrefs($obj2), [$ref2]); +$helper->assert('Single weak ref reported for object with weakrefs()', weakrefs($obj2), [$weak_ref2]); $helper->dump(weakrefs($obj2)); $helper->line(); @@ -78,14 +83,37 @@ $helper->dump(weakrefs($obj3)); $helper->line(); -$helper->header('Test Weak\refcount'); +$helper->header('Test Weak\object_handle'); $helper->export_annotated('object_handle($obj1)', object_handle($obj1)); $helper->export_annotated('object_handle($obj2)', object_handle($obj2)); $helper->line(); -$helper->export_annotated('spl_object_hash($obj1)', spl_object_hash($obj1)); -$helper->export_annotated('spl_object_hash($obj2)', spl_object_hash($obj2)); -$helper->line(); + +$helper->header('Test Weak\is_obj_destructor_called'); + +class ObjectWithSoftDestructor { + + private $external; + + public function __construct(&$external) + { + $this->external = &$external; + } + + public function __destruct() + { + echo __METHOD__ . ' called', PHP_EOL; + $this->external = $this; + } +} + +$external = null; +$obj = new ObjectWithSoftDestructor($external); + +$helper->export_annotated('is_obj_destructor_called($obj)', is_obj_destructor_called($obj)); +$obj = null; +$helper->assert('Object stored to external value during destructor call', is_object($external)); +$helper->export_annotated('is_obj_destructor_called($external)', is_obj_destructor_called($external)); ?> --EXPECTF-- @@ -123,30 +151,30 @@ Multiple weak refs reported for object with weakrefs(): ok array(2) refcount(2){ [0]=> object(Weak\Reference)#5 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(3){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } [1]=> object(Weak\Reference)#6 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#2 (0) refcount(3){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } } -Single weak refs reported for object with weakrefs(): ok +Single weak ref reported for object with weakrefs(): ok array(1) refcount(2){ [0]=> object(Weak\Reference)#7 (2) refcount(2){ - ["referent":"Weak\Reference":private]=> + ["referent":"Weak\AbstractReference":private]=> object(stdClass)#3 (0) refcount(2){ } - ["notifier":"Weak\Reference":private]=> + ["notifier":"Weak\AbstractReference":private]=> NULL } } @@ -155,10 +183,14 @@ No weak refs reported for object with weakrefs(): ok array(0) refcount(2){ } -Test Weak\refcount: -------------------- +Test Weak\object_handle: +------------------------ object_handle($obj1): integer: 2 object_handle($obj2): integer: 3 -spl_object_hash($obj1): string: '%s' -spl_object_hash($obj2): string: '%s' +Test Weak\is_obj_destructor_called: +----------------------------------- +is_obj_destructor_called($obj): boolean: false +ObjectWithSoftDestructor::__destruct called +Object stored to external value during destructor call: ok +is_obj_destructor_called($external): boolean: true diff --git a/tests/004-SofReference-basic.phpt b/tests/004-SofReference-basic.phpt new file mode 100644 index 0000000..c5c2c69 --- /dev/null +++ b/tests/004-SofReference-basic.phpt @@ -0,0 +1,56 @@ +--TEST-- +Weak\SoftReference - dump representation, get() and valid() methods +--SKIPIF-- + +--FILE-- +header('When referent object alive'); +$helper->assert('Referent object alive', $ref->get() === $obj); +$helper->assert('Referent object valid', $ref->valid()); +$helper->dump($ref); +$helper->space(); + +$obj = null; + + +$helper->header('When referent object dead'); +$helper->assert('Referent object dead', $ref->get() === null); +$helper->assert('Referent object invalid', $ref->valid(), false); +$helper->dump($ref); +$helper->line(); +?> +EOF +--EXPECT-- +When referent object alive: +--------------------------- +Referent object alive: ok +Referent object valid: ok +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + NULL +} + + +When referent object dead: +-------------------------- +Referent object dead: ok +Referent object invalid: ok +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + NULL +} + +EOF diff --git a/tests/004-SofrReference-compare.phpt b/tests/004-SofrReference-compare.phpt new file mode 100644 index 0000000..5d54058 --- /dev/null +++ b/tests/004-SofrReference-compare.phpt @@ -0,0 +1,107 @@ +--TEST-- +Weak\SoftReference - compare references +--SKIPIF-- + +--FILE-- +assert('References to the same object matches', $sr1a == $sr1b); +$helper->assert('References to the same object do not match strictly', $sr1a !== $sr1b); +$helper->line(); + +$helper->assert('Different object same of a kind match', $obj1 == $obj2); +$helper->assert('References to the different object same of a kind match', $sr1 == $sr2); +// no compare_object handlers called during strict comparison +$helper->assert('Different object same of a kind do not match strictly', $obj1 !== $obj2); +$helper->assert('References to the different object same of a kind do not match strictly', $sr1 !== $sr2); +$helper->line(); + +$helper->assert('Different object different of a kind do not match', $obj1 != $obj3); +$helper->assert('References to the different object of the different kind do not match', $sr1 != $sr3); +// no compare_object handlers called during strict comparison +$helper->assert('Different object different of a kind do not match strictly', $obj1 !== $obj3); +$helper->assert('References to the different object of the different kind do not match strictly', $sr1a !== $sr3); +$helper->line(); + +$obj1 = null; + +$helper->assert('References to the same object after their death matches', $sr1a == $sr1b); +$helper->assert('References to the same object after their death do not match strictly', $sr1a !== $sr1b); +$helper->line(); + +$helper->assert('References to the different object same of a kind after one\' death do not match', $sr1 != $sr2); +$helper->assert('References to the different object same of a kind after one\' death do not match strictly', $sr1 !== $sr2); +$helper->line(); + +$helper->assert('References to the different object of the different kind after one\' death do not match', $sr1 != $sr3); +$helper->assert('References to the different object of the different kind after one\' death do not match strictly', $sr1a !== $sr3); +$helper->line(); + + +$obj1 = new stdClass(); + +$sr1 = new Weak\SoftReference($obj1, ['first']); +$sr2 = new Weak\SoftReference($obj1, ['second']); + +$helper->assert('References to the same object with different notifiers do not match', $sr1 != $sr2); + +$sr1 = new Weak\SoftReference($obj1, []); +$sr2 = new Weak\SoftReference($obj1, []); + +$helper->assert('References to the same object with same notifiers match', $sr1 == $sr2); + +$sr2->property = 'changed'; + +$helper->assert('References to the same object with same notifiers but different properties do not match', $sr1 != $sr2); + +$sr3 = new class($obj1, []) extends Weak\SoftReference {}; + +$helper->assert('References to the same object with same notifiers do not match if they are instances of different classes', $sr2 != $sr3); + +$helper->line(); + +?> +EOF +--EXPECT-- +References to the same object matches: ok +References to the same object do not match strictly: ok + +Different object same of a kind match: ok +References to the different object same of a kind match: ok +Different object same of a kind do not match strictly: ok +References to the different object same of a kind do not match strictly: ok + +Different object different of a kind do not match: ok +References to the different object of the different kind do not match: ok +Different object different of a kind do not match strictly: ok +References to the different object of the different kind do not match strictly: ok + +References to the same object after their death matches: ok +References to the same object after their death do not match strictly: ok + +References to the different object same of a kind after one' death do not match: ok +References to the different object same of a kind after one' death do not match strictly: ok + +References to the different object of the different kind after one' death do not match: ok +References to the different object of the different kind after one' death do not match strictly: ok + +References to the same object with different notifiers do not match: ok +References to the same object with same notifiers match: ok +References to the same object with same notifiers but different properties do not match: ok +References to the same object with same notifiers do not match if they are instances of different classes: ok + +EOF diff --git a/tests/004-SoftReference-clone.phpt b/tests/004-SoftReference-clone.phpt new file mode 100644 index 0000000..3a9ac8a --- /dev/null +++ b/tests/004-SoftReference-clone.phpt @@ -0,0 +1,142 @@ +--TEST-- +Weak\SoftReference - clone reference +--SKIPIF-- + +--FILE-- +dump($ref); +}; + +$ref = new \Weak\SoftReference($obj, $notifier); + +$helper->export_annotated('softrefcount($obj)', softrefcount($obj)); +$helper->dump($ref); +$helper->line(); + +$ref2 = clone $ref; + +$helper->assert('Cloned soft reference matches original', $ref == $ref2); +$helper->assert('Cloned soft reference does not match original soft reference strictly', $ref !== $ref2); +$helper->line(); + +$helper->export_annotated('softrefcount($obj)', softrefcount($obj)); +$helper->dump($ref2); +$helper->line(); + +$helper->assert('Soft references reported with cloned reference', softrefs($obj), [$ref, $ref2]); +$helper->line(); + +$obj = null; +$helper->line(); + +$helper->assert('Cloned soft reference matches original', $ref == $ref2); +$helper->assert('Cloned soft reference does not match original soft reference strictly', $ref !== $ref2); +$helper->line(); + + +?> +EOF +--EXPECT-- +softrefcount($obj): integer: 1 +object(Weak\SoftReference)#4 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (2) refcount(3){ + ["static"]=> + array(1) refcount(1){ + ["helper"]=> + object(Testsuite)#1 (0) refcount(4){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$ref"]=> + string(10) "" refcount(1) + } + } +} + +Cloned soft reference matches original: ok +Cloned soft reference does not match original soft reference strictly: ok + +softrefcount($obj): integer: 2 +object(Weak\SoftReference)#5 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (2) refcount(4){ + ["static"]=> + array(1) refcount(1){ + ["helper"]=> + object(Testsuite)#1 (0) refcount(4){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$ref"]=> + string(10) "" refcount(1) + } + } +} + +Soft references reported with cloned reference: ok + +Notified: object(Weak\SoftReference)#5 (2) refcount(6){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (2) refcount(5){ + ["static"]=> + array(1) refcount(1){ + ["helper"]=> + object(Testsuite)#1 (0) refcount(5){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$ref"]=> + string(10) "" refcount(1) + } + } +} +Notified: object(Weak\SoftReference)#4 (2) refcount(6){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (2) refcount(5){ + ["static"]=> + array(1) refcount(1){ + ["helper"]=> + object(Testsuite)#1 (0) refcount(5){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$ref"]=> + string(10) "" refcount(1) + } + } +} + +Cloned soft reference matches original: ok +Cloned soft reference does not match original soft reference strictly: ok + +EOF diff --git a/tests/004-SoftReference-clone_extended.phpt b/tests/004-SoftReference-clone_extended.phpt new file mode 100644 index 0000000..3bebdab --- /dev/null +++ b/tests/004-SoftReference-clone_extended.phpt @@ -0,0 +1,80 @@ +--TEST-- +Weak\SoftReference - clone reference +--SKIPIF-- + +--FILE-- +line(); + +$ref2 = clone $ref; + +var_dump($ref2); +$helper->line(); + + +?> +EOF +--EXPECT-- +object(WeakTests\ExtendedSoftReference)#4 (3) { + ["test":"WeakTests\ExtendedSoftReference":private]=> + array(1) { + [0]=> + int(42) + } + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) { + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (1) { + ["parameter"]=> + array(1) { + ["$ref"]=> + string(10) "" + } + } +} + +object(WeakTests\ExtendedSoftReference)#5 (3) { + ["test":"WeakTests\ExtendedSoftReference":private]=> + array(1) { + [0]=> + int(42) + } + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) { + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#3 (1) { + ["parameter"]=> + array(1) { + ["$ref"]=> + string(10) "" + } + } +} + +EOF diff --git a/tests/004-SoftReference-closure.phpt b/tests/004-SoftReference-closure.phpt new file mode 100644 index 0000000..2673908 --- /dev/null +++ b/tests/004-SoftReference-closure.phpt @@ -0,0 +1,72 @@ +--TEST-- +Weak\SoftReference - referencing closure +--SKIPIF-- + +--FILE-- +header('When referent object alive'); +$helper->assert('Referent object alive', $sr->get() === $obj); +$helper->dump($sr); +$helper->space(); + +$obj = null; + +$helper->header('When referent object dead'); +$helper->assert('Referent object dead', $sr->get() === null); + +$helper->dump($sr); + +$helper->line(); +?> +EOF +--EXPECT-- +When referent object alive: +--------------------------- +Referent object alive: ok +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(Closure)#2 (1) refcount(2){ + ["parameter"]=> + array(1) refcount(1){ + ["$greeting"]=> + string(10) "" refcount(1) + } + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (1) refcount(2){ + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + + +When referent object dead: +-------------------------- +Referent object dead: ok +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (1) refcount(2){ + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + +EOF diff --git a/tests/004-SoftReference-die_in_dtor.phpt b/tests/004-SoftReference-die_in_dtor.phpt new file mode 100644 index 0000000..6863898 --- /dev/null +++ b/tests/004-SoftReference-die_in_dtor.phpt @@ -0,0 +1,44 @@ +--TEST-- +Weak\SoftReference - destructor calls die() +--SKIPIF-- + +--FILE-- +exception_export($e); + $helper->line(); +} + +register_shutdown_function(function () use (&$sr, &$helper) { + echo 'We did not die properly', PHP_EOL; +}); + + +?> +EOF +--EXPECT-- +Soft notifier called +Destructor dies diff --git a/tests/004-SoftReference-dump_extended.phpt b/tests/004-SoftReference-dump_extended.phpt new file mode 100644 index 0000000..3287ad3 --- /dev/null +++ b/tests/004-SoftReference-dump_extended.phpt @@ -0,0 +1,66 @@ +--TEST-- +Weak\SoftReference - dump representation of extended reference class +--SKIPIF-- + +--FILE-- +line(); + +$obj = null; + +var_dump($sr); +$helper->line(); +?> +EOF +--EXPECT-- +object(WeakTests\ExtendedSoftReference)#3 (3) { + ["test":"WeakTests\ExtendedSoftReference":private]=> + array(1) { + [0]=> + int(42) + } + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) { + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (1) { + ["parameter"]=> + array(1) { + ["$reference"]=> + string(10) "" + } + } +} + +object(WeakTests\ExtendedSoftReference)#3 (3) { + ["test":"WeakTests\ExtendedSoftReference":private]=> + array(1) { + [0]=> + int(42) + } + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (1) { + ["parameter"]=> + array(1) { + ["$reference"]=> + string(10) "" + } + } +} + +EOF diff --git a/tests/004-SoftReference-exception_in_callback.phpt b/tests/004-SoftReference-exception_in_callback.phpt new file mode 100644 index 0000000..fa0df55 --- /dev/null +++ b/tests/004-SoftReference-exception_in_callback.phpt @@ -0,0 +1,34 @@ +--TEST-- +Weak\SoftReference - exception thrown in callback +--SKIPIF-- + +--FILE-- +weak_exception_export($e); +} + +?> +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from callback +EOF diff --git a/tests/004-SoftReference-exception_in_multiple_callbacks.phpt b/tests/004-SoftReference-exception_in_multiple_callbacks.phpt new file mode 100644 index 0000000..2540be0 --- /dev/null +++ b/tests/004-SoftReference-exception_in_multiple_callbacks.phpt @@ -0,0 +1,60 @@ +--TEST-- +Weak\SoftReference - exception thrown in callback +--SKIPIF-- + +--FILE-- +weak_exception_export($e); +} + + +$helper->line(); +?> +EOF +--EXPECT-- +Callback #0 called +Callback #1 called +Callback #2 called +Callback #3 called +Callback #4 called +Callback #5 called +WeakTests\TrackingDtor's destructor called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from callback #1 + Exception: Test exception from callback #3 + +EOF diff --git a/tests/004-SoftReference-exception_in_orig_dtor.phpt b/tests/004-SoftReference-exception_in_orig_dtor.phpt new file mode 100644 index 0000000..5526413 --- /dev/null +++ b/tests/004-SoftReference-exception_in_orig_dtor.phpt @@ -0,0 +1,42 @@ +--TEST-- +Weak\SoftReference - exception thrown in orig dtor +--SKIPIF-- + +--FILE-- +weak_exception_export($e); +} + +?> +EOF +--EXPECT-- +Weak callback called +Dtor called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from dtor +EOF diff --git a/tests/004-SoftReference-exception_in_orig_dtor_and_callback.phpt b/tests/004-SoftReference-exception_in_orig_dtor_and_callback.phpt new file mode 100644 index 0000000..eb167ac --- /dev/null +++ b/tests/004-SoftReference-exception_in_orig_dtor_and_callback.phpt @@ -0,0 +1,46 @@ +--TEST-- +Weak\SoftReference - exception thrown in orig dtor and callback +--SKIPIF-- + +--FILE-- +weak_exception_export($e); +} + +?> +EOF +--EXPECT-- +Callback called +Dtor called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Test exception from callback + Exception: Test exception from dtor +EOF diff --git a/tests/004-SoftReference-extended_dtor_called.phpt b/tests/004-SoftReference-extended_dtor_called.phpt new file mode 100644 index 0000000..bd4b1db --- /dev/null +++ b/tests/004-SoftReference-extended_dtor_called.phpt @@ -0,0 +1,32 @@ +--TEST-- +Weak\SoftReference - dump representation of extended reference class +--SKIPIF-- + +--FILE-- +line(); +?> +EOF +--EXPECT-- +Dtoring ExtendedReferenceTrackingDtor + +EOF diff --git a/tests/004-SoftReference-multiple_obj.phpt b/tests/004-SoftReference-multiple_obj.phpt new file mode 100644 index 0000000..b2c4e2e --- /dev/null +++ b/tests/004-SoftReference-multiple_obj.phpt @@ -0,0 +1,36 @@ +--TEST-- +Weak\SoftReference - track only single specific object +--SKIPIF-- + +--FILE-- +line(); +$obj0 = null; + + +?> +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called + +Weak notifier called +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-multiple_weak.phpt b/tests/004-SoftReference-multiple_weak.phpt new file mode 100644 index 0000000..a1ba113 --- /dev/null +++ b/tests/004-SoftReference-multiple_weak.phpt @@ -0,0 +1,43 @@ +--TEST-- +Weak\SoftReference - multiple weak references to the same object +--SKIPIF-- + +--FILE-- +assert("First weak references points to original object", $sr1->get() === $obj); +$helper->assert("Second weak references points to original object", $sr2->get() === $obj); + +$helper->line(); +$obj = null; +$helper->line(); + +$helper->assert("First weak references points to null", $sr1->get() === null); +$helper->assert("Second weak references points to null", $sr2->get() === null); + +$sr1 = null; +$sr2 = null; + +?> +EOF +--EXPECT-- +First weak references points to original object: ok +Second weak references points to original object: ok + +WeakTests\TrackingDtor's destructor called + +First weak references points to null: ok +Second weak references points to null: ok +EOF diff --git a/tests/004-SoftReference-multiple_with_notify_and_orig_dtor.phpt b/tests/004-SoftReference-multiple_with_notify_and_orig_dtor.phpt new file mode 100644 index 0000000..fd95505 --- /dev/null +++ b/tests/004-SoftReference-multiple_with_notify_and_orig_dtor.phpt @@ -0,0 +1,34 @@ +--TEST-- +Weak\SoftReference - multiple weak references with notifiers, original object destructor called once and after all notifiers +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +Weak notifier 2 called +Weak notifier 1 called +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-notified.phpt b/tests/004-SoftReference-notified.phpt new file mode 100644 index 0000000..bea0868 --- /dev/null +++ b/tests/004-SoftReference-notified.phpt @@ -0,0 +1,33 @@ +--TEST-- +Weak\SoftReference - weak reference notified when object destroyed +--SKIPIF-- + +--FILE-- +assert('Notifier called', true); + $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); + $helper->assert('Notifier get weak reference as it argument', $reference instanceof Weak\SoftReference); + $helper->assert('Original object is null', null === $obj); + $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); + $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof stdClass); +}); + +$obj = null; + +?> +EOF +--EXPECT-- +Notifier called: ok +Notifier get 1 argument: ok +Notifier get weak reference as it argument: ok +Original object is null: ok +Soft reference in notifier is not null: ok +Soft reference in notifier points to original object: ok +EOF diff --git a/tests/004-SoftReference-notified_prevent_destoying.phpt b/tests/004-SoftReference-notified_prevent_destoying.phpt new file mode 100644 index 0000000..db077a9 --- /dev/null +++ b/tests/004-SoftReference-notified_prevent_destoying.phpt @@ -0,0 +1,129 @@ +--TEST-- +Weak\SoftReference - prevent original object from being destroyed +--SKIPIF-- + +--FILE-- +assert('Notifier called', true); + $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); + $helper->assert('Notifier get weak reference as it argument', $reference instanceof Weak\SoftReference); + $helper->assert('Original object is null', null === $obj); + $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); + $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof \WeakTests\TrackingDtor); + $helper->space(); + + $obj_copy = $reference->get(); +}); + +$helper->header('When referent object alive'); +$helper->assert('Referent object alive', $sr->get() === $obj); + +$helper->line(); +$helper->dump($sr); +$helper->space(); + +$obj = null; + +$helper->header('When referent object was nullified but reference to it stored from notifier'); +$helper->assert('Original variable holds null', null === $obj); +$helper->assert('Object copy is not null', null !== $obj_copy); +$helper->assert('Object copy dtor was not called', false === is_obj_destructor_called($obj_copy)); +$helper->assert('Referent object alive', $sr->get() === $obj_copy); + +$helper->line(); +$helper->dump($sr); +$helper->line(); + +$sr = null; +$obj_copy = null; + +?> +EOF +--EXPECT-- +When referent object alive: +--------------------------- +Referent object alive: ok + +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (2) refcount(2){ + ["static"]=> + array(3) refcount(1){ + ["obj"]=> + &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["obj_copy"]=> + &NULL + ["helper"]=> + &object(Testsuite)#1 (0) refcount(2){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + + +Notifier called: ok +Notifier get 1 argument: ok +Notifier get weak reference as it argument: ok +Original object is null: ok +Soft reference in notifier is not null: ok +Soft reference in notifier points to original object: ok + + +When referent object was nullified but reference to it stored from notifier: +---------------------------------------------------------------------------- +Original variable holds null: ok +Object copy is not null: ok +Object copy dtor was not called: ok +Referent object alive: ok + +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (2) refcount(2){ + ["static"]=> + array(3) refcount(1){ + ["obj"]=> + &NULL + ["obj_copy"]=> + &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["helper"]=> + &object(Testsuite)#1 (0) refcount(2){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-notified_prevent_destoying_forever.phpt b/tests/004-SoftReference-notified_prevent_destoying_forever.phpt new file mode 100644 index 0000000..ee6ae17 --- /dev/null +++ b/tests/004-SoftReference-notified_prevent_destoying_forever.phpt @@ -0,0 +1,136 @@ +--TEST-- +Weak\SoftReference - prevent original object from being destroyed forever +--SKIPIF-- + +--FILE-- +assert('Notifier called', true); + $helper->assert('Notifier get 1 argument', sizeof(func_get_args()) === 1); + $helper->assert('Notifier get weak reference as it argument', $reference instanceof Weak\SoftReference); + $helper->assert('Original object is null', null === $obj); + $helper->assert('Soft reference in notifier is not null', null !== $reference->get()); + $helper->assert('Soft reference in notifier points to original object', $reference->get() instanceof \WeakTests\TrackingDtor); + $helper->space(); + + $obj_copy = $reference->get(); +}); + +$helper->header('When referent object alive'); +$helper->assert('Referent object alive', $sr->get() === $obj); + +$helper->line(); +$helper->dump($sr); +$helper->space(); + +$obj = null; + +$helper->header('When referent object was nullified but reference to it stored from notifier'); +$helper->assert('Original variable holds null', null === $obj); +$helper->assert('Object copy is not null', null !== $obj_copy); +$helper->assert('Object copy dtor was not called', false === is_obj_destructor_called($obj_copy)); +$helper->assert('Referent object alive', $sr->get() === $obj_copy); + +$helper->line(); +$helper->dump($sr); +$helper->line(); + +$obj_copy = null; + +?> +EOF +--EXPECT-- +When referent object alive: +--------------------------- +Referent object alive: ok + +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (2) refcount(2){ + ["static"]=> + array(3) refcount(1){ + ["obj"]=> + &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["obj_copy"]=> + &NULL + ["helper"]=> + &object(Testsuite)#1 (0) refcount(2){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + + +Notifier called: ok +Notifier get 1 argument: ok +Notifier get weak reference as it argument: ok +Original object is null: ok +Soft reference in notifier is not null: ok +Soft reference in notifier points to original object: ok + + +When referent object was nullified but reference to it stored from notifier: +---------------------------------------------------------------------------- +Original variable holds null: ok +Object copy is not null: ok +Object copy dtor was not called: ok +Referent object alive: ok + +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (2) refcount(2){ + ["static"]=> + array(3) refcount(1){ + ["obj"]=> + &NULL + ["obj_copy"]=> + &object(WeakTests\TrackingDtor)#2 (0) refcount(2){ + } + ["helper"]=> + &object(Testsuite)#1 (0) refcount(2){ + } + } + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} + +Notifier called: ok +Notifier get 1 argument: ok +Notifier get weak reference as it argument: ok +Original object is null: ok +Soft reference in notifier is not null: ok +Soft reference in notifier points to original object: ok + + +EOF +WeakTests\TrackingDtor's destructor called diff --git a/tests/004-SoftReference-notified_prevent_destoying_forever_with_weak.phpt b/tests/004-SoftReference-notified_prevent_destoying_forever_with_weak.phpt new file mode 100644 index 0000000..a49e3ef --- /dev/null +++ b/tests/004-SoftReference-notified_prevent_destoying_forever_with_weak.phpt @@ -0,0 +1,40 @@ +--TEST-- +Weak\SoftReference - prevent original object from being destroyed, weak notifiers called only when soft ref released +--SKIPIF-- + +--FILE-- +get(); +}); + +$wr = new Weak\Reference($obj, function (Weak\Reference $reference){ + echo 'Weak notifier called', PHP_EOL; +}); + +$obj = null; +$sr = null; + +echo 'Here original dtor will be called', PHP_EOL; +$obj_copy = null; + +?> +EOF +--EXPECT-- +Soft notifier called +Here original dtor will be called +WeakTests\TrackingDtor's destructor called +Weak notifier called +EOF diff --git a/tests/004-SoftReference-notified_prevent_destoying_with_weak.phpt b/tests/004-SoftReference-notified_prevent_destoying_with_weak.phpt new file mode 100644 index 0000000..a6bcddb --- /dev/null +++ b/tests/004-SoftReference-notified_prevent_destoying_with_weak.phpt @@ -0,0 +1,40 @@ +--TEST-- +Weak\SoftReference - prevent original object from being destroyed forever, weak notifiers will not be called +--SKIPIF-- + +--FILE-- +get(); +}); + +$wr = new Weak\Reference($obj, function (Weak\Reference $reference){ + echo 'Weak notifier called', PHP_EOL; +}); + +$obj = null; +//$sr = null; + +echo 'Here soft reference notifier will be called', PHP_EOL; +$obj_copy = null; + +?> +EOF +--EXPECT-- +Soft notifier called +Here soft reference notifier will be called +Soft notifier called +EOF +WeakTests\TrackingDtor's destructor called diff --git a/tests/004-SoftReference-notified_with_weak.phpt b/tests/004-SoftReference-notified_with_weak.phpt new file mode 100644 index 0000000..54019cf --- /dev/null +++ b/tests/004-SoftReference-notified_with_weak.phpt @@ -0,0 +1,32 @@ +--TEST-- +Weak\SoftReference - soft reference notifier then original object dtor and then weak notifiers called +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +Soft notifier called +WeakTests\TrackingDtor's destructor called +Weak notifier called +EOF diff --git a/tests/004-SoftReference-notifier.phpt b/tests/004-SoftReference-notifier.phpt new file mode 100644 index 0000000..6bf9c0c --- /dev/null +++ b/tests/004-SoftReference-notifier.phpt @@ -0,0 +1,35 @@ +--TEST-- +Weak\SoftReference - notifier accessor +--SKIPIF-- + +--FILE-- +assert('Notifier is null by default', $sr0->notifier(), null); +$helper->assert('Null notifier acceptable', $sr1->notifier(), null); +$helper->assert('Array notifier acceptable', $sr2->notifier(), $array_notifier); +$helper->assert('Callback notifier acceptable', $sr3->notifier(), $callback_notifier); + +$helper->line(); +?> +EOF +--EXPECT-- +Notifier is null by default: ok +Null notifier acceptable: ok +Array notifier acceptable: ok +Callback notifier acceptable: ok + +EOF diff --git a/tests/004-SoftReference-notifier_array.phpt b/tests/004-SoftReference-notifier_array.phpt new file mode 100644 index 0000000..3d4d62f --- /dev/null +++ b/tests/004-SoftReference-notifier_array.phpt @@ -0,0 +1,58 @@ +--TEST-- +Weak\SoftReference - array notifier +--SKIPIF-- + +--FILE-- +line(); +?> +EOF +--EXPECT-- +array(0) { +} +array(1) { + [0]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(1) { + [0]=> + *RECURSION* + } + } +} +array(1) { + [0]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(1) { + [0]=> + *RECURSION* + } + } +} + +EOF diff --git a/tests/004-SoftReference-notifier_array_clone.phpt b/tests/004-SoftReference-notifier_array_clone.phpt new file mode 100644 index 0000000..1aa9e93 --- /dev/null +++ b/tests/004-SoftReference-notifier_array_clone.phpt @@ -0,0 +1,107 @@ +--TEST-- +Weak\SoftReference - array notifier +--SKIPIF-- + +--FILE-- +line(); +?> +EOF +--EXPECT-- +array(0) { +} +array(2) { + [0]=> + object(Weak\SoftReference)#4 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + *RECURSION* + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + } + } + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + object(Weak\SoftReference)#4 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + [1]=> + *RECURSION* + } + } +} +array(2) { + [0]=> + object(Weak\SoftReference)#4 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + *RECURSION* + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + } + } + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + object(Weak\SoftReference)#4 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + [1]=> + *RECURSION* + } + } +} + +EOF diff --git a/tests/004-SoftReference-notifier_array_reliability.phpt b/tests/004-SoftReference-notifier_array_reliability.phpt new file mode 100644 index 0000000..c72fc4e --- /dev/null +++ b/tests/004-SoftReference-notifier_array_reliability.phpt @@ -0,0 +1,116 @@ +--TEST-- +Weak\SoftReference - array notifier +--SKIPIF-- + +--FILE-- +exception_export($e); +} + +var_dump($notifier); + +$sr1 = null; + +var_dump($notifier); + +$helper->line(); +?> +EOF +--EXPECT-- +array(0) { +} +array(2) { + [0]=> + object(Weak\SoftReference)#6 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + *RECURSION* + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + } + } + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + object(Weak\SoftReference)#6 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + [1]=> + *RECURSION* + } + } +} +array(2) { + [0]=> + object(Weak\SoftReference)#6 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + *RECURSION* + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + } + } + [1]=> + object(Weak\SoftReference)#3 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + array(2) { + [0]=> + object(Weak\SoftReference)#6 (2) { + ["referent":"Weak\AbstractReference":private]=> + NULL + ["notifier":"Weak\AbstractReference":private]=> + *RECURSION* + } + [1]=> + *RECURSION* + } + } +} + +EOF diff --git a/tests/004-SoftReference-notifier_callable_array.phpt b/tests/004-SoftReference-notifier_callable_array.phpt new file mode 100644 index 0000000..01e0af3 --- /dev/null +++ b/tests/004-SoftReference-notifier_callable_array.phpt @@ -0,0 +1,37 @@ +--TEST-- +Weak\SoftReference - callable notifier passed as array +--SKIPIF-- + +--FILE-- +wr = new Weak\SoftReference($obj, [$this, 'notifier']); + } + + public function notifier() + { + echo 'Notified', PHP_EOL; + } +} + +$obj = new stdClass(); +$t = new Test($obj); +$obj = null; + +$helper->line(); +?> +EOF +--EXPECT-- +Notified + +EOF diff --git a/tests/004-SoftReference-notifier_callable_string.phpt b/tests/004-SoftReference-notifier_callable_string.phpt new file mode 100644 index 0000000..e2ef1c3 --- /dev/null +++ b/tests/004-SoftReference-notifier_callable_string.phpt @@ -0,0 +1,27 @@ +--TEST-- +Weak\SoftReference - callable notifier passed as string +--SKIPIF-- + +--FILE-- +line(); +?> +EOF +--EXPECT-- +Notified + +EOF diff --git a/tests/004-SoftReference-notifier_change.phpt b/tests/004-SoftReference-notifier_change.phpt new file mode 100644 index 0000000..148688b --- /dev/null +++ b/tests/004-SoftReference-notifier_change.phpt @@ -0,0 +1,81 @@ +--TEST-- +Weak\SoftReference - changing notifier +--SKIPIF-- + +--FILE-- +assert('Notifier is null by default', $sr->notifier(), null); +$helper->assert('Notifier was null', $sr->notifier($array_notifier), null); +$helper->assert('Notifier is array', $sr->notifier(), $array_notifier); + +$obj = null; +$helper->assert('New array notifier notified on referent object death', $array_notifier, [$sr]); +$helper->line(); + + +$obj = new stdClass(); +$array_notifier = []; +$sr = new Weak\SoftReference($obj, $array_notifier); + +$helper->assert('Notifier is array by default', $sr->notifier(), $array_notifier); +$helper->assert('Notifier was array', $sr->notifier($callback_notifier), $array_notifier); +$helper->assert('Notifier is callback', $sr->notifier(), $callback_notifier); + +$obj = null; +$helper->line(); + +$obj = new stdClass(); +$array_notifier = []; +$sr = new Weak\SoftReference($obj, $callback_notifier); + +$helper->assert('Notifier is callback by default', $sr->notifier(), $callback_notifier); +$helper->assert('Notifier was callback', $sr->notifier(null), $callback_notifier); +$helper->assert('Notifier is null', $sr->notifier(), null); +$obj = null; +$helper->line(); + +$notifier = 'var_dump'; +$sr->notifier($notifier); + +try { + $sr->notifier('nonexistent'); +} catch (Error $e) { + $helper->exception_export($e); +} + +$helper->assert('Notifier stays the same', $sr->notifier(), $notifier); +$helper->line(); + +?> +EOF +--EXPECT-- +Notifier is null by default: ok +Notifier was null: ok +Notifier is array: ok +New array notifier notified on referent object death: ok + +Notifier is array by default: ok +Notifier was array: ok +Notifier is callback: ok +Callback notified + +Notifier is callback by default: ok +Notifier was callback: ok +Notifier is null: ok + +TypeError: Argument 2 passed to Weak\SoftReference::notifier() must be callable, array or null, string given +Notifier stays the same: ok + +EOF diff --git a/tests/004-SoftReference-notifier_clone_change.phpt b/tests/004-SoftReference-notifier_clone_change.phpt new file mode 100644 index 0000000..e390737 --- /dev/null +++ b/tests/004-SoftReference-notifier_clone_change.phpt @@ -0,0 +1,42 @@ +--TEST-- +Weak\SoftReference - change notifier on cloned object +--SKIPIF-- + +--FILE-- +assert('Notifier is array', $sr->notifier(), $array_notifier); + +$array_notifier1 = []; + +$sr1 = clone $sr; +$helper->assert('Cloned notifier is array', $sr1->notifier(), $array_notifier); +$sr1->notifier($array_notifier1); +$helper->assert('Cloned notifier changed to it own array', $sr1->notifier(), $array_notifier1); + +$obj = null; +$helper->line(); + +$helper->assert('First array notifier notified', $array_notifier, [$sr]); +$helper->assert('Second array notifier notified', $array_notifier1, [$sr1]); +$helper->line(); +?> +EOF +--EXPECT-- +Notifier is array: ok +Cloned notifier is array: ok +Cloned notifier changed to it own array: ok + +First array notifier notified: ok +Second array notifier notified: ok + +EOF diff --git a/tests/004-SoftReference-notifier_invalid_callback.phpt b/tests/004-SoftReference-notifier_invalid_callback.phpt new file mode 100644 index 0000000..435b845 --- /dev/null +++ b/tests/004-SoftReference-notifier_invalid_callback.phpt @@ -0,0 +1,25 @@ +--TEST-- +Weak\SoftReference - invalid notifier callback passed +--SKIPIF-- + +--FILE-- +exception_export($e); +} + +$helper->line(); +?> +EOF +--EXPECT-- +TypeError: Argument 2 passed to Weak\SoftReference::__construct() must be callable, array or null, string given + +EOF diff --git a/tests/004-SoftReference-notifier_not_called_after_wr_dies_first.phpt b/tests/004-SoftReference-notifier_not_called_after_wr_dies_first.phpt new file mode 100644 index 0000000..10d1832 --- /dev/null +++ b/tests/004-SoftReference-notifier_not_called_after_wr_dies_first.phpt @@ -0,0 +1,47 @@ +--TEST-- +Weak\SoftReference - notifier not called after weak reference dies first +--SKIPIF-- + +--FILE-- +weak_exception_export($e); + $helper->line(); +} + +$helper->assert('Referent object dead', $sr->get() === null); +$helper->assert('Referent object invalid', $sr->valid(), false); +$helper->line(); + +?> +EOF +--EXPECT-- +Weak notifier called +Weak\NotifierException: One or more exceptions thrown during notifiers calling + Exception: Destructor throws exception + +Referent object dead: ok +Referent object invalid: ok + +EOF diff --git a/tests/004-SoftReference-orig_dtor_and_notifier_when_wr_dies_first.phpt b/tests/004-SoftReference-orig_dtor_and_notifier_when_wr_dies_first.phpt new file mode 100644 index 0000000..feca464 --- /dev/null +++ b/tests/004-SoftReference-orig_dtor_and_notifier_when_wr_dies_first.phpt @@ -0,0 +1,24 @@ +--TEST-- +Weak\SoftReference - original object destructor called after weak reference dies first +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-orig_dtor_and_notify.phpt b/tests/004-SoftReference-orig_dtor_and_notify.phpt new file mode 100644 index 0000000..58b817c --- /dev/null +++ b/tests/004-SoftReference-orig_dtor_and_notify.phpt @@ -0,0 +1,41 @@ +--TEST-- +Weak\SoftReference - original object destructor called with notify callback after it +--SKIPIF-- + +--FILE-- +assert("Weak references points to original object", $sr->get() === $obj); + +$helper->line(); +$obj = null; +$helper->line(); + +$helper->assert("Weak references points to null", $sr->get() === null); + +$obj = null; + +?> +EOF +--EXPECT-- +Weak references points to original object: ok + +Weak notifier called +WeakTests\TrackingDtor's destructor called + +Weak references points to null: ok +EOF diff --git a/tests/004-SoftReference-orig_dtor_called.phpt b/tests/004-SoftReference-orig_dtor_called.phpt new file mode 100644 index 0000000..93687b3 --- /dev/null +++ b/tests/004-SoftReference-orig_dtor_called.phpt @@ -0,0 +1,27 @@ +--TEST-- +Weak\SoftReference - original object destructor called but notifier not when weak reference dies first +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-orig_dtor_called_after_wr_dies_first.phpt b/tests/004-SoftReference-orig_dtor_called_after_wr_dies_first.phpt new file mode 100644 index 0000000..feca464 --- /dev/null +++ b/tests/004-SoftReference-orig_dtor_called_after_wr_dies_first.phpt @@ -0,0 +1,24 @@ +--TEST-- +Weak\SoftReference - original object destructor called after weak reference dies first +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-orig_dtor_called_once.phpt b/tests/004-SoftReference-orig_dtor_called_once.phpt new file mode 100644 index 0000000..12862e3 --- /dev/null +++ b/tests/004-SoftReference-orig_dtor_called_once.phpt @@ -0,0 +1,25 @@ +--TEST-- +Weak\SoftReference - multiple weak references to the same object, original object destructor called once +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +WeakTests\TrackingDtor's destructor called +EOF diff --git a/tests/004-SoftReference-serialize_extended_not_allowed.phpt b/tests/004-SoftReference-serialize_extended_not_allowed.phpt new file mode 100644 index 0000000..ab58463 --- /dev/null +++ b/tests/004-SoftReference-serialize_extended_not_allowed.phpt @@ -0,0 +1,34 @@ +--TEST-- +Weak\SoftReference - serialize extended reference that tries to implemet Serializable interface is not allowed +--SKIPIF-- + +--FILE-- + +EOF +--EXPECT-- +Fatal error: Class SerializableWeakReference could not implement interface Serializable in Unknown on line 0 diff --git a/tests/004-SoftReference-serialize_not_allowed.phpt b/tests/004-SoftReference-serialize_not_allowed.phpt new file mode 100644 index 0000000..87428db --- /dev/null +++ b/tests/004-SoftReference-serialize_not_allowed.phpt @@ -0,0 +1,43 @@ +--TEST-- +Weak\SoftReference - serialize reference +--SKIPIF-- + +--FILE-- +dump($sr); + +try { + $serialized = serialize($sr); + $helper->dump($serialized); +} catch (\Throwable $e) { + $helper->exception_export($e); +} + +$helper->line(); + +?> +EOF +--EXPECT-- +object(Weak\SoftReference)#3 (2) refcount(3){ + ["referent":"Weak\AbstractReference":private]=> + object(stdClass)#2 (0) refcount(2){ + } + ["notifier":"Weak\AbstractReference":private]=> + object(Closure)#4 (1) refcount(2){ + ["parameter"]=> + array(1) refcount(1){ + ["$reference"]=> + string(10) "" refcount(1) + } + } +} +Exception: Serialization of 'Weak\SoftReference' is not allowed + +EOF diff --git a/tests/004-SoftReference-spl_hash_consistent.phpt b/tests/004-SoftReference-spl_hash_consistent.phpt new file mode 100644 index 0000000..7e6bc95 --- /dev/null +++ b/tests/004-SoftReference-spl_hash_consistent.phpt @@ -0,0 +1,49 @@ +--TEST-- +Weak\SoftReference - spl_object_hash still consistent before and after wrapping +--SKIPIF-- + +--FILE-- +assert('Wrapped object hash matches origin one', $original_hash == $current_hash); + + +$sr2 = new Weak\SoftReference($obj); + +$double_hash = spl_object_hash($obj); +$helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); + +$sr = null; + +$again_hash = spl_object_hash($obj); +$helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); + +$sr2 = null; + +$nullified_hash = spl_object_hash($obj); +$helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); +$helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); + +$helper->line(); +?> +EOF +--EXPECT-- +Wrapped object hash matches origin one: ok +Repeatedly wrapped object hash does not changes: ok +Repeatedly wrapped object hash does not changes after some reference death: ok +Wrapped object hash still not changed even after all references died: ok +Wrapped object hash still the same even after all references died: ok + +EOF diff --git a/tests/004-SoftReference-spl_object_storage_debug_hash_consistent.phpt b/tests/004-SoftReference-spl_object_storage_debug_hash_consistent.phpt new file mode 100644 index 0000000..c433fcb --- /dev/null +++ b/tests/004-SoftReference-spl_object_storage_debug_hash_consistent.phpt @@ -0,0 +1,55 @@ +--TEST-- +Weak\SoftReference - SplObjectStorage hashes in debug output still consistent before and after wrapping +--SKIPIF-- + +--FILE-- +attach($obj); +$sr = new Weak\SoftReference($obj); +$current_hash = spl_object_hash($obj); +$helper->assert('Stored in SplObjectStorage weak-referenced object hash matches origin one', $original_hash == $current_hash); + +ob_start(); +debug_zval_dump($s); +$res = ob_get_contents(); +ob_end_clean(); + +$helper->assert('Object hash in SplObjectStorage debug output not changed', false !== strpos($res, $original_hash)); +$helper->line(); + +debug_zval_dump($original_hash); +debug_zval_dump($current_hash); +echo $res; + +$helper->line(); + +?> +EOF +--EXPECTF-- +Stored in SplObjectStorage weak-referenced object hash matches origin one: ok +Object hash in SplObjectStorage debug output not changed: ok + +string(32) "%s" refcount(2) +string(32) "%s" refcount(2) +object(SplObjectStorage)#2 (1) refcount(2){ + ["storage":"SplObjectStorage":private]=> + array(1) refcount(1){ + ["%s"]=> + array(2) refcount(1){ + ["obj"]=> + object(stdClass)#3 (0) refcount(2){ + } + ["inf"]=> + NULL + } + } +} + +EOF diff --git a/tests/004-SoftReference-spl_object_storage_hash_consistent.phpt b/tests/004-SoftReference-spl_object_storage_hash_consistent.phpt new file mode 100644 index 0000000..a4e6a8e --- /dev/null +++ b/tests/004-SoftReference-spl_object_storage_hash_consistent.phpt @@ -0,0 +1,71 @@ +--TEST-- +Weak\SoftReference - SplObjectStorage::getHash() still consistent before and after wrapping +--SKIPIF-- + +--FILE-- +getHash($obj); + +$sr = new Weak\SoftReference($obj); + +$current_hash = $s->getHash($obj); + +$helper->assert('Wrapped object hash matches origin one', $original_hash == $current_hash); + + +$sr2 = new Weak\SoftReference($obj); + +$double_hash = $s->getHash($obj); +$helper->assert('Repeatedly wrapped object hash does not changes', $current_hash == $double_hash); + +$sr = null; + +$again_hash = $s->getHash($obj); +$helper->assert('Repeatedly wrapped object hash does not changes after some reference death', $current_hash == $again_hash); + +$sr2 = null; + +$nullified_hash = $s->getHash($obj); +$helper->assert('Wrapped object hash still not changed even after all references died', $current_hash == $original_hash); +$helper->assert('Wrapped object hash still the same even after all references died', $current_hash == $nullified_hash); + +$helper->line(); + +$s = new SplObjectStorage(); +$obj = new stdClass(); +$original_hash = spl_object_hash($obj); +$s->attach($obj); +$current_hash = spl_object_hash($obj); +$helper->assert('Stored in SplObjectStorage object hash matches origin one', $original_hash == $current_hash); + +$s = new SplObjectStorage(); +$obj = new stdClass(); +$original_hash = spl_object_hash($obj); +$sr = new Weak\SoftReference($obj); +$s->attach($sr); +$current_hash = spl_object_hash($obj); +$helper->assert('Stored in SplObjectStorage weak-referenced object hash matches origin one', $original_hash == $current_hash); + +$helper->line(); + +?> +EOF +--EXPECT-- +Wrapped object hash matches origin one: ok +Repeatedly wrapped object hash does not changes: ok +Repeatedly wrapped object hash does not changes after some reference death: ok +Wrapped object hash still not changed even after all references died: ok +Wrapped object hash still the same even after all references died: ok + +Stored in SplObjectStorage object hash matches origin one: ok +Stored in SplObjectStorage weak-referenced object hash matches origin one: ok + +EOF