Skip to content

Commit c162c29

Browse files
committed
PHPC-248: Allow ->setTypeMap() to set 'array' and 'stdclass'
1 parent 3c57497 commit c162c29

File tree

4 files changed

+197
-49
lines changed

4 files changed

+197
-49
lines changed

php_bson.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,41 @@
2626
/* PHP Core stuff */
2727
#include <php.h>
2828

29-
/* Our stuff */
30-
#include "php_phongo.h"
29+
30+
#define BSON_UNSERIALIZE_FUNC_NAME "bsonUnserialize"
31+
#define BSON_SERIALIZE_FUNC_NAME "bsonSerialize"
3132

3233
typedef enum {
3334
PHONGO_BSON_NONE = 0x00,
3435
PHONGO_BSON_ADD_ID = 0x01,
3536
PHONGO_BSON_RETURN_ID = 0x02,
3637
PHONGO_BSON_ADD_ODS = 0x04,
3738
PHONGO_BSON_ADD_CHILD_ODS = 0x08
38-
} phongo_bson_flags_t;
39+
} php_phongo_bson_flags_t;
40+
41+
typedef enum {
42+
PHONGO_TYPEMAP_NONE,
43+
PHONGO_TYPEMAP_NATIVE_ARRAY,
44+
PHONGO_TYPEMAP_NATIVE_STDCLASS,
45+
PHONGO_TYPEMAP_CLASS
46+
} php_phongo_bson_typemap_types;
47+
48+
typedef struct {
49+
php_phongo_bson_typemap_types document_type;
50+
zend_class_entry *document;
51+
php_phongo_bson_typemap_types array_type;
52+
zend_class_entry *array;
53+
} php_phongo_bson_typemap;
54+
55+
typedef struct {
56+
zval *zchild;
57+
php_phongo_bson_typemap map;
58+
zend_class_entry *odm;
59+
} php_phongo_bson_state;
60+
61+
#define PHONGO_BSON_STATE_INITIALIZER {NULL, { PHONGO_TYPEMAP_NONE, NULL, PHONGO_TYPEMAP_NONE, NULL}, NULL}
3962

40-
PHONGO_API void zval_to_bson(zval *data, phongo_bson_flags_t flags, bson_t *bson, bson_t **bson_out TSRMLS_DC);
63+
PHONGO_API void zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson, bson_t **bson_out TSRMLS_DC);
4164
PHONGO_API int bson_to_zval(const unsigned char *data, int data_len, php_phongo_bson_state *state);
4265
PHONGO_API void php_phongo_bson_typemap_to_state(zval *typemap, php_phongo_bson_typemap *map TSRMLS_DC);
4366

php_phongo_classes.h

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,15 @@
2525
#include <php.h>
2626
#include <mongoc-bulk-operation-private.h>
2727

28-
#define BSON_UNSERIALIZE_FUNC_NAME "bsonUnserialize"
29-
#define BSON_SERIALIZE_FUNC_NAME "bsonSerialize"
28+
/* Our stuffz */
29+
#include "php_bson.h"
3030

31-
#define PHONGO_BSON_STATE_INITIALIZER {NULL, {NULL, NULL}, NULL}
3231

3332
typedef struct {
3433
zend_object std;
3534
bson_t *bson;
3635
} php_phongo_command_t;
3736

38-
typedef struct {
39-
zend_class_entry *document;
40-
zend_class_entry *array;
41-
} php_phongo_bson_typemap;
42-
43-
typedef struct {
44-
zval *zchild;
45-
php_phongo_bson_typemap map;
46-
zend_class_entry *odm;
47-
} php_phongo_bson_state;
48-
4937
typedef struct {
5038
zend_object std;
5139
mongoc_cursor_t *cursor;

src/bson.c

Lines changed: 78 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -464,18 +464,32 @@ bool php_phongo_bson_visit_document(const bson_iter_t *iter ARG_UNUSED, const ch
464464
array_init(state.zchild);
465465

466466
if (!bson_iter_visit_all(&child, &php_bson_visitors, &state)) {
467-
if ((state.map.document || state.odm) && instanceof_function(state.odm ? state.odm : state.map.document, php_phongo_unserializable_ce TSRMLS_CC)) {
468-
zval *obj = NULL;
469-
470-
MAKE_STD_ZVAL(obj);
471-
object_init_ex(obj, state.odm ? state.odm : state.map.document);
472-
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
473-
add_assoc_zval(retval, key, obj);
474-
zval_ptr_dtor(&state.zchild);
475-
} else {
476-
object_and_properties_init(state.zchild, zend_standard_class_def, Z_ARRVAL_P(state.zchild));
477-
add_assoc_zval(retval, key, state.zchild);
478-
Z_SET_REFCOUNT_P(state.zchild, 1);
467+
if (state.odm) {
468+
state.map.document_type = PHONGO_TYPEMAP_CLASS;
469+
}
470+
switch(state.map.document_type) {
471+
case PHONGO_TYPEMAP_NATIVE_ARRAY:
472+
add_assoc_zval(retval, key, state.zchild);
473+
Z_SET_REFCOUNT_P(state.zchild, 1);
474+
break;
475+
476+
case PHONGO_TYPEMAP_CLASS:
477+
if (instanceof_function(state.odm ? state.odm : state.map.document, php_phongo_unserializable_ce TSRMLS_CC)) {
478+
zval *obj = NULL;
479+
480+
MAKE_STD_ZVAL(obj);
481+
object_init_ex(obj, state.odm ? state.odm : state.map.document);
482+
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
483+
add_assoc_zval(retval, key, obj);
484+
zval_ptr_dtor(&state.zchild);
485+
break;
486+
}
487+
488+
case PHONGO_TYPEMAP_NATIVE_STDCLASS:
489+
default:
490+
object_and_properties_init(state.zchild, zend_standard_class_def, Z_ARRVAL_P(state.zchild));
491+
add_assoc_zval(retval, key, state.zchild);
492+
Z_SET_REFCOUNT_P(state.zchild, 1);
479493
}
480494
}
481495
}
@@ -500,17 +514,35 @@ bool php_phongo_bson_visit_array(const bson_iter_t *iter ARG_UNUSED, const char
500514

501515
if (!bson_iter_visit_all(&child, &php_bson_visitors, &state)) {
502516

503-
if (state.map.array && instanceof_function(state.map.array, php_phongo_unserializable_ce TSRMLS_CC)) {
504-
zval *obj = NULL;
517+
switch(state.map.array_type) {
518+
case PHONGO_TYPEMAP_CLASS:
519+
if (instanceof_function(state.map.array, php_phongo_unserializable_ce TSRMLS_CC)) {
520+
zval *obj = NULL;
521+
522+
MAKE_STD_ZVAL(obj);
523+
object_init_ex(obj, state.map.array);
524+
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
525+
add_assoc_zval(retval, key, obj);
526+
zval_ptr_dtor(&state.zchild);
527+
break;
528+
}
529+
/* If the object someehow doesn't implement php_phongo_unserializable_ce then use stdclass.
530+
* This is needed as we need to know how to pass the state.zchild to the class to populate it.
531+
* Not all classes have ctor that accepts first parameter array of values.
532+
*/
533+
534+
/* break intentionally omitted */
535+
case PHONGO_TYPEMAP_NATIVE_STDCLASS:
536+
object_and_properties_init(state.zchild, zend_standard_class_def, Z_ARRVAL_P(state.zchild));
537+
add_assoc_zval(retval, key, state.zchild);
538+
Z_SET_REFCOUNT_P(state.zchild, 1);
539+
break;
505540

506-
MAKE_STD_ZVAL(obj);
507-
object_init_ex(obj, state.map.array);
508-
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
509-
add_assoc_zval(retval, key, obj);
510-
zval_ptr_dtor(&state.zchild);
511-
} else {
512-
add_assoc_zval(retval, key, state.zchild);
513-
Z_SET_REFCOUNT_P(state.zchild, 1);
541+
case PHONGO_TYPEMAP_NATIVE_ARRAY:
542+
default:
543+
add_assoc_zval(retval, key, state.zchild);
544+
Z_SET_REFCOUNT_P(state.zchild, 1);
545+
break;
514546
}
515547
}
516548

@@ -561,7 +593,7 @@ int php_phongo_is_array_or_document(zval **val TSRMLS_DC) /* {{{ */
561593
return IS_ARRAY;
562594
}
563595
/* }}} */
564-
void object_to_bson(zval *object, phongo_bson_flags_t flags, const char *key, long key_len, bson_t *bson TSRMLS_DC)
596+
void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key, long key_len, bson_t *bson TSRMLS_DC)
565597
{
566598
bson_t child;
567599

@@ -667,7 +699,7 @@ void object_to_bson(zval *object, phongo_bson_flags_t flags, const char *key, lo
667699
}
668700
}
669701
}
670-
void phongo_bson_append(bson_t *bson, phongo_bson_flags_t flags, const char *key, long key_len, int entry_type, zval *entry TSRMLS_DC)
702+
void phongo_bson_append(bson_t *bson, php_phongo_bson_flags_t flags, const char *key, long key_len, int entry_type, zval *entry TSRMLS_DC)
671703
{
672704
switch (entry_type)
673705
{
@@ -725,7 +757,7 @@ void phongo_bson_append(bson_t *bson, phongo_bson_flags_t flags, const char *key
725757
}
726758
}
727759

728-
PHONGO_API void zval_to_bson(zval *data, phongo_bson_flags_t flags, bson_t *bson, bson_t **bson_out TSRMLS_DC) /* {{{ */
760+
PHONGO_API void zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *bson, bson_t **bson_out TSRMLS_DC) /* {{{ */
729761
{
730762
HashPosition pos;
731763
HashTable *ht_data;
@@ -906,10 +938,17 @@ void php_phongo_bson_typemap_to_state(zval *typemap, php_phongo_bson_typemap *ma
906938

907939
classname = php_array_fetchl_string(typemap, "array", sizeof("array")-1, &classname_len, &classname_free);
908940
if (classname_len) {
909-
array_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
941+
if (!strcasecmp(classname, "array")) {
942+
map->array_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
943+
} else if (!strcasecmp(classname, "stdclass")) {
944+
map->array_type = PHONGO_TYPEMAP_NATIVE_STDCLASS;
945+
} else {
946+
map->array_type = PHONGO_TYPEMAP_CLASS;
947+
array_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
910948

911-
if (instanceof_function(array_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
912-
map->array = array_ce;
949+
if (instanceof_function(array_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
950+
map->array = array_ce;
951+
}
913952
}
914953
if (classname_free) {
915954
efree(classname);
@@ -918,9 +957,17 @@ void php_phongo_bson_typemap_to_state(zval *typemap, php_phongo_bson_typemap *ma
918957

919958
classname = php_array_fetchl_string(typemap, "document", sizeof("document")-1, &classname_len, &classname_free);
920959
if (classname_len) {
921-
document_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
922-
if (instanceof_function(document_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
923-
map->document = document_ce;
960+
if (!strcasecmp(classname, "array")) {
961+
map->document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
962+
} else if (!strcasecmp(classname, "stdclass")) {
963+
map->document_type = PHONGO_TYPEMAP_NATIVE_STDCLASS;
964+
} else {
965+
map->document_type = PHONGO_TYPEMAP_CLASS;
966+
document_ce = zend_fetch_class(classname, classname_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
967+
968+
if (instanceof_function(document_ce, php_phongo_unserializable_ce TSRMLS_CC)) {
969+
map->document = document_ce;
970+
}
924971
}
925972
if (classname_free) {
926973
efree(classname);

tests/bson/typemap-001.phpt

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
--TEST--
2+
MongoDB\Driver\Cursor::setTypeMap(): Setting typemaps
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; CLEANUP(STANDALONE) ?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
class MyArrayObject extends ArrayObject implements BSON\Unserializable
10+
{
11+
function bsonUnserialize(array $data)
12+
{
13+
parent::__construct($data);
14+
}
15+
}
16+
17+
$manager = new MongoDB\Driver\Manager(STANDALONE);
18+
19+
$manager->executeInsert(NS, array('_id' => 1, 'bson_array' => array(1, 2, 3), 'bson_object' => array("string" => "keys", "for" => "ever")));
20+
$manager->executeInsert(NS, array('_id' => 2, 'bson_array' => array(4, 5, 6)));
21+
22+
function fetch($manager, $typemap = array()) {
23+
$cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query(array('bson_array' => 1)));
24+
if ($typemap) {
25+
$cursor->setTypeMap($typemap);
26+
}
27+
28+
$documents = $cursor->toArray();
29+
return $documents;
30+
}
31+
32+
33+
/* Default */
34+
$documents = fetch($manager);
35+
var_dump(is_array($documents[0]['bson_array']));
36+
var_dump(is_object($documents[0]['bson_object']));
37+
38+
39+
/* Setting to MyArrayObject */
40+
$documents = fetch($manager, array("array" => "MyArrayObject"));
41+
var_dump($documents[0]['bson_array'] instanceof MyArrayObject);
42+
var_dump(is_object($documents[0]['bson_object']));
43+
44+
/* Setting to MyArrayObject & MyArrayObject */
45+
$documents = fetch($manager, array("array" => "MyArrayObject", "document" => "MyArrayObject"));
46+
var_dump($documents[0]['bson_array'] instanceof MyArrayObject);
47+
var_dump($documents[0]['bson_object'] instanceof MyArrayObject);
48+
49+
50+
/* Setting to array */
51+
$documents = fetch($manager, array("array" => "array", "document" => "array"));
52+
var_dump(is_array($documents[0]['bson_array']));
53+
var_dump(is_array($documents[0]['bson_object']));
54+
55+
56+
/* Setting to stdlcass & array */
57+
$documents = fetch($manager, array("array" => "stdclass", "document" => "array"));
58+
var_dump(is_object($documents[0]['bson_array']));
59+
var_dump(is_array($documents[0]['bson_object']));
60+
61+
62+
/* Setting to array & stdclass */
63+
$documents = fetch($manager, array("array" => "array", "document" => "stdclass"));
64+
var_dump(is_array($documents[0]['bson_array']));
65+
var_dump(is_object($documents[0]['bson_object']));
66+
67+
68+
/* Setting to stdclass */
69+
$documents = fetch($manager, array("array" => "stdclass", "document" => "stdclass"));
70+
var_dump(is_object($documents[0]['bson_array']));
71+
var_dump(is_object($documents[0]['bson_object']));
72+
?>
73+
===DONE===
74+
<?php exit(0); ?>
75+
--EXPECT--
76+
bool(true)
77+
bool(true)
78+
bool(true)
79+
bool(true)
80+
bool(true)
81+
bool(true)
82+
bool(true)
83+
bool(true)
84+
bool(true)
85+
bool(true)
86+
bool(true)
87+
bool(true)
88+
bool(true)
89+
bool(true)
90+
===DONE===

0 commit comments

Comments
 (0)