Skip to content

Commit 78cb221

Browse files
committed
Fix foreach after the garbage collector is invoked or after a dynamic property is used
1 parent 049a2bf commit 78cb221

31 files changed

+669
-112
lines changed

php_phongo.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,10 @@ static HashTable* php_phongo_std_get_gc(zend_object* object, zval** table, int*
159159
{
160160
*table = NULL;
161161
*n = 0;
162-
return zend_std_get_properties(object);
162+
return object->handlers->get_properties(object);
163163
}
164164

165+
165166
PHP_MINIT_FUNCTION(mongodb) /* {{{ */
166167
{
167168
bson_mem_vtable_t bson_mem_vtable = {
@@ -197,8 +198,6 @@ PHP_MINIT_FUNCTION(mongodb) /* {{{ */
197198
/* Disable cloning by default. Individual classes can opt in if they need to
198199
* support this (e.g. BSON objects). */
199200
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. */
202201
phongo_std_object_handlers.get_gc = php_phongo_std_get_gc;
203202

204203
/* Initialize zend_class_entry dependencies.

php_phongo.h

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,28 +60,112 @@ ZEND_TSRMLS_CACHE_EXTERN()
6060

6161
zend_object_handlers* phongo_get_std_object_handlers(void);
6262

63+
#define PHONGO_RETURN_PROPS(is_temp, props) \
64+
if (!(is_temp)) { \
65+
GC_ADDREF(props); \
66+
} \
67+
return props;
68+
6369
#define PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_temp, intern, props, size) \
6470
do { \
71+
if (!(intern)->php_properties) { \
72+
ALLOC_HASHTABLE((intern)->php_properties); \
73+
zend_hash_init((intern)->php_properties, 0, NULL, ZVAL_PTR_DTOR, 0); \
74+
GC_ADDREF((intern)->php_properties); \
75+
} \
6576
if (is_temp) { \
66-
ALLOC_HASHTABLE(props); \
67-
zend_hash_init((props), (size), NULL, ZVAL_PTR_DTOR, 0); \
68-
} else if ((intern)->properties) { \
69-
(props) = (intern)->properties; \
77+
(props) = zend_array_dup((intern)->php_properties); \
7078
} else { \
71-
ALLOC_HASHTABLE(props); \
72-
zend_hash_init((props), (size), NULL, ZVAL_PTR_DTOR, 0); \
79+
(props) = zend_array_dup((intern)->php_properties); \
80+
if ((intern)->properties) { \
81+
HashTable *__tmp = (intern)->properties; \
82+
(intern)->properties = NULL; \
83+
zend_hash_release(__tmp); \
84+
} \
7385
(intern)->properties = (props); \
7486
} \
7587
} while (0)
7688

7789
#define PHONGO_GET_PROPERTY_HASH_FREE_PROPS(is_temp, props) \
7890
do { \
7991
if (is_temp) { \
80-
zend_hash_destroy((props)); \
81-
FREE_HASHTABLE(props); \
92+
zend_hash_release((props)); \
8293
} \
8394
} while (0)
8495

96+
#define PHONGO_GET_PROPERTY_HANDLERS(_name, _intern_extractor) \
97+
static zval* php_phongo_##_name##_read_property(zend_object *zobj, zend_string *member, int type, void **cache_slot, zval *rv) \
98+
{ \
99+
HashTable *props = _intern_extractor(zobj)->php_properties; \
100+
if (!props) { \
101+
ALLOC_HASHTABLE(props); \
102+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
103+
_intern_extractor(zobj)->php_properties = props; \
104+
} \
105+
return zend_hash_find(props, member); \
106+
} \
107+
\
108+
static zval *php_phongo_##_name##_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot) \
109+
{ \
110+
Z_TRY_ADDREF_P(value); \
111+
HashTable *props = _intern_extractor(zobj)->php_properties; \
112+
if (!props) { \
113+
ALLOC_HASHTABLE(props); \
114+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
115+
_intern_extractor(zobj)->php_properties = props; \
116+
} \
117+
return zend_hash_add_new(props, name, value); \
118+
} \
119+
static int php_phongo_##_name##_has_property(zend_object *zobj, zend_string *name, int has_set_exists, void **cache_slot) \
120+
{ \
121+
HashTable *props = _intern_extractor(zobj)->php_properties; \
122+
if (!props) { \
123+
ALLOC_HASHTABLE(props); \
124+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
125+
_intern_extractor(zobj)->php_properties = props; \
126+
} \
127+
zval *value = zend_hash_find(props, name); \
128+
if (value) { \
129+
if (has_set_exists == ZEND_PROPERTY_NOT_EMPTY) { \
130+
return zend_is_true(value); \
131+
} \
132+
if (has_set_exists < ZEND_PROPERTY_NOT_EMPTY) { \
133+
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_ISSET); \
134+
ZVAL_DEREF(value); \
135+
return (Z_TYPE_P(value) != IS_NULL); \
136+
} \
137+
ZEND_ASSERT(has_set_exists == ZEND_PROPERTY_EXISTS); \
138+
return true; \
139+
} \
140+
return false; \
141+
} \
142+
static void php_phongo_##_name##_unset_property(zend_object *zobj, zend_string *name, void **cache_slot) \
143+
{ \
144+
HashTable *props = _intern_extractor(zobj)->php_properties; \
145+
if (!props) { \
146+
ALLOC_HASHTABLE(props); \
147+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
148+
_intern_extractor(zobj)->php_properties = props; \
149+
} \
150+
zend_hash_del(props, name); \
151+
} \
152+
\
153+
static zval *php_phongo_##_name##_get_property_ptr_ptr(zend_object *zobj, zend_string *name, int type, void **cache_slot) \
154+
{ \
155+
HashTable *props = _intern_extractor(zobj)->php_properties; \
156+
if (!props) { \
157+
ALLOC_HASHTABLE(props); \
158+
zend_hash_init(props, 0, NULL, ZVAL_PTR_DTOR, 0); \
159+
_intern_extractor(zobj)->php_properties = props; \
160+
} \
161+
\
162+
zval *value = zend_hash_find(props, name); \
163+
if (value) { \
164+
return value; \
165+
} \
166+
return zend_hash_add(props, name, &EG(uninitialized_zval)); \
167+
}
168+
85169
#define PHONGO_ZVAL_EXCEPTION_NAME(e) (ZSTR_VAL(e->ce->name))
86170

87171
#define PHONGO_SET_CREATED_BY_PID(intern) \

src/BSON/Binary.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ static HashTable* php_phongo_binary_get_properties_hash(zend_object* object, boo
7373
PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_temp, intern, props, 2);
7474

7575
if (!intern->data) {
76-
return props;
76+
PHONGO_RETURN_PROPS(is_temp, props);
7777
}
7878

7979
{
@@ -86,7 +86,7 @@ static HashTable* php_phongo_binary_get_properties_hash(zend_object* object, boo
8686
zend_hash_str_update(props, "type", sizeof("type") - 1, &type);
8787
}
8888

89-
return props;
89+
PHONGO_RETURN_PROPS(is_temp, props);
9090
}
9191

9292
/* Construct a new BSON binary type */
@@ -213,9 +213,16 @@ static void php_phongo_binary_free_object(zend_object* object)
213213
efree(intern->data);
214214
}
215215

216+
216217
if (intern->properties) {
217-
zend_hash_destroy(intern->properties);
218-
FREE_HASHTABLE(intern->properties);
218+
HashTable* props = intern->properties;
219+
intern->properties = NULL;
220+
zend_hash_release(props);
221+
}
222+
if (intern->php_properties) {
223+
HashTable* props = intern->php_properties;
224+
intern->php_properties = NULL;
225+
zend_hash_release(props);
219226
}
220227
}
221228

@@ -281,6 +288,8 @@ static HashTable* php_phongo_binary_get_properties(zend_object* object)
281288
return php_phongo_binary_get_properties_hash(object, false);
282289
}
283290

291+
PHONGO_GET_PROPERTY_HANDLERS(binary, Z_OBJ_BINARY);
292+
284293
void php_phongo_binary_init_ce(INIT_FUNC_ARGS)
285294
{
286295
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);
@@ -291,6 +300,11 @@ void php_phongo_binary_init_ce(INIT_FUNC_ARGS)
291300
php_phongo_handler_binary.clone_obj = php_phongo_binary_clone_object;
292301
php_phongo_handler_binary.get_debug_info = php_phongo_binary_get_debug_info;
293302
php_phongo_handler_binary.get_properties = php_phongo_binary_get_properties;
303+
php_phongo_handler_binary.read_property = php_phongo_binary_read_property;
304+
php_phongo_handler_binary.write_property = php_phongo_binary_write_property;
305+
php_phongo_handler_binary.has_property = php_phongo_binary_has_property;
306+
php_phongo_handler_binary.unset_property = php_phongo_binary_unset_property;
307+
php_phongo_handler_binary.get_property_ptr_ptr = php_phongo_binary_get_property_ptr_ptr;
294308
php_phongo_handler_binary.free_obj = php_phongo_binary_free_object;
295309
php_phongo_handler_binary.offset = XtOffsetOf(php_phongo_binary_t, std);
296310
}

src/BSON/DBPointer.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ HashTable* php_phongo_dbpointer_get_properties_hash(zend_object* object, bool is
7474
PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_temp, intern, props, 2);
7575

7676
if (!intern->ref) {
77-
return props;
77+
PHONGO_RETURN_PROPS(is_temp, props);
7878
}
7979

8080
{
@@ -86,7 +86,7 @@ HashTable* php_phongo_dbpointer_get_properties_hash(zend_object* object, bool is
8686
zend_hash_str_update(props, "id", sizeof("id") - 1, &id);
8787
}
8888

89-
return props;
89+
PHONGO_RETURN_PROPS(is_temp, props);
9090
}
9191

9292
PHONGO_DISABLED_CONSTRUCTOR(MongoDB_BSON_DBPointer)
@@ -176,9 +176,16 @@ static void php_phongo_dbpointer_free_object(zend_object* object)
176176
efree(intern->ref);
177177
}
178178

179+
179180
if (intern->properties) {
180-
zend_hash_destroy(intern->properties);
181-
FREE_HASHTABLE(intern->properties);
181+
HashTable* props = intern->properties;
182+
intern->properties = NULL;
183+
zend_hash_release(props);
184+
}
185+
if (intern->php_properties) {
186+
HashTable* props = intern->php_properties;
187+
intern->php_properties = NULL;
188+
zend_hash_release(props);
182189
}
183190
}
184191

@@ -241,6 +248,8 @@ static HashTable* php_phongo_dbpointer_get_properties(zend_object* object)
241248
return php_phongo_dbpointer_get_properties_hash(object, false);
242249
}
243250

251+
PHONGO_GET_PROPERTY_HANDLERS(dbpointer, Z_OBJ_DBPOINTER);
252+
244253
void php_phongo_dbpointer_init_ce(INIT_FUNC_ARGS)
245254
{
246255
php_phongo_dbpointer_ce = register_class_MongoDB_BSON_DBPointer(php_phongo_json_serializable_ce, php_phongo_type_ce, zend_ce_stringable);
@@ -251,6 +260,11 @@ void php_phongo_dbpointer_init_ce(INIT_FUNC_ARGS)
251260
php_phongo_handler_dbpointer.clone_obj = php_phongo_dbpointer_clone_object;
252261
php_phongo_handler_dbpointer.get_debug_info = php_phongo_dbpointer_get_debug_info;
253262
php_phongo_handler_dbpointer.get_properties = php_phongo_dbpointer_get_properties;
263+
php_phongo_handler_dbpointer.read_property = php_phongo_dbpointer_read_property;
264+
php_phongo_handler_dbpointer.write_property = php_phongo_dbpointer_write_property;
265+
php_phongo_handler_dbpointer.has_property = php_phongo_dbpointer_has_property;
266+
php_phongo_handler_dbpointer.unset_property = php_phongo_dbpointer_unset_property;
267+
php_phongo_handler_dbpointer.get_property_ptr_ptr = php_phongo_dbpointer_get_property_ptr_ptr;
254268
php_phongo_handler_dbpointer.free_obj = php_phongo_dbpointer_free_object;
255269
php_phongo_handler_dbpointer.offset = XtOffsetOf(php_phongo_dbpointer_t, std);
256270
}

src/BSON/Decimal128.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static HashTable* php_phongo_decimal128_get_properties_hash(zend_object* object,
6464
PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_temp, intern, props, 1);
6565

6666
if (!intern->initialized) {
67-
return props;
67+
PHONGO_RETURN_PROPS(is_temp, props);
6868
}
6969

7070
bson_decimal128_to_string(&intern->decimal, outbuf);
@@ -76,7 +76,7 @@ static HashTable* php_phongo_decimal128_get_properties_hash(zend_object* object,
7676
zend_hash_str_update(props, "dec", sizeof("dec") - 1, &dec);
7777
}
7878

79-
return props;
79+
PHONGO_RETURN_PROPS(is_temp, props);
8080
}
8181

8282
/* Construct a new BSON Decimal128 type */
@@ -168,9 +168,16 @@ static void php_phongo_decimal128_free_object(zend_object* object)
168168

169169
zend_object_std_dtor(&intern->std);
170170

171+
171172
if (intern->properties) {
172-
zend_hash_destroy(intern->properties);
173-
FREE_HASHTABLE(intern->properties);
173+
HashTable* props = intern->properties;
174+
intern->properties = NULL;
175+
zend_hash_release(props);
176+
}
177+
if (intern->php_properties) {
178+
HashTable* props = intern->php_properties;
179+
intern->php_properties = NULL;
180+
zend_hash_release(props);
174181
}
175182
}
176183

@@ -216,6 +223,8 @@ static HashTable* php_phongo_decimal128_get_properties(zend_object* object)
216223
return php_phongo_decimal128_get_properties_hash(object, false);
217224
}
218225

226+
PHONGO_GET_PROPERTY_HANDLERS(decimal128, Z_OBJ_DECIMAL128);
227+
219228
void php_phongo_decimal128_init_ce(INIT_FUNC_ARGS)
220229
{
221230
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);
@@ -225,6 +234,11 @@ void php_phongo_decimal128_init_ce(INIT_FUNC_ARGS)
225234
php_phongo_handler_decimal128.clone_obj = php_phongo_decimal128_clone_object;
226235
php_phongo_handler_decimal128.get_debug_info = php_phongo_decimal128_get_debug_info;
227236
php_phongo_handler_decimal128.get_properties = php_phongo_decimal128_get_properties;
237+
php_phongo_handler_decimal128.read_property = php_phongo_decimal128_read_property;
238+
php_phongo_handler_decimal128.write_property = php_phongo_decimal128_write_property;
239+
php_phongo_handler_decimal128.has_property = php_phongo_decimal128_has_property;
240+
php_phongo_handler_decimal128.unset_property = php_phongo_decimal128_unset_property;
241+
php_phongo_handler_decimal128.get_property_ptr_ptr = php_phongo_decimal128_get_property_ptr_ptr;
228242
php_phongo_handler_decimal128.free_obj = php_phongo_decimal128_free_object;
229243
php_phongo_handler_decimal128.offset = XtOffsetOf(php_phongo_decimal128_t, std);
230244
}

src/BSON/Document.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static HashTable* php_phongo_document_get_properties_hash(zend_object* object, b
6565
PHONGO_GET_PROPERTY_HASH_INIT_PROPS(is_temp, intern, props, size);
6666

6767
if (!intern->bson) {
68-
return props;
68+
PHONGO_RETURN_PROPS(is_temp, props);
6969
}
7070

7171
{
@@ -75,7 +75,7 @@ static HashTable* php_phongo_document_get_properties_hash(zend_object* object, b
7575
zend_hash_str_update(props, "data", sizeof("data") - 1, &data);
7676
}
7777

78-
return props;
78+
PHONGO_RETURN_PROPS(is_temp, props);
7979
}
8080

8181
PHONGO_DISABLED_CONSTRUCTOR(MongoDB_BSON_Document)
@@ -446,9 +446,16 @@ static void php_phongo_document_free_object(zend_object* object)
446446
bson_destroy(intern->bson);
447447
}
448448

449+
449450
if (intern->properties) {
450-
zend_hash_destroy(intern->properties);
451-
FREE_HASHTABLE(intern->properties);
451+
HashTable* props = intern->properties;
452+
intern->properties = NULL;
453+
zend_hash_release(props);
454+
}
455+
if (intern->php_properties) {
456+
HashTable* props = intern->php_properties;
457+
intern->php_properties = NULL;
458+
zend_hash_release(props);
452459
}
453460
}
454461

@@ -520,7 +527,7 @@ static HashTable* php_phongo_document_get_debug_info(zend_object* object, int* i
520527
zend_hash_str_update(props, "value", sizeof("value") - 1, &state.zchild);
521528
}
522529

523-
return props;
530+
PHONGO_RETURN_PROPS(is_temp, props);
524531

525532
failure:
526533
PHONGO_GET_PROPERTY_HASH_FREE_PROPS(is_temp, props);

0 commit comments

Comments
 (0)