Skip to content

Commit 6d1d1a1

Browse files
committed
Add tests of SplObjectStorage unset/offsetUnset
1 parent 7e59d4a commit 6d1d1a1

File tree

2 files changed

+75
-0
lines changed

2 files changed

+75
-0
lines changed

ext/spl/spl_observer.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ PHPAPI zend_object_handlers spl_handler_SplObjectStorage;
4747
/* Bit flags for marking internal functionality overridden by SplObjectStorage subclasses. */
4848
#define SOS_OVERRIDDEN_READ_DIMENSION 1
4949
#define SOS_OVERRIDDEN_WRITE_DIMENSION 2
50+
#define SOS_OVERRIDDEN_UNSET_DIMENSION 4
5051

5152
typedef struct _spl_SplObjectStorage { /* {{{ */
5253
HashTable storage;
@@ -221,6 +222,9 @@ spl_SplObjectStorageElement *spl_object_storage_attach(spl_SplObjectStorage *int
221222

222223
static int spl_object_storage_detach(spl_SplObjectStorage *intern, zend_object *obj) /* {{{ */
223224
{
225+
if (EXPECTED(!(intern->flags & SOS_OVERRIDDEN_UNSET_DIMENSION))) {
226+
return zend_hash_index_del(&intern->storage, obj->handle);
227+
}
224228
int ret = FAILURE;
225229
zend_hash_key key;
226230
if (spl_object_storage_get_hash(&key, intern, obj) == FAILURE) {
@@ -284,6 +288,11 @@ static zend_object *spl_object_storage_new_ex(zend_class_entry *class_type, zend
284288
SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, ZEND_STR_OFFSETSET)) {
285289
intern->flags |= SOS_OVERRIDDEN_WRITE_DIMENSION;
286290
}
291+
292+
if (intern->fptr_get_hash != NULL ||
293+
SPL_OBJECT_STORAGE_CLASS_HAS_OVERRIDE(class_type, ZEND_STR_OFFSETUNSET)) {
294+
intern->flags |= SOS_OVERRIDDEN_UNSET_DIMENSION;
295+
}
287296
}
288297
break;
289298
}
@@ -487,6 +496,16 @@ static void spl_object_storage_write_dimension(zend_object *object, zval *offset
487496
spl_object_storage_attach_handle(intern, Z_OBJ_P(offset), inf);
488497
}
489498

499+
static void spl_object_storage_unset_dimension(zend_object *object, zval *offset)
500+
{
501+
spl_SplObjectStorage *intern = spl_object_storage_from_obj(object);
502+
if (UNEXPECTED(Z_TYPE_P(offset) != IS_OBJECT || (intern->flags & SOS_OVERRIDDEN_UNSET_DIMENSION))) {
503+
zend_std_unset_dimension(object, offset);
504+
return;
505+
}
506+
zend_hash_index_del(&intern->storage, Z_OBJ_HANDLE_P(offset));
507+
}
508+
490509
/* {{{ Detaches an object from the storage */
491510
PHP_METHOD(SplObjectStorage, detach)
492511
{
@@ -1330,6 +1349,7 @@ PHP_MINIT_FUNCTION(spl_observer)
13301349
spl_handler_SplObjectStorage.read_dimension = spl_object_storage_read_dimension;
13311350
spl_handler_SplObjectStorage.write_dimension = spl_object_storage_write_dimension;
13321351
spl_handler_SplObjectStorage.has_dimension = spl_object_storage_has_dimension;
1352+
spl_handler_SplObjectStorage.unset_dimension = spl_object_storage_unset_dimension;
13331353

13341354
spl_ce_MultipleIterator = register_class_MultipleIterator(zend_ce_iterator);
13351355
spl_ce_MultipleIterator->create_object = spl_SplObjectStorage_new;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
--TEST--
2+
SplObjectStorage unset and destructor edge cases
3+
--FILE--
4+
<?php
5+
class HasDestructor {
6+
public function __destruct() {
7+
echo "In destructor. Should no longer be accessible in \$s:\n";
8+
var_dump($GLOBALS['s']);
9+
10+
throw new RuntimeException("thrown from destructor");
11+
}
12+
}
13+
$o = new stdClass();
14+
$s = new SplObjectStorage();
15+
$s[$o] = new HasDestructor();
16+
try {
17+
unset($s[$o]);
18+
} catch (Exception $e) {
19+
echo "Caught: {$e->getMessage()}\n";
20+
}
21+
var_dump($s);
22+
$s[$o] = new HasDestructor();
23+
try {
24+
$s->offsetUnset($o);
25+
} catch (Exception $e) {
26+
echo "Caught: {$e->getMessage()}\n";
27+
}
28+
29+
var_dump($s);
30+
?>
31+
--EXPECT--
32+
In destructor. Should no longer be accessible in $s:
33+
object(SplObjectStorage)#2 (1) {
34+
["storage":"SplObjectStorage":private]=>
35+
array(0) {
36+
}
37+
}
38+
Caught: thrown from destructor
39+
object(SplObjectStorage)#2 (1) {
40+
["storage":"SplObjectStorage":private]=>
41+
array(0) {
42+
}
43+
}
44+
In destructor. Should no longer be accessible in $s:
45+
object(SplObjectStorage)#2 (1) {
46+
["storage":"SplObjectStorage":private]=>
47+
array(0) {
48+
}
49+
}
50+
Caught: thrown from destructor
51+
object(SplObjectStorage)#2 (1) {
52+
["storage":"SplObjectStorage":private]=>
53+
array(0) {
54+
}
55+
}

0 commit comments

Comments
 (0)