Skip to content

Commit 2f4d154

Browse files
committed
Override has_dimension handler for SplObjectStorage
Preserve existing behavior
1 parent 84cd786 commit 2f4d154

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

ext/spl/spl_observer.c

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,20 +399,22 @@ static zend_object *spl_SplObjectStorage_new(zend_class_entry *class_type)
399399
}
400400
/* }}} */
401401

402+
/* Returns 1 if the SplObjectStorage contains an entry for getHash(obj), even if the corresponding value is null. */
402403
int spl_object_storage_contains(spl_SplObjectStorage *intern, zend_object *obj) /* {{{ */
403404
{
405+
if (EXPECTED(!intern->fptr_get_hash)) {
406+
return zend_hash_index_find(&intern->storage, obj->handle) != NULL;
407+
}
404408
int found;
405409
zend_hash_key key;
406410
if (spl_object_storage_get_hash(&key, intern, obj) == FAILURE) {
407411
return 0;
408412
}
409413

410-
if (key.key) {
411-
found = zend_hash_exists(&intern->storage, key.key);
412-
} else {
413-
found = zend_hash_index_exists(&intern->storage, key.h);
414-
}
415-
spl_object_storage_free_hash(intern, &key);
414+
ZEND_ASSERT(key.key);
415+
found = zend_hash_exists(&intern->storage, key.key);
416+
zend_string_release_ex(key.key, 0);
417+
416418
return found;
417419
} /* }}} */
418420

@@ -432,6 +434,25 @@ PHP_METHOD(SplObjectStorage, attach)
432434
spl_object_storage_attach(intern, obj, inf);
433435
} /* }}} */
434436

437+
static int spl_object_storage_has_dimension(zend_object *object, zval *offset, int check_empty)
438+
{
439+
spl_SplObjectStorage *intern = spl_object_storage_from_obj(object);
440+
if (UNEXPECTED(offset == NULL || Z_TYPE_P(offset) != IS_OBJECT || (intern->flags & SOS_OVERRIDDEN_READ_DIMENSION))) {
441+
/* Can't optimize empty()/isset() check if getHash, offsetExists, or offsetGet is overridden */
442+
return zend_std_has_dimension(object, offset, check_empty);
443+
}
444+
spl_SplObjectStorageElement *element = zend_hash_index_find_ptr(&intern->storage, Z_OBJ_HANDLE_P(offset));
445+
if (!element) {
446+
return 0;
447+
}
448+
449+
if (check_empty) {
450+
return i_zend_is_true(&element->inf);
451+
}
452+
/* NOTE: SplObjectStorage->offsetExists() is an alias of SplObjectStorage->contains(), so this returns true even if the value is null. */
453+
return 1;
454+
}
455+
435456
static zval *spl_object_storage_read_dimension(zend_object *object, zval *offset, int type, zval *rv)
436457
{
437458
spl_SplObjectStorage *intern = spl_object_storage_from_obj(object);
@@ -1307,6 +1328,7 @@ PHP_MINIT_FUNCTION(spl_observer)
13071328
spl_handler_SplObjectStorage.free_obj = spl_SplObjectStorage_free_storage;
13081329
spl_handler_SplObjectStorage.read_dimension = spl_object_storage_read_dimension;
13091330
spl_handler_SplObjectStorage.write_dimension = spl_object_storage_write_dimension;
1331+
spl_handler_SplObjectStorage.has_dimension = spl_object_storage_has_dimension;
13101332

13111333
spl_ce_MultipleIterator = register_class_MultipleIterator(zend_ce_iterator);
13121334
spl_ce_MultipleIterator->create_object = spl_SplObjectStorage_new;

ext/spl/tests/SplObjectStorage_coalesce.phpt

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,22 @@ var_dump($s[$o1] ??= $i);
1212
var_dump($s[$o1] ??= null);
1313
var_dump($s[$o1] ??= false);
1414
var_dump($s[$o1] ?? $i);
15-
$s[$o2] = null;
15+
var_dump(isset($s[$o1]));
16+
var_dump(empty($s[$o1]));
17+
echo "o2\n";
1618
var_dump(isset($s[$o2]));
19+
var_dump(empty($s[$o2]));
20+
$s[$o2] = null;
1721
var_dump($s[$o2] ?? new stdClass());
22+
echo "check isset/empty/contains for null. offsetExists returns true as long as the entry is there.\n";
23+
var_dump(isset($s[$o2]));
24+
var_dump(empty($s[$o2]));
25+
var_dump($s->contains($o2));
26+
echo "check isset/empty/contains for false.\n";
27+
$s[$o2] = false;
1828
var_dump(isset($s[$o2]));
29+
var_dump(empty($s[$o2]));
30+
var_dump($s->contains($o2));
1931
try {
2032
$s['invalid'] = 123;
2133
} catch (Error $e) {
@@ -38,13 +50,24 @@ string(7) "dynamic"
3850
string(7) "dynamic"
3951
string(7) "dynamic"
4052
bool(true)
53+
bool(false)
54+
o2
55+
bool(false)
56+
bool(true)
4157
object(stdClass)#4 (0) {
4258
}
59+
check isset/empty/contains for null. offsetExists returns true as long as the entry is there.
60+
bool(true)
61+
bool(true)
62+
bool(true)
63+
check isset/empty/contains for false.
64+
bool(true)
65+
bool(true)
4366
bool(true)
4467
TypeError: SplObjectStorage::offsetSet(): Argument #1 ($object) must be of type object, string given
4568
TypeError: SplObjectStorage::offsetExists(): Argument #1 ($object) must be of type object, string given
4669

47-
Notice: Indirect modification of overloaded element of SplObjectStorage has no effect in %s on line 26
70+
Notice: Indirect modification of overloaded element of SplObjectStorage has no effect in %s on line 38
4871
object(SplObjectStorage)#1 (1) {
4972
["storage":"SplObjectStorage":private]=>
5073
array(2) {
@@ -62,7 +85,7 @@ object(SplObjectStorage)#1 (1) {
6285
object(stdClass)#3 (0) {
6386
}
6487
["inf"]=>
65-
NULL
88+
bool(false)
6689
}
6790
}
6891
}

0 commit comments

Comments
 (0)