Skip to content
This repository was archived by the owner on Jul 7, 2018. It is now read-only.

Commit 3c84ead

Browse files
committed
Add SoftReference and AbstractReference
1 parent 289f064 commit 3c84ead

File tree

63 files changed

+2674
-154
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2674
-154
lines changed

README.md

+66-27
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
[![Windows Build status](https://ci.appveyor.com/api/projects/status/7r07eydi6c3lj36a/branch/master?svg=true)](https://ci.appveyor.com/project/pinepain/php-weak)
55
[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/pinepain/php-weak/master/LICENSE)
66

7-
This extension provides [weak references](https://en.wikipedia.org/wiki/Weak_reference) support for PHP 7 and serves
8-
as a ground for other weak data structures.
7+
This extension adds [Soft Reference](https://en.wikipedia.org/wiki/Weak_reference) and
8+
[Weak References](https://en.wikipedia.org/wiki/Weak_reference) to PHP 7 and may serve as a ground for other
9+
data structures that require advanced referencing model.
910

1011

1112
## Usage
@@ -14,12 +15,18 @@ as a ground for other weak data structures.
1415
<?php
1516

1617
use Weak\Reference;
18+
use Weak\SoftReference;
1719

18-
$obj = new stdClass();
20+
$obj = new class {
21+
public function __destruct() {
22+
echo 'Destructor called', PHP_EOL;
23+
}
24+
};
1925

20-
$ref = new Reference($obj, function () { echo 'Object destroyed', PHP_EOL; });
26+
$softref = new SoftReference($obj, function () { echo 'Object will be destroyed', PHP_EOL; });
27+
$weakref = new Reference($obj, function () { echo 'Object destroyed', PHP_EOL; });
2128

22-
$obj = null; // outputs "Object destroyed"
29+
$obj = null; // outputs "Object will be destroyed", "Destructor called", "Object destroyed" in this specific order.
2330
```
2431

2532

@@ -29,13 +36,15 @@ This extension adds `Weak` namespace and all entities are created inside it.
2936

3037
There are no INI setting or constants provided by this extension.
3138

32-
Brief docs about [`Weak\Reference` class](./stubs/weak/Reference.php) and [functions](./stubs/weak/functions.php)
39+
Brief docs about classes and [functions](./stubs/weak/functions.php)
3340
may be seen in [stub files](./stubs/weak).
3441

3542
Short list if what provided by this extension is:
3643

37-
- `class Weak\Reference`
38-
- `class Weak\NotifierException extend Exception`
44+
- `abstract class Weak\AbstractReference` *may not be subclassed directly* [doc](./stubs/weak/AbstractReference.php)
45+
- `class Weak\SoftReference extends AbstractReference` [doc](./stubs/weak/SoftReference.php)
46+
- `class Weak\Reference extends AbstractReference` [doc](./stubs/weak/Reference.php)
47+
- `class Weak\NotifierException extend Exception` [doc](./stubs/weak/NotifierException.php)
3948
- `function Weak\refcounted()`
4049
- `function Weak\refcount()`
4150
- `function Weak\softrefcounted()`
@@ -47,6 +56,13 @@ Short list if what provided by this extension is:
4756
- `function Weak\object_handle()`
4857
- `function Weak\is_obj_destructor_called()`
4958

59+
### References
60+
61+
There are two type of reference provided by this extension: `SoftReference` and `Reference`. The main difference is that
62+
`SoftReference` call it notifier before referent object will be destructed which allows to prevent object be destroyed,
63+
while `Reference` call it notifier after referent object was destructed.
64+
65+
Note: What this extension provides aren't quite actual soft and weak references, but it comes close for most use cases.
5066

5167
### Notifiers
5268

@@ -57,14 +73,14 @@ will return `null` (unless rare case when object refcount get incremented in des
5773
somewhere else).
5874

5975
If object destructor or one or more notifiers throw exception, all further notifier callbacks will be called as if
60-
that exception was thrown inside `try-catch` block. In case one or more exception was thrown, `Weak\NotifierException`
76+
that exception was thrown inside `try-catch` block. In case one or more exceptions were thrown, `Weak\NotifierException`
6177
will be thrown and all thrown exceptions will be available via `Weak\NotifierException::getExceptions()` method.
6278

6379

6480
### Cloning
6581

66-
When `Weak\Reference` cloned, notifier cloned too, so when tracking object destroyed, both notifier will be called, but
67-
they will be invoked with different `Weak\Reference` objects.
82+
When reference is cloned, notifier is cloned too, so when tracked object destroyed, both notifier will be called,
83+
but they will be invoked with different reference objects.
6884

6985
```php
7086
<?php
@@ -105,14 +121,14 @@ $obj = null; // outputs "Own notifier called" and then "Object destroyed"
105121

106122
### Serializing
107123

108-
Serializing `Weak\Reference` is prohibited. Attempting to implement the `Serializable` interface will lead to a
124+
Serializing reference object is prohibited. Attempting to implement the `Serializable` interface will lead to a
109125
fatal error.
110126

111127

112128
## Stub files
113129

114-
If you are also using Composer, it is recommended that you add the [php-weak-stub](https://github.com/pinepain/php-weak-stubs)
115-
package as a dev-mode requirement. This provides skeleton definitions and annotations to enable support for auto-completion
130+
If you are also using Composer, it is recommended to add the [php-weak-stub](https://github.com/pinepain/php-weak-stubs)
131+
package as a dev-mode requirement. It provides skeleton definitions and annotations to enable support for auto-completion
116132
in your IDE and other code-analysis tools.
117133

118134
composer require --dev pinepain/php-weak-stubs
@@ -141,7 +157,7 @@ To install extension globally run
141157

142158
# sudo make install
143159

144-
You will need to copy the extension config to your php dir, here is example for Ubuntu with PHP 7 from
160+
You will need to copy the extension config to your php dir, here is example for Ubuntu with PHP 7.0 from
145161
[Ondřej Surý's PPA for PHP](https://launchpad.net/~ondrej/+archive/ubuntu/php):
146162

147163
# sudo cp provision/php/weak.ini /etc/php/mods-available/
@@ -160,23 +176,17 @@ You may also want to add php-weak extension as a [composer.json dependency](http
160176
## Internals
161177

162178
`Weak\Reference` class is implemented by storing tracked object handlers and then wrapping it original `dtor_obj` handler
163-
with custom one, which meta-code is:
179+
with a custom one, which meta-code is:
164180

165181
```php
166182
$exceptions = [];
167183

168-
try {
169-
run_original_dtor_obj($object);
170-
} catch(Throwable $e) {
171-
$exceptions[] = $e;
172-
}
173-
174-
foreach($weak_references as $weak_ref_object_handle => $weak_reference) {
184+
foreach($soft_references as $soft_ref_object_handle => $soft_reference) {
175185
if (is_array($weak_reference->notifier)) {
176-
$weak_reference->notifier[] = $weak_reference;
177-
} elseif (is_callable($weak_reference->notifier)) {
186+
$soft_reference->notifier[] = $weak_reference;
187+
} elseif (is_callable($soft_reference->notifier)) {
178188
try {
179-
$weak_reference->notifier($weak_reference);
189+
$soft_reference->notifier($weak_reference);
180190
} catch(Throwable $e) {
181191
$exceptions[] = $e;
182192
}
@@ -186,11 +196,38 @@ foreach($weak_references as $weak_ref_object_handle => $weak_reference) {
186196
if ($exceptions) {
187197
throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions);
188198
}
199+
200+
if (refcount($object) == 1) {
201+
try {
202+
run_original_dtor_obj($object);
203+
} catch(Throwable $e) {
204+
$exceptions[] = $e;
205+
}
206+
207+
foreach($weak_references as $weak_ref_object_handle => $weak_reference) {
208+
if (is_array($weak_reference->notifier)) {
209+
$weak_reference->notifier[] = $weak_reference;
210+
} elseif (is_callable($weak_reference->notifier)) {
211+
try {
212+
$weak_reference->notifier($weak_reference);
213+
} catch(Throwable $e) {
214+
$exceptions[] = $e;
215+
}
216+
}
217+
}
218+
219+
if ($exceptions) {
220+
throw new Weak\NotifierException('One or more exceptions thrown during notifiers calling', $exceptions);
221+
}
222+
} else {
223+
// required while internally PHP GC mark object as it dtor was called before calling dtor
224+
mark_object_as_no_destructor_was_called($object);
225+
}
189226
```
190227

191228
## Development and testing
192229

193-
This extension shipped with Vagrant file which provides basic environment for development and testing purposes.
230+
This extension shipped with Vagrant file which provides basic environment for development and testing purposes.
194231
To start it, just type `vagrant up` and then `vagrant ssh` in php-weak directory.
195232

196233
Services available out of the box are:
@@ -208,12 +245,14 @@ between large variety of PHP versions.
208245

209246
## Reference:
210247

248+
[Soft reference on Wikipedia](https://en.wikipedia.org/wiki/Soft_reference)
211249
[Weak reference on Wikipedia](https://en.wikipedia.org/wiki/Weak_reference)
212250

213251
#### In other languages:
214252

215253
##### Java:
216254

255+
- [Class `SoftReference<T>`](https://docs.oracle.com/javase/7/docs/api/java/lang/ref/SoftReference.html)
217256
- [Class `WeakReference<T>`](https://docs.oracle.com/javase/7/docs/api/java/lang/ref/WeakReference.html)
218257
- [Guidelines for using the Java 2 reference classes](http://www.ibm.com/developerworks/library/j-refs/)
219258
- [Strong, Soft, Weak and Phantom References](http://neverfear.org/blog/view/150/Strong_Soft_Weak_and_Phantom_References_Java)

php_weak_reference.c

+83-7
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@
2222
#include "ext/spl/spl_observer.h"
2323
#endif
2424

25-
zend_class_entry *php_weak_reference_class_entry;
26-
#define this_ce php_weak_reference_class_entry
25+
zend_class_entry *php_weak_abstract_reference_class_entry;
26+
zend_class_entry *php_weak_soft_reference_class_entry;
27+
zend_class_entry *php_weak_weak_reference_class_entry;
28+
29+
#define this_ce php_weak_abstract_reference_class_entry
2730

2831
zend_object_handlers php_weak_reference_object_handlers;
2932

@@ -349,6 +352,26 @@ php_weak_referent_t *php_weak_referent_get_or_create(zval *referent_zv) /* {{{ *
349352
return referent;
350353
} /* }}} */
351354

355+
void php_weak_soft_reference_attach(php_weak_reference_t *reference, php_weak_referent_t *referent) /* {{{ */
356+
{
357+
reference->referent = referent;
358+
zend_hash_index_add_ptr(&referent->soft_references, (zend_ulong) Z_OBJ_HANDLE_P(&reference->this_ptr), reference);
359+
} /* }}} */
360+
361+
void php_weak_soft_reference_unregister(php_weak_reference_t *reference) /* {{{ */
362+
{
363+
zend_hash_index_del(&reference->referent->soft_references, (zend_ulong) Z_OBJ_HANDLE_P(&reference->this_ptr));
364+
} /* }}} */
365+
366+
void php_weak_soft_reference_maybe_unregister(php_weak_reference_t *reference) /* {{{ */
367+
{
368+
if (NULL == reference->referent) {
369+
return;
370+
}
371+
372+
php_weak_soft_reference_unregister(reference);
373+
} /* }}} */
374+
352375
void php_weak_reference_attach(php_weak_reference_t *reference, php_weak_referent_t *referent) /* {{{ */
353376
{
354377
reference->referent = referent;
@@ -483,7 +506,43 @@ static void php_weak_reference_dtor(zend_object *object) /* {{{ */
483506
zend_objects_destroy_object(object);
484507
} /* }}} */
485508

486-
static zend_object *php_weak_reference_ctor(zend_class_entry *ce) /* {{{ */
509+
static zend_object *php_weak_abstract_reference_ctor(zend_class_entry *ce) /* {{{ */
510+
{
511+
php_weak_reference_t *reference;
512+
513+
reference = (php_weak_reference_t *) ecalloc(1, sizeof(php_weak_reference_t) + zend_object_properties_size(ce));
514+
515+
zend_object_std_init(&reference->std, ce);
516+
object_properties_init(&reference->std, ce);
517+
518+
reference->std.handlers = &php_weak_reference_object_handlers;
519+
520+
reference->register_reference = NULL;
521+
reference->unregister_reference = NULL;
522+
523+
zend_throw_error(NULL, "%s class may not be subclassed directly", ZSTR_VAL(this_ce->name));
524+
525+
return &reference->std;
526+
} /* }}} */
527+
528+
static zend_object *php_weak_soft_reference_ctor(zend_class_entry *ce) /* {{{ */
529+
{
530+
php_weak_reference_t *reference;
531+
532+
reference = (php_weak_reference_t *) ecalloc(1, sizeof(php_weak_reference_t) + zend_object_properties_size(ce));
533+
534+
zend_object_std_init(&reference->std, ce);
535+
object_properties_init(&reference->std, ce);
536+
537+
reference->std.handlers = &php_weak_reference_object_handlers;
538+
539+
reference->register_reference = php_weak_soft_reference_attach;
540+
reference->unregister_reference = php_weak_soft_reference_maybe_unregister;
541+
542+
return &reference->std;
543+
} /* }}} */
544+
545+
static zend_object *php_weak_weak_reference_ctor(zend_class_entry *ce) /* {{{ */
487546
{
488547
php_weak_reference_t *reference;
489548

@@ -507,7 +566,7 @@ static zend_object *php_weak_reference_clone_obj(zval *object) /* {{{ */
507566

508567
old_object = Z_OBJ_P(object);
509568

510-
new_object = php_weak_reference_ctor(old_object->ce);
569+
new_object = old_object->ce->create_object(old_object->ce);
511570

512571
php_weak_reference_t *old_reference = php_weak_reference_fetch_object(old_object);
513572
php_weak_reference_t *new_reference = php_weak_reference_fetch_object(new_object);
@@ -689,7 +748,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_weak_reference_notifier, ZEND_SEND_BY_VAL, ZEND_R
689748
ZEND_END_ARG_INFO()
690749

691750

692-
static const zend_function_entry php_weak_reference_methods[] = { /* {{{ */
751+
static const zend_function_entry php_weak_abstract_reference_methods[] = { /* {{{ */
693752
PHP_ME(WeakReference, __construct, arginfo_weak_reference___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
694753

695754
PHP_ME(WeakReference, get, arginfo_weak_reference_get, ZEND_ACC_PUBLIC)
@@ -699,14 +758,23 @@ static const zend_function_entry php_weak_reference_methods[] = { /* {{{ */
699758
PHP_FE_END
700759
}; /* }}} */
701760

761+
static const zend_function_entry php_weak_weak_reference_methods[] = { /* {{{ */
762+
PHP_FE_END
763+
}; /* }}} */
764+
765+
static const zend_function_entry php_weak_soft_reference_methods[] = { /* {{{ */
766+
PHP_FE_END
767+
}; /* }}} */
768+
702769

703770
PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */
704771
{
705772
zend_class_entry ce;
706773

707-
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_reference_methods);
774+
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "AbstractReference", php_weak_abstract_reference_methods);
708775
this_ce = zend_register_internal_class(&ce);
709-
this_ce->create_object = php_weak_reference_ctor;
776+
this_ce->ce_flags |= ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
777+
this_ce->create_object = php_weak_abstract_reference_ctor;
710778
this_ce->serialize = zend_class_serialize_deny;
711779
this_ce->unserialize = zend_class_unserialize_deny;
712780

@@ -720,6 +788,14 @@ PHP_MINIT_FUNCTION (php_weak_reference) /* {{{ */
720788
php_weak_reference_object_handlers.get_debug_info = php_weak_get_debug_info;
721789
php_weak_reference_object_handlers.compare_objects = php_weak_compare_objects;
722790

791+
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "SoftReference", php_weak_weak_reference_methods);
792+
php_weak_soft_reference_class_entry = zend_register_internal_class_ex(&ce, php_weak_abstract_reference_class_entry);
793+
php_weak_soft_reference_class_entry->create_object = php_weak_soft_reference_ctor;
794+
795+
INIT_NS_CLASS_ENTRY(ce, PHP_WEAK_NS, "Reference", php_weak_weak_reference_methods);
796+
php_weak_weak_reference_class_entry = zend_register_internal_class_ex(&ce, php_weak_abstract_reference_class_entry);
797+
php_weak_weak_reference_class_entry->create_object = php_weak_weak_reference_ctor;
798+
723799
return SUCCESS;
724800
} /* }}} */
725801

php_weak_reference.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include "TSRM.h"
2222
#endif
2323

24-
extern zend_class_entry *php_weak_reference_class_entry;
24+
extern zend_class_entry *php_weak_abstract_reference_class_entry;
25+
extern zend_class_entry *php_weak_soft_reference_class_entry;
26+
extern zend_class_entry *php_weak_weak_reference_class_entry;
2527

2628

2729
typedef struct _php_weak_referent_t php_weak_referent_t;

0 commit comments

Comments
 (0)