Skip to content

Commit 04dc9f1

Browse files
committed
Merged pull request #776
2 parents 4c32de9 + 8136d50 commit 04dc9f1

17 files changed

+519
-115
lines changed

php_bson.h

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,30 @@ typedef struct {
4949
zend_class_entry* root;
5050
} php_phongo_bson_typemap;
5151

52+
typedef struct {
53+
const char** elements;
54+
size_t allocated_levels;
55+
size_t current_level;
56+
size_t ref_count;
57+
} php_phongo_field_path;
58+
5259
typedef struct {
5360
ZVAL_RETVAL_TYPE zchild;
5461
php_phongo_bson_typemap map;
5562
zend_class_entry* odm;
5663
bool is_visiting_array;
64+
php_phongo_field_path* field_path;
5765
} php_phongo_bson_state;
5866

5967
#if PHP_VERSION_ID >= 70000
60-
#define PHONGO_BSON_STATE_INITIALIZER \
61-
{ \
62-
{ { 0 } }, { PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL }, NULL, 0 \
68+
#define PHONGO_BSON_STATE_INITIALIZER \
69+
{ \
70+
{ { 0 } }, { PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL }, NULL, NULL \
6371
}
6472
#else
65-
#define PHONGO_BSON_STATE_INITIALIZER \
66-
{ \
67-
NULL, { PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL }, NULL, 0 \
73+
#define PHONGO_BSON_STATE_INITIALIZER \
74+
{ \
75+
NULL, { PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL }, NULL, NULL \
6876
}
6977
#endif
7078

@@ -76,6 +84,17 @@ bool php_phongo_bson_to_zval(const unsigned char* data, int data_len, zval* out)
7684
bool php_phongo_bson_to_zval(const unsigned char* data, int data_len, zval** out);
7785
#endif
7886
bool php_phongo_bson_typemap_to_state(zval* typemap, php_phongo_bson_typemap* map TSRMLS_DC);
87+
void php_phongo_bson_state_ctor(php_phongo_bson_state* state);
88+
void php_phongo_bson_state_dtor(php_phongo_bson_state* state);
89+
void php_phongo_bson_state_copy_ctor(php_phongo_bson_state* dst, php_phongo_bson_state* src);
90+
91+
php_phongo_field_path* php_phongo_field_path_alloc(void);
92+
void php_phongo_field_path_free(php_phongo_field_path* field_path);
93+
void php_phongo_field_path_write_item_at_current_level(php_phongo_field_path* field_path, const char* element);
94+
bool php_phongo_field_path_push(php_phongo_field_path* field_path, const char* element);
95+
bool php_phongo_field_path_pop(php_phongo_field_path* field_path);
96+
97+
char* php_phongo_field_path_as_string(php_phongo_field_path* field_path);
7998

8099
#endif /* PHONGO_BSON_H */
81100

src/bson-encode.c

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
#undef MONGOC_LOG_DOMAIN
4646
#define MONGOC_LOG_DOMAIN "PHONGO-BSON"
4747

48+
/* Forwards declarations */
49+
static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* field_path, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out TSRMLS_DC);
50+
4851
/* Determines whether the argument should be serialized as a BSON array or
4952
* document. IS_ARRAY is returned if the argument's keys are a sequence of
5053
* integers starting at zero; otherwise, IS_OBJECT is returned. */
@@ -113,7 +116,7 @@ static int php_phongo_is_array_or_document(zval* val TSRMLS_DC) /* {{{ */
113116
* will be appended as an embedded document. Other MongoDB\BSON\Type instances
114117
* will be appended as the appropriate BSON type. Other array or object values
115118
* will be appended as an embedded document. */
116-
static void php_phongo_bson_append_object(bson_t* bson, php_phongo_bson_flags_t flags, const char* key, long key_len, zval* object TSRMLS_DC) /* {{{ */
119+
static void php_phongo_bson_append_object(bson_t* bson, php_phongo_field_path* field_path, php_phongo_bson_flags_t flags, const char* key, long key_len, zval* object TSRMLS_DC) /* {{{ */
117120
{
118121
if (Z_TYPE_P(object) == IS_OBJECT && instanceof_function(Z_OBJCE_P(object), php_phongo_cursorid_ce TSRMLS_CC)) {
119122
bson_append_int64(bson, key, key_len, Z_CURSORID_OBJ_P(object)->id);
@@ -178,17 +181,17 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_bson_flags_t
178181
#endif
179182
}
180183
#if PHP_VERSION_ID >= 70000
181-
php_phongo_zval_to_bson(&obj_data, flags, &child, NULL TSRMLS_CC);
184+
php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, &child, NULL TSRMLS_CC);
182185
#else
183-
php_phongo_zval_to_bson(obj_data, flags, &child, NULL TSRMLS_CC);
186+
php_phongo_zval_to_bson_internal(obj_data, field_path, flags, &child, NULL TSRMLS_CC);
184187
#endif
185188
bson_append_document_end(bson, &child);
186189
} else {
187190
bson_append_array_begin(bson, key, key_len, &child);
188191
#if PHP_VERSION_ID >= 70000
189-
php_phongo_zval_to_bson(&obj_data, flags, &child, NULL TSRMLS_CC);
192+
php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, &child, NULL TSRMLS_CC);
190193
#else
191-
php_phongo_zval_to_bson(obj_data, flags, &child, NULL TSRMLS_CC);
194+
php_phongo_zval_to_bson_internal(obj_data, field_path, flags, &child, NULL TSRMLS_CC);
192195
#endif
193196
bson_append_array_end(bson, &child);
194197
}
@@ -294,16 +297,18 @@ static void php_phongo_bson_append_object(bson_t* bson, php_phongo_bson_flags_t
294297

295298
mongoc_log(MONGOC_LOG_LEVEL_TRACE, MONGOC_LOG_DOMAIN, "encoding document");
296299
bson_append_document_begin(bson, key, key_len, &child);
297-
php_phongo_zval_to_bson(object, flags, &child, NULL TSRMLS_CC);
300+
php_phongo_zval_to_bson_internal(object, field_path, flags, &child, NULL TSRMLS_CC);
298301
bson_append_document_end(bson, &child);
299302
}
300303
} /* }}} */
301304

302305
/* Appends the zval argument to the BSON document. If the argument is an object,
303306
* or an array that should be serialized as an embedded document, this function
304307
* will defer to php_phongo_bson_append_object(). */
305-
static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags, const char* key, long key_len, zval* entry TSRMLS_DC) /* {{{ */
308+
static void php_phongo_bson_append(bson_t* bson, php_phongo_field_path* field_path, php_phongo_bson_flags_t flags, const char* key, long key_len, zval* entry TSRMLS_DC) /* {{{ */
306309
{
310+
php_phongo_field_path_write_item_at_current_level(field_path, key);
311+
307312
#if PHP_VERSION_ID >= 70000
308313
try_again:
309314
#endif
@@ -337,7 +342,9 @@ static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags,
337342
if (bson_utf8_validate(Z_STRVAL_P(entry), Z_STRLEN_P(entry), true)) {
338343
bson_append_utf8(bson, key, key_len, Z_STRVAL_P(entry), Z_STRLEN_P(entry));
339344
} else {
340-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected invalid UTF-8 for fieldname \"%s\": %s", key, Z_STRVAL_P(entry));
345+
char* path_string = php_phongo_field_path_as_string(field_path);
346+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected invalid UTF-8 for field path \"%s\": %s", path_string, Z_STRVAL_P(entry));
347+
efree(path_string);
341348
}
342349
break;
343350

@@ -347,7 +354,9 @@ static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags,
347354
HashTable* tmp_ht = HASH_OF(entry);
348355

349356
if (tmp_ht && ZEND_HASH_GET_APPLY_COUNT(tmp_ht) > 0) {
350-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected recursion for fieldname \"%s\"", key);
357+
char* path_string = php_phongo_field_path_as_string(field_path);
358+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected recursion for field path \"%s\"", path_string);
359+
efree(path_string);
351360
break;
352361
}
353362

@@ -356,7 +365,9 @@ static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags,
356365
}
357366

358367
bson_append_array_begin(bson, key, key_len, &child);
359-
php_phongo_zval_to_bson(entry, flags, &child, NULL TSRMLS_CC);
368+
field_path->current_level++;
369+
php_phongo_zval_to_bson_internal(entry, field_path, flags, &child, NULL TSRMLS_CC);
370+
field_path->current_level--;
360371
bson_append_array_end(bson, &child);
361372

362373
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
@@ -370,15 +381,19 @@ static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags,
370381
HashTable* tmp_ht = HASH_OF(entry);
371382

372383
if (tmp_ht && ZEND_HASH_GET_APPLY_COUNT(tmp_ht) > 0) {
373-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected recursion for fieldname \"%s\"", key);
384+
char* path_string = php_phongo_field_path_as_string(field_path);
385+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected recursion for field path \"%s\"", path_string);
386+
efree(path_string);
374387
break;
375388
}
376389

377390
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
378391
ZEND_HASH_INC_APPLY_COUNT(tmp_ht);
379392
}
380393

381-
php_phongo_bson_append_object(bson, flags, key, key_len, entry TSRMLS_CC);
394+
field_path->current_level++;
395+
php_phongo_bson_append_object(bson, field_path, flags, key, key_len, entry TSRMLS_CC);
396+
field_path->current_level--;
382397

383398
if (tmp_ht && ZEND_HASH_APPLY_PROTECTION(tmp_ht)) {
384399
ZEND_HASH_DEC_APPLY_COUNT(tmp_ht);
@@ -388,23 +403,23 @@ static void php_phongo_bson_append(bson_t* bson, php_phongo_bson_flags_t flags,
388403

389404
#if PHP_VERSION_ID >= 70000
390405
case IS_INDIRECT:
391-
php_phongo_bson_append(bson, flags, key, key_len, Z_INDIRECT_P(entry) TSRMLS_DC);
406+
php_phongo_bson_append(bson, field_path, flags, key, key_len, Z_INDIRECT_P(entry) TSRMLS_DC);
392407
break;
393408

394409
case IS_REFERENCE:
395410
ZVAL_DEREF(entry);
396411
goto try_again;
397412
#endif
398413

399-
default:
400-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected unsupported PHP type for fieldname \"%s\": %d (%s)", key, Z_TYPE_P(entry), zend_get_type_by_const(Z_TYPE_P(entry)));
414+
default: {
415+
char* path_string = php_phongo_field_path_as_string(field_path);
416+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "Detected unsupported PHP type for field path \"%s\": %d (%s)", path_string, Z_TYPE_P(entry), zend_get_type_by_const(Z_TYPE_P(entry)));
417+
efree(path_string);
418+
}
401419
}
402420
} /* }}} */
403421

404-
/* Converts the array or object argument to a BSON document. If the object is an
405-
* instance of MongoDB\BSON\Serializable, the return value of bsonSerialize()
406-
* will be used. */
407-
void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out TSRMLS_DC) /* {{{ */
422+
static void php_phongo_zval_to_bson_internal(zval* data, php_phongo_field_path* field_path, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out TSRMLS_DC) /* {{{ */
408423
{
409424
HashTable* ht_data = NULL;
410425
#if PHP_VERSION_ID >= 70000
@@ -482,7 +497,6 @@ void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t*
482497

483498
if (instanceof_function(Z_OBJCE_P(data), php_phongo_type_ce TSRMLS_CC)) {
484499
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC, "%s instance %s cannot be serialized as a root element", ZSTR_VAL(php_phongo_type_ce->name), ZSTR_VAL(Z_OBJCE_P(data)->name));
485-
486500
return;
487501
}
488502

@@ -538,7 +552,7 @@ void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t*
538552
zend_string_addref(string_key);
539553
}
540554

541-
php_phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, ZSTR_VAL(string_key), strlen(ZSTR_VAL(string_key)), value TSRMLS_CC);
555+
php_phongo_bson_append(bson, field_path, flags & ~PHONGO_BSON_ADD_ID, ZSTR_VAL(string_key), strlen(ZSTR_VAL(string_key)), value TSRMLS_CC);
542556

543557
zend_string_release(string_key);
544558
}
@@ -593,7 +607,7 @@ void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t*
593607
spprintf(&string_key, 0, "%ld", num_key);
594608
}
595609

596-
php_phongo_bson_append(bson, flags & ~PHONGO_BSON_ADD_ID, string_key, strlen(string_key), *value TSRMLS_CC);
610+
php_phongo_bson_append(bson, field_path, flags & ~PHONGO_BSON_ADD_ID, string_key, strlen(string_key), *value TSRMLS_CC);
597611

598612
if (hash_type == HASH_KEY_IS_LONG) {
599613
efree(string_key);
@@ -629,6 +643,18 @@ void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t*
629643
}
630644
} /* }}} */
631645

646+
/* Converts the array or object argument to a BSON document. If the object is an
647+
* instance of MongoDB\BSON\Serializable, the return value of bsonSerialize()
648+
* will be used. */
649+
void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out TSRMLS_DC) /* {{{ */
650+
{
651+
php_phongo_field_path* field_path = php_phongo_field_path_alloc();
652+
653+
php_phongo_zval_to_bson_internal(data, field_path, flags, bson, bson_out TSRMLS_CC);
654+
655+
php_phongo_field_path_free(field_path);
656+
} /* }}} */
657+
632658
/*
633659
* Local variables:
634660
* tab-width: 4

0 commit comments

Comments
 (0)