Skip to content

Commit 5b45a55

Browse files
committed
PHPC-2505: Add tests for foreach after GC invocation
Add three new tests (bug2505-001, -002, -003) that verify foreach iteration on BSON objects is consistent before and after gc_collect_cycles(). Also remove the PHP < 8.2 skip condition from tests/bson/bug1598-002.phpt since the GC behavior being tested is relevant on all supported PHP versions.
1 parent 2f18e42 commit 5b45a55

26 files changed

Lines changed: 477 additions & 49 deletions

phongo.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,6 @@ static zend_class_entry* phongo_fetch_internal_class(const char* class_name, siz
155155
return NULL;
156156
}
157157

158-
static HashTable* phongo_std_get_gc(zend_object* object, zval** table, int* n)
159-
{
160-
*table = NULL;
161-
*n = 0;
162-
return zend_std_get_properties(object);
163-
}
164-
165158
PHP_MINIT_FUNCTION(mongodb) /* {{{ */
166159
{
167160
bson_mem_vtable_t bson_mem_vtable = {
@@ -197,9 +190,6 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */
197190
/* Disable cloning by default. Individual classes can opt in if they need to
198191
* support this (e.g. BSON objects). */
199192
phongo_std_object_handlers.clone_obj = NULL;
200-
/* Ensure that get_gc delegates to zend_std_get_properties directly in case
201-
* our class defines a get_properties handler for debugging purposes. */
202-
phongo_std_object_handlers.get_gc = phongo_std_get_gc;
203193

204194
/* Initialize zend_class_entry dependencies.
205195
*

phongo.h

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,105 @@ zend_object_handlers* phongo_get_std_object_handlers(void);
7777
#define PHONGO_GET_PROPERTY_HASH_FREE_PROPS(is_temp, props) \
7878
do { \
7979
if (is_temp) { \
80-
zend_hash_destroy((props)); \
81-
FREE_HASHTABLE(props); \
80+
zend_hash_release((props)); \
8281
} \
8382
} while (0)
8483

84+
#define PHONGO_ASSIGN_PROPERTY_HANDLERS(_name) \
85+
do { \
86+
phongo_handler_##_name.read_property = phongo_##_name##_read_property; \
87+
phongo_handler_##_name.write_property = phongo_##_name##_write_property; \
88+
phongo_handler_##_name.has_property = phongo_##_name##_has_property; \
89+
phongo_handler_##_name.unset_property = phongo_##_name##_unset_property; \
90+
phongo_handler_##_name.get_property_ptr_ptr = phongo_##_name##_get_property_ptr_ptr; \
91+
phongo_handler_##_name.get_gc = phongo_##_name##_get_gc; \
92+
} while (0)
93+
94+
#define PHONGO_DEFINE_PROPERTY_HANDLERS(_name, _intern_extractor) \
95+
static zval* phongo_##_name##_read_property(zend_object* zobj, zend_string* member, int type, void** cache_slot, zval* rv) \
96+
{ \
97+
HashTable* props = _intern_extractor(zobj)->php_properties; \
98+
if (!props) { \
99+
ALLOC_HASHTABLE(props); \
100+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
101+
_intern_extractor(zobj)->php_properties = props; \
102+
} \
103+
zval* ret = zend_hash_find(props, member); \
104+
if (ret) { \
105+
return ret; \
106+
} \
107+
return &EG(uninitialized_zval); \
108+
} \
109+
\
110+
static zval* phongo_##_name##_write_property(zend_object* zobj, zend_string* name, zval* value, void** cache_slot) \
111+
{ \
112+
Z_TRY_ADDREF_P(value); \
113+
HashTable* props = _intern_extractor(zobj)->php_properties; \
114+
if (!props) { \
115+
ALLOC_HASHTABLE(props); \
116+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
117+
_intern_extractor(zobj)->php_properties = props; \
118+
} \
119+
return zend_hash_add_new(props, name, value); \
120+
} \
121+
\
122+
static int phongo_##_name##_has_property(zend_object* zobj, zend_string* name, int has_set_exists, void** cache_slot) \
123+
{ \
124+
HashTable* props = _intern_extractor(zobj)->php_properties; \
125+
if (!props) { \
126+
ALLOC_HASHTABLE(props); \
127+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
128+
_intern_extractor(zobj)->php_properties = props; \
129+
} \
130+
zval* value = zend_hash_find(props, name); \
131+
if (value) { \
132+
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) { \
133+
return zend_is_true(value); \
134+
} \
135+
if (has_set_exists < ZEND_PROPERTY_NOT_EMPTY) { \
136+
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_ISSET); \
137+
ZVAL_DEREF(value); \
138+
return (Z_TYPE_P(value) != IS_NULL); \
139+
} \
140+
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_EXISTS); \
141+
return true; \
142+
} \
143+
return false; \
144+
} \
145+
\
146+
static void phongo_##_name##_unset_property(zend_object* zobj, zend_string* name, void** cache_slot) \
147+
{ \
148+
HashTable* props = _intern_extractor(zobj)->php_properties; \
149+
if (!props) { \
150+
ALLOC_HASHTABLE(props); \
151+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
152+
_intern_extractor(zobj)->php_properties = props; \
153+
} \
154+
zend_hash_del(props, name); \
155+
} \
156+
\
157+
static zval* phongo_##_name##_get_property_ptr_ptr(zend_object* zobj, zend_string* name, int type, void** cache_slot) \
158+
{ \
159+
HashTable* props = _intern_extractor(zobj)->php_properties; \
160+
if (!props) { \
161+
ALLOC_HASHTABLE(props); \
162+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
163+
_intern_extractor(zobj)->php_properties = props; \
164+
} \
165+
zval* value = zend_hash_find(props, name); \
166+
if (value) { \
167+
return value; \
168+
} \
169+
return zend_hash_add(props, name, &EG(uninitialized_zval)); \
170+
} \
171+
\
172+
static HashTable* phongo_##_name##_get_gc(zend_object* zobj, zval** table, int* n) \
173+
{ \
174+
*table = NULL; \
175+
*n = 0; \
176+
return _intern_extractor(zobj)->php_properties; \
177+
}
178+
85179
#define PHONGO_ZVAL_EXCEPTION_NAME(e) (ZSTR_VAL(e->ce->name))
86180

87181
#define PHONGO_SET_CREATED_BY_PID(intern) \

src/BSON/Binary.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,11 @@ static void phongo_binary_free_object(zend_object* object)
216216
}
217217

218218
if (intern->properties) {
219-
zend_hash_destroy(intern->properties);
220-
FREE_HASHTABLE(intern->properties);
219+
zend_hash_release(intern->properties);
220+
}
221+
222+
if (intern->php_properties) {
223+
zend_hash_release(intern->php_properties);
221224
}
222225
}
223226

@@ -308,6 +311,8 @@ static HashTable* phongo_binary_get_properties(zend_object* object)
308311
return phongo_binary_get_properties_hash(object, false, false);
309312
}
310313

314+
PHONGO_DEFINE_PROPERTY_HANDLERS(binary, Z_OBJ_BINARY);
315+
311316
void phongo_binary_init_ce(INIT_FUNC_ARGS)
312317
{
313318
phongo_binary_ce = register_class_MongoDB_BSON_Binary(phongo_binary_interface_ce, phongo_json_serializable_ce, phongo_type_ce, zend_ce_stringable);
@@ -320,6 +325,8 @@ void phongo_binary_init_ce(INIT_FUNC_ARGS)
320325
phongo_handler_binary.get_properties = phongo_binary_get_properties;
321326
phongo_handler_binary.free_obj = phongo_binary_free_object;
322327
phongo_handler_binary.offset = XtOffsetOf(phongo_binary_t, std);
328+
329+
PHONGO_ASSIGN_PROPERTY_HANDLERS(binary);
323330
}
324331

325332
bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type)

src/BSON/DBPointer.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,11 @@ static void phongo_dbpointer_free_object(zend_object* object)
171171
}
172172

173173
if (intern->properties) {
174-
zend_hash_destroy(intern->properties);
175-
FREE_HASHTABLE(intern->properties);
174+
zend_hash_release(intern->properties);
175+
}
176+
177+
if (intern->php_properties) {
178+
zend_hash_release(intern->php_properties);
176179
}
177180
}
178181

@@ -232,6 +235,8 @@ static HashTable* phongo_dbpointer_get_properties(zend_object* object)
232235
return phongo_dbpointer_get_properties_hash(object, false);
233236
}
234237

238+
PHONGO_DEFINE_PROPERTY_HANDLERS(dbpointer, Z_OBJ_DBPOINTER);
239+
235240
void phongo_dbpointer_init_ce(INIT_FUNC_ARGS)
236241
{
237242
phongo_dbpointer_ce = register_class_MongoDB_BSON_DBPointer(phongo_json_serializable_ce, phongo_type_ce, zend_ce_stringable);
@@ -244,6 +249,8 @@ void phongo_dbpointer_init_ce(INIT_FUNC_ARGS)
244249
phongo_handler_dbpointer.get_properties = phongo_dbpointer_get_properties;
245250
phongo_handler_dbpointer.free_obj = phongo_dbpointer_free_object;
246251
phongo_handler_dbpointer.offset = XtOffsetOf(phongo_dbpointer_t, std);
252+
253+
PHONGO_ASSIGN_PROPERTY_HANDLERS(dbpointer);
247254
}
248255

249256
bool phongo_dbpointer_new(zval* object, const char* ref, size_t ref_len, const bson_oid_t* oid)

src/BSON/Decimal128.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,11 @@ static void phongo_decimal128_free_object(zend_object* object)
159159
zend_object_std_dtor(&intern->std);
160160

161161
if (intern->properties) {
162-
zend_hash_destroy(intern->properties);
163-
FREE_HASHTABLE(intern->properties);
162+
zend_hash_release(intern->properties);
163+
}
164+
165+
if (intern->php_properties) {
166+
zend_hash_release(intern->php_properties);
164167
}
165168
}
166169

@@ -203,6 +206,8 @@ static HashTable* phongo_decimal128_get_properties(zend_object* object)
203206
return phongo_decimal128_get_properties_hash(object, false);
204207
}
205208

209+
PHONGO_DEFINE_PROPERTY_HANDLERS(decimal128, Z_OBJ_DECIMAL128);
210+
206211
void phongo_decimal128_init_ce(INIT_FUNC_ARGS)
207212
{
208213
phongo_decimal128_ce = register_class_MongoDB_BSON_Decimal128(phongo_decimal128_interface_ce, phongo_json_serializable_ce, phongo_type_ce, zend_ce_stringable);
@@ -214,6 +219,8 @@ void phongo_decimal128_init_ce(INIT_FUNC_ARGS)
214219
phongo_handler_decimal128.get_properties = phongo_decimal128_get_properties;
215220
phongo_handler_decimal128.free_obj = phongo_decimal128_free_object;
216221
phongo_handler_decimal128.offset = XtOffsetOf(phongo_decimal128_t, std);
222+
223+
PHONGO_ASSIGN_PROPERTY_HANDLERS(decimal128);
217224
}
218225

219226
bool phongo_decimal128_new(zval* object, const bson_decimal128_t* decimal)

src/BSON/Document.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,7 @@ static void phongo_document_free_object(zend_object* object)
425425
}
426426

427427
if (intern->properties) {
428-
zend_hash_destroy(intern->properties);
429-
FREE_HASHTABLE(intern->properties);
428+
zend_hash_release(intern->properties);
430429
}
431430
}
432431

src/BSON/Int64.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,11 @@ static void phongo_int64_free_object(zend_object* object)
167167
zend_object_std_dtor(&intern->std);
168168

169169
if (intern->properties) {
170-
zend_hash_destroy(intern->properties);
171-
FREE_HASHTABLE(intern->properties);
170+
zend_hash_release(intern->properties);
171+
}
172+
173+
if (intern->php_properties) {
174+
zend_hash_release(intern->php_properties);
172175
}
173176
}
174177

@@ -539,6 +542,8 @@ static HashTable* phongo_int64_get_properties(zend_object* object)
539542
return phongo_int64_get_properties_hash(object, false);
540543
}
541544

545+
PHONGO_DEFINE_PROPERTY_HANDLERS(int64, Z_OBJ_INT64);
546+
542547
void phongo_int64_init_ce(INIT_FUNC_ARGS)
543548
{
544549
phongo_int64_ce = register_class_MongoDB_BSON_Int64(phongo_json_serializable_ce, phongo_type_ce, zend_ce_stringable);
@@ -553,6 +558,8 @@ void phongo_int64_init_ce(INIT_FUNC_ARGS)
553558
phongo_handler_int64.offset = XtOffsetOf(phongo_int64_t, std);
554559
phongo_handler_int64.cast_object = phongo_int64_cast_object;
555560
phongo_handler_int64.do_operation = phongo_int64_do_operation;
561+
562+
PHONGO_ASSIGN_PROPERTY_HANDLERS(int64);
556563
}
557564

558565
bool phongo_int64_new(zval* object, int64_t integer)

src/BSON/Iterator.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,11 @@ static void phongo_iterator_free_object(zend_object* object)
246246
zend_object_std_dtor(&intern->std);
247247

248248
if (intern->properties) {
249-
zend_hash_destroy(intern->properties);
250-
FREE_HASHTABLE(intern->properties);
249+
zend_hash_release(intern->properties);
250+
}
251+
252+
if (intern->php_properties) {
253+
zend_hash_release(intern->php_properties);
251254
}
252255

253256
phongo_iterator_free_current(intern);
@@ -372,6 +375,8 @@ static zend_object_iterator* phongo_iterator_get_iterator(zend_class_entry* ce,
372375
return iterator;
373376
}
374377

378+
PHONGO_DEFINE_PROPERTY_HANDLERS(iterator, Z_OBJ_ITERATOR);
379+
375380
void phongo_iterator_init_ce(INIT_FUNC_ARGS)
376381
{
377382
phongo_iterator_ce = register_class_MongoDB_BSON_Iterator(zend_ce_iterator);
@@ -384,4 +389,6 @@ void phongo_iterator_init_ce(INIT_FUNC_ARGS)
384389
phongo_handler_iterator.get_properties = phongo_iterator_get_properties;
385390
phongo_handler_iterator.free_obj = phongo_iterator_free_object;
386391
phongo_handler_iterator.offset = XtOffsetOf(phongo_iterator_t, std);
392+
393+
PHONGO_ASSIGN_PROPERTY_HANDLERS(iterator);
387394
}

src/BSON/Javascript.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,8 +250,11 @@ static void phongo_javascript_free_object(zend_object* object)
250250
}
251251

252252
if (intern->properties) {
253-
zend_hash_destroy(intern->properties);
254-
FREE_HASHTABLE(intern->properties);
253+
zend_hash_release(intern->properties);
254+
}
255+
256+
if (intern->php_properties) {
257+
zend_hash_release(intern->php_properties);
255258
}
256259
}
257260

@@ -306,6 +309,8 @@ static HashTable* phongo_javascript_get_properties(zend_object* object)
306309
return phongo_javascript_get_properties_hash(object, false);
307310
}
308311

312+
PHONGO_DEFINE_PROPERTY_HANDLERS(javascript, Z_OBJ_JAVASCRIPT);
313+
309314
void phongo_javascript_init_ce(INIT_FUNC_ARGS)
310315
{
311316
phongo_javascript_ce = register_class_MongoDB_BSON_Javascript(phongo_javascript_interface_ce, phongo_json_serializable_ce, phongo_type_ce, zend_ce_stringable);
@@ -318,6 +323,8 @@ void phongo_javascript_init_ce(INIT_FUNC_ARGS)
318323
phongo_handler_javascript.get_properties = phongo_javascript_get_properties;
319324
phongo_handler_javascript.free_obj = phongo_javascript_free_object;
320325
phongo_handler_javascript.offset = XtOffsetOf(phongo_javascript_t, std);
326+
327+
PHONGO_ASSIGN_PROPERTY_HANDLERS(javascript);
321328
}
322329

323330
bool phongo_javascript_new(zval* object, const char* code, size_t code_len, const bson_t* scope)

0 commit comments

Comments
 (0)