Skip to content

Fix foreach after the garbage collector is invoked #1850

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 47 commits into
base: v2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5324acf
Refactor
danog Jul 17, 2025
880a4fc
Fix
danog Jul 17, 2025
c8808c5
Add test
danog Jul 17, 2025
fe87c0e
Test
danog Jul 17, 2025
f8f1dcf
Cleanup
danog Jul 17, 2025
70c6c8c
Small fix
danog Jul 21, 2025
1ab1886
Tmp
danog Jul 21, 2025
bde2b37
Cleanup
danog Jul 21, 2025
63407c0
Tmp
danog Jul 22, 2025
f83cb3f
Fix test
danog Jul 22, 2025
9dff9d3
Add another test
danog Jul 22, 2025
4857be7
Cleanup
danog Jul 22, 2025
460aa36
Cleanup
danog Jul 22, 2025
d0c8206
Fix
danog Jul 22, 2025
0afea8b
Merge remote-tracking branch 'origin/v2.x' into v2.x
danog Jul 22, 2025
f89a436
Fix
danog Jul 22, 2025
77d3706
Fix
danog Jul 22, 2025
fb6594f
Fix
danog Jul 22, 2025
04774df
Add missing handler
danog Jul 22, 2025
0593616
Add one more test
danog Jul 22, 2025
da6e6fc
Fix
danog Jul 22, 2025
4d39faf
Cleanup
danog Jul 22, 2025
edb8098
Fix
danog Jul 22, 2025
6499672
Fix
danog Jul 22, 2025
6e8575c
Fix
danog Jul 22, 2025
c84f9e9
Add separate php_properties attr
danog Jul 23, 2025
ec237b0
Cleanup
danog Jul 23, 2025
2c21995
Setup handlers
danog Jul 23, 2025
efb8c74
Setup handlers
danog Jul 23, 2025
9d66170
Testi
danog Jul 23, 2025
be65b64
Fixup ordering
danog Jul 24, 2025
7850762
Fixup
danog Jul 24, 2025
4584698
Fixup
danog Jul 24, 2025
7cd8bc2
Bump
danog Jul 24, 2025
78cb221
Fix foreach after the garbage collector is invoked or after a dynamic…
danog Jul 24, 2025
17434b9
Fix
danog Jul 24, 2025
7d477cc
The test is now correct
danog Jul 24, 2025
263b20c
Add custom gc handlers
danog Jul 24, 2025
3b4f176
clang-format
danog Jul 25, 2025
2520bfc
More fixes
danog Jul 25, 2025
a3c767d
Add get_gc handler
danog Jul 25, 2025
85bd361
Fixes
danog Jul 25, 2025
6331ace
Merge branch 'v2.x' into fix_foreach
danog Jul 25, 2025
9343af5
Fix
danog Jul 25, 2025
ae08bbc
Merge remote-tracking branch 'origin/v2.x' into fix_foreach
danog Jul 25, 2025
cb33911
Fix leak
danog Jul 29, 2025
262d68c
Fix wrong format caused by clang-format
alcaeus Jul 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions php_phongo.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ static zend_class_entry* php_phongo_fetch_internal_class(const char* class_name,
return NULL;
}

static HashTable* php_phongo_std_get_gc(zend_object* object, zval** table, int* n)
{
*table = NULL;
*n = 0;
return zend_std_get_properties(object);
}

PHP_MINIT_FUNCTION(mongodb) /* {{{ */
{
bson_mem_vtable_t bson_mem_vtable = {
Expand Down Expand Up @@ -197,9 +190,6 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */
/* Disable cloning by default. Individual classes can opt in if they need to
* support this (e.g. BSON objects). */
phongo_std_object_handlers.clone_obj = NULL;
/* Ensure that get_gc delegates to zend_std_get_properties directly in case
* our class defines a get_properties handler for debugging purposes. */
phongo_std_object_handlers.get_gc = php_phongo_std_get_gc;

/* Initialize zend_class_entry dependencies.
*
Expand Down
90 changes: 88 additions & 2 deletions php_phongo.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,97 @@ zend_object_handlers* phongo_get_std_object_handlers(void);
#define PHONGO_GET_PROPERTY_HASH_FREE_PROPS(is_temp, props) \
do { \
if (is_temp) { \
zend_hash_destroy((props)); \
FREE_HASHTABLE(props); \
zend_hash_release((props)); \
} \
} while (0)

#define PHONGO_GET_PROPERTY_HANDLERS(_name, _intern_extractor) \
PHONGO_GET_PROPERTY_HANDLERS_NO_GC(_name, _intern_extractor) \
\
static HashTable* php_phongo_##_name##_get_gc(zend_object* zobj, zval** table, int* n) \
{ \
*table = NULL; \
*n = 0; \
return _intern_extractor(zobj)->php_properties; \
}

#define PHONGO_GET_PROPERTY_HANDLERS_NO_GC(_name, _intern_extractor) \
static zval* php_phongo_##_name##_read_property(zend_object* zobj, zend_string* member, int type, void** cache_slot, zval* rv) \
{ \
HashTable* props = _intern_extractor(zobj)->php_properties; \
if (!props) { \
ALLOC_HASHTABLE(props); \
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
_intern_extractor(zobj)->php_properties = props; \
} \
zval* ret = zend_hash_find(props, member); \
if (ret) { \
return ret; \
} \
return &EG(uninitialized_zval); \
} \
\
static zval* php_phongo_##_name##_write_property(zend_object* zobj, zend_string* name, zval* value, void** cache_slot) \
{ \
Z_TRY_ADDREF_P(value); \
HashTable* props = _intern_extractor(zobj)->php_properties; \
if (!props) { \
ALLOC_HASHTABLE(props); \
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
_intern_extractor(zobj)->php_properties = props; \
} \
return zend_hash_add_new(props, name, value); \
} \
static int php_phongo_##_name##_has_property(zend_object* zobj, zend_string* name, int has_set_exists, void** cache_slot) \
{ \
HashTable* props = _intern_extractor(zobj)->php_properties; \
if (!props) { \
ALLOC_HASHTABLE(props); \
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
_intern_extractor(zobj)->php_properties = props; \
} \
zval* value = zend_hash_find(props, name); \
if (value) { \
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) { \
return zend_is_true(value); \
} \
if (has_set_exists < ZEND_PROPERTY_NOT_EMPTY) { \
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_ISSET); \
ZVAL_DEREF(value); \
return (Z_TYPE_P(value) != IS_NULL); \
} \
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_EXISTS); \
return true; \
} \
return false; \
} \
static void php_phongo_##_name##_unset_property(zend_object* zobj, zend_string* name, void** cache_slot) \
{ \
HashTable* props = _intern_extractor(zobj)->php_properties; \
if (!props) { \
ALLOC_HASHTABLE(props); \
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
_intern_extractor(zobj)->php_properties = props; \
} \
zend_hash_del(props, name); \
} \
\
static zval* php_phongo_##_name##_get_property_ptr_ptr(zend_object* zobj, zend_string* name, int type, void** cache_slot) \
{ \
HashTable* props = _intern_extractor(zobj)->php_properties; \
if (!props) { \
ALLOC_HASHTABLE(props); \
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
_intern_extractor(zobj)->php_properties = props; \
} \
\
zval* value = zend_hash_find(props, name); \
if (value) { \
return value; \
} \
return zend_hash_add(props, name, &EG(uninitialized_zval)); \
}

#define PHONGO_ZVAL_EXCEPTION_NAME(e) (ZSTR_VAL(e->ce->name))

#define PHONGO_SET_CREATED_BY_PID(intern) \
Expand Down
30 changes: 22 additions & 8 deletions src/BSON/Binary.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,14 @@ static void php_phongo_binary_free_object(zend_object* object)
}

if (intern->properties) {
zend_hash_destroy(intern->properties);
FREE_HASHTABLE(intern->properties);
HashTable* props = intern->properties;
intern->properties = NULL;
zend_hash_release(props);
}
if (intern->php_properties) {
HashTable* props = intern->php_properties;
intern->php_properties = NULL;
zend_hash_release(props);
}
}

Expand Down Expand Up @@ -281,18 +287,26 @@ static HashTable* php_phongo_binary_get_properties(zend_object* object)
return php_phongo_binary_get_properties_hash(object, false);
}

PHONGO_GET_PROPERTY_HANDLERS(binary, Z_OBJ_BINARY);

void php_phongo_binary_init_ce(INIT_FUNC_ARGS)
{
php_phongo_binary_ce = register_class_MongoDB_BSON_Binary(php_phongo_binary_interface_ce, php_phongo_json_serializable_ce, php_phongo_type_ce, zend_ce_stringable);
php_phongo_binary_ce->create_object = php_phongo_binary_create_object;

memcpy(&php_phongo_handler_binary, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
php_phongo_handler_binary.compare = php_phongo_binary_compare_objects;
php_phongo_handler_binary.clone_obj = php_phongo_binary_clone_object;
php_phongo_handler_binary.get_debug_info = php_phongo_binary_get_debug_info;
php_phongo_handler_binary.get_properties = php_phongo_binary_get_properties;
php_phongo_handler_binary.free_obj = php_phongo_binary_free_object;
php_phongo_handler_binary.offset = XtOffsetOf(php_phongo_binary_t, std);
php_phongo_handler_binary.compare = php_phongo_binary_compare_objects;
php_phongo_handler_binary.clone_obj = php_phongo_binary_clone_object;
php_phongo_handler_binary.get_debug_info = php_phongo_binary_get_debug_info;
php_phongo_handler_binary.get_properties = php_phongo_binary_get_properties;
php_phongo_handler_binary.read_property = php_phongo_binary_read_property;
php_phongo_handler_binary.write_property = php_phongo_binary_write_property;
php_phongo_handler_binary.has_property = php_phongo_binary_has_property;
php_phongo_handler_binary.unset_property = php_phongo_binary_unset_property;
php_phongo_handler_binary.get_property_ptr_ptr = php_phongo_binary_get_property_ptr_ptr;
php_phongo_handler_binary.get_gc = php_phongo_binary_get_gc;
php_phongo_handler_binary.free_obj = php_phongo_binary_free_object;
php_phongo_handler_binary.offset = XtOffsetOf(php_phongo_binary_t, std);
}

bool phongo_binary_new(zval* object, const char* data, size_t data_len, bson_subtype_t type)
Expand Down
30 changes: 22 additions & 8 deletions src/BSON/DBPointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,14 @@ static void php_phongo_dbpointer_free_object(zend_object* object)
}

if (intern->properties) {
zend_hash_destroy(intern->properties);
FREE_HASHTABLE(intern->properties);
HashTable* props = intern->properties;
intern->properties = NULL;
zend_hash_release(props);
}
if (intern->php_properties) {
HashTable* props = intern->php_properties;
intern->php_properties = NULL;
zend_hash_release(props);
}
}

Expand Down Expand Up @@ -241,18 +247,26 @@ static HashTable* php_phongo_dbpointer_get_properties(zend_object* object)
return php_phongo_dbpointer_get_properties_hash(object, false);
}

PHONGO_GET_PROPERTY_HANDLERS(dbpointer, Z_OBJ_DBPOINTER);

void php_phongo_dbpointer_init_ce(INIT_FUNC_ARGS)
{
php_phongo_dbpointer_ce = register_class_MongoDB_BSON_DBPointer(php_phongo_json_serializable_ce, php_phongo_type_ce, zend_ce_stringable);
php_phongo_dbpointer_ce->create_object = php_phongo_dbpointer_create_object;

memcpy(&php_phongo_handler_dbpointer, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
php_phongo_handler_dbpointer.compare = php_phongo_dbpointer_compare_objects;
php_phongo_handler_dbpointer.clone_obj = php_phongo_dbpointer_clone_object;
php_phongo_handler_dbpointer.get_debug_info = php_phongo_dbpointer_get_debug_info;
php_phongo_handler_dbpointer.get_properties = php_phongo_dbpointer_get_properties;
php_phongo_handler_dbpointer.free_obj = php_phongo_dbpointer_free_object;
php_phongo_handler_dbpointer.offset = XtOffsetOf(php_phongo_dbpointer_t, std);
php_phongo_handler_dbpointer.compare = php_phongo_dbpointer_compare_objects;
php_phongo_handler_dbpointer.clone_obj = php_phongo_dbpointer_clone_object;
php_phongo_handler_dbpointer.get_debug_info = php_phongo_dbpointer_get_debug_info;
php_phongo_handler_dbpointer.get_properties = php_phongo_dbpointer_get_properties;
php_phongo_handler_dbpointer.read_property = php_phongo_dbpointer_read_property;
php_phongo_handler_dbpointer.write_property = php_phongo_dbpointer_write_property;
php_phongo_handler_dbpointer.has_property = php_phongo_dbpointer_has_property;
php_phongo_handler_dbpointer.unset_property = php_phongo_dbpointer_unset_property;
php_phongo_handler_dbpointer.get_property_ptr_ptr = php_phongo_dbpointer_get_property_ptr_ptr;
php_phongo_handler_dbpointer.get_gc = php_phongo_dbpointer_get_gc;
php_phongo_handler_dbpointer.free_obj = php_phongo_dbpointer_free_object;
php_phongo_handler_dbpointer.offset = XtOffsetOf(php_phongo_dbpointer_t, std);
}

bool phongo_dbpointer_new(zval* object, const char* ref, size_t ref_len, const bson_oid_t* oid)
Expand Down
28 changes: 21 additions & 7 deletions src/BSON/Decimal128.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,14 @@ static void php_phongo_decimal128_free_object(zend_object* object)
zend_object_std_dtor(&intern->std);

if (intern->properties) {
zend_hash_destroy(intern->properties);
FREE_HASHTABLE(intern->properties);
HashTable* props = intern->properties;
intern->properties = NULL;
zend_hash_release(props);
}
if (intern->php_properties) {
HashTable* props = intern->php_properties;
intern->php_properties = NULL;
zend_hash_release(props);
}
}

Expand Down Expand Up @@ -216,17 +222,25 @@ static HashTable* php_phongo_decimal128_get_properties(zend_object* object)
return php_phongo_decimal128_get_properties_hash(object, false);
}

PHONGO_GET_PROPERTY_HANDLERS(decimal128, Z_OBJ_DECIMAL128);

void php_phongo_decimal128_init_ce(INIT_FUNC_ARGS)
{
php_phongo_decimal128_ce = register_class_MongoDB_BSON_Decimal128(php_phongo_decimal128_interface_ce, php_phongo_json_serializable_ce, php_phongo_type_ce, zend_ce_stringable);
php_phongo_decimal128_ce->create_object = php_phongo_decimal128_create_object;

memcpy(&php_phongo_handler_decimal128, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));
php_phongo_handler_decimal128.clone_obj = php_phongo_decimal128_clone_object;
php_phongo_handler_decimal128.get_debug_info = php_phongo_decimal128_get_debug_info;
php_phongo_handler_decimal128.get_properties = php_phongo_decimal128_get_properties;
php_phongo_handler_decimal128.free_obj = php_phongo_decimal128_free_object;
php_phongo_handler_decimal128.offset = XtOffsetOf(php_phongo_decimal128_t, std);
php_phongo_handler_decimal128.clone_obj = php_phongo_decimal128_clone_object;
php_phongo_handler_decimal128.get_debug_info = php_phongo_decimal128_get_debug_info;
php_phongo_handler_decimal128.get_properties = php_phongo_decimal128_get_properties;
php_phongo_handler_decimal128.read_property = php_phongo_decimal128_read_property;
php_phongo_handler_decimal128.write_property = php_phongo_decimal128_write_property;
php_phongo_handler_decimal128.has_property = php_phongo_decimal128_has_property;
php_phongo_handler_decimal128.unset_property = php_phongo_decimal128_unset_property;
php_phongo_handler_decimal128.get_property_ptr_ptr = php_phongo_decimal128_get_property_ptr_ptr;
php_phongo_handler_decimal128.get_gc = php_phongo_decimal128_get_gc;
php_phongo_handler_decimal128.free_obj = php_phongo_decimal128_free_object;
php_phongo_handler_decimal128.offset = XtOffsetOf(php_phongo_decimal128_t, std);
}

bool phongo_decimal128_new(zval* object, const bson_decimal128_t* decimal)
Expand Down
10 changes: 8 additions & 2 deletions src/BSON/Document.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,8 +447,14 @@ static void php_phongo_document_free_object(zend_object* object)
}

if (intern->properties) {
zend_hash_destroy(intern->properties);
FREE_HASHTABLE(intern->properties);
HashTable* props = intern->properties;
intern->properties = NULL;
zend_hash_release(props);
}
if (intern->php_properties) {
HashTable* props = intern->php_properties;
intern->php_properties = NULL;
zend_hash_release(props);
}
}

Expand Down
Loading
Loading