Skip to content

Commit 148e726

Browse files
committed
PHPC-42: BSON Serialization for classes
This is a basic ODM. When a class implements the BSON\Persistable interface gets bson encoded we will embed a __ binary(type=0x80) value into the document. Upon bson decoding a document, if we find that binary type convert that document/array into the php class
1 parent f4a574e commit 148e726

File tree

11 files changed

+446
-29
lines changed

11 files changed

+446
-29
lines changed

config.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ if test "$PHONGO" != "no"; then
142142
src/BSON/Type.c \
143143
src/BSON/Unserializable.c \
144144
src/BSON/Serializable.c \
145+
src/BSON/Persistable.c \
145146
src/BSON/Binary.c \
146147
src/BSON/Javascript.c \
147148
src/BSON/MaxKey.c \

php_phongo.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,9 @@ zend_bool phongo_writeconcernerror_init(zval *return_value, bson_t *bson TSRMLS_
271271
writeconcernerror->message = bson_iter_dup_utf8(&iter, NULL);
272272
}
273273
if (bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
274-
uint32_t len;
275-
const uint8_t *data;
276-
php_phongo_bson_state state = {NULL, {NULL, NULL} };
274+
uint32_t len;
275+
const uint8_t *data;
276+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
277277

278278
bson_iter_document(&iter, &len, &data);
279279

@@ -1453,6 +1453,7 @@ PHP_MINIT_FUNCTION(phongo)
14531453
PHP_MINIT(Type)(INIT_FUNC_ARGS_PASSTHRU);
14541454
PHP_MINIT(Serializable)(INIT_FUNC_ARGS_PASSTHRU);
14551455
PHP_MINIT(Unserializable)(INIT_FUNC_ARGS_PASSTHRU);
1456+
PHP_MINIT(Persistable)(INIT_FUNC_ARGS_PASSTHRU);
14561457
PHP_MINIT(Binary)(INIT_FUNC_ARGS_PASSTHRU);
14571458
PHP_MINIT(Javascript)(INIT_FUNC_ARGS_PASSTHRU);
14581459
PHP_MINIT(MaxKey)(INIT_FUNC_ARGS_PASSTHRU);

php_phongo_classes.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#define BSON_UNSERIALIZE_FUNC_NAME "bsonUnserialize"
2828
#define BSON_SERIALIZE_FUNC_NAME "bsonSerialize"
2929

30+
#define PHONGO_BSON_STATE_INITIALIZER {NULL, {NULL, NULL}, NULL}
31+
3032
typedef struct {
3133
zend_object std;
3234
bson_t *bson;
@@ -40,6 +42,7 @@ typedef struct {
4042
typedef struct {
4143
zval *zchild;
4244
php_phongo_bson_typemap map;
45+
zend_class_entry *odm;
4346
} php_phongo_bson_state;
4447

4548
typedef struct {
@@ -209,6 +212,7 @@ extern PHONGO_API zend_class_entry *php_phongo_duplicatekeyexception_ce;
209212
extern PHONGO_API zend_class_entry *php_phongo_writeexception_ce;
210213

211214
extern PHONGO_API zend_class_entry *php_phongo_type_ce;
215+
extern PHONGO_API zend_class_entry *php_phongo_persistable_ce;
212216
extern PHONGO_API zend_class_entry *php_phongo_unserializable_ce;
213217
extern PHONGO_API zend_class_entry *php_phongo_serializable_ce;
214218
extern PHONGO_API zend_class_entry *php_phongo_binary_ce;
@@ -249,6 +253,7 @@ PHP_MINIT_FUNCTION(WriteException);
249253
PHP_MINIT_FUNCTION(Type);
250254
PHP_MINIT_FUNCTION(Unserializable);
251255
PHP_MINIT_FUNCTION(Serializable);
256+
PHP_MINIT_FUNCTION(Persistable);
252257
PHP_MINIT_FUNCTION(Binary);
253258
PHP_MINIT_FUNCTION(Javascript);
254259
PHP_MINIT_FUNCTION(MaxKey);

src/BSON/Persistable.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
+---------------------------------------------------------------------------+
3+
| PHP Driver for MongoDB |
4+
+---------------------------------------------------------------------------+
5+
| Copyright 2013-2014 MongoDB, Inc. |
6+
| |
7+
| Licensed under the Apache License, Version 2.0 (the "License"); |
8+
| you may not use this file except in compliance with the License. |
9+
| You may obtain a copy of the License at |
10+
| |
11+
| http://www.apache.org/licenses/LICENSE-2.0 |
12+
| |
13+
| Unless required by applicable law or agreed to in writing, software |
14+
| distributed under the License is distributed on an "AS IS" BASIS, |
15+
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16+
| See the License for the specific language governing permissions and |
17+
| limitations under the License. |
18+
+---------------------------------------------------------------------------+
19+
| Copyright (c) 2014, MongoDB, Inc. |
20+
+---------------------------------------------------------------------------+
21+
*/
22+
23+
#ifdef HAVE_CONFIG_H
24+
# include "config.h"
25+
#endif
26+
27+
/* External libs */
28+
#include <bson.h>
29+
#include <mongoc.h>
30+
31+
/* PHP Core stuff */
32+
#include <php.h>
33+
#include <php_ini.h>
34+
#include <ext/standard/info.h>
35+
#include <Zend/zend_interfaces.h>
36+
#include <ext/spl/spl_iterators.h>
37+
/* Our Compatability header */
38+
#include "php_compat_53.h"
39+
40+
/* Our stuffz */
41+
#include "php_phongo.h"
42+
#include "php_bson.h"
43+
44+
45+
PHONGO_API zend_class_entry *php_phongo_persistable_ce;
46+
47+
48+
49+
/* {{{ BSON\Persistable */
50+
51+
52+
static zend_function_entry php_phongo_persistable_me[] = {
53+
PHP_FE_END
54+
};
55+
56+
/* }}} */
57+
58+
59+
60+
/* {{{ PHP_MINIT_FUNCTION */
61+
PHP_MINIT_FUNCTION(Persistable)
62+
{
63+
(void)type;
64+
(void)module_number;
65+
zend_class_entry ce;
66+
67+
INIT_NS_CLASS_ENTRY(ce, "BSON", "Persistable", php_phongo_persistable_me);
68+
php_phongo_persistable_ce = zend_register_internal_interface(&ce TSRMLS_CC);
69+
zend_class_implements(php_phongo_persistable_ce TSRMLS_CC, 2, php_phongo_unserializable_ce, php_phongo_serializable_ce);
70+
71+
return SUCCESS;
72+
}
73+
/* }}} */
74+
75+
76+
77+
/*
78+
* Local variables:
79+
* tab-width: 4
80+
* c-basic-offset: 4
81+
* End:
82+
* vim600: noet sw=4 ts=4 fdm=marker
83+
* vim<600: noet sw=4 ts=4
84+
*/

src/BSON/Serializable.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ PHP_MINIT_FUNCTION(Serializable)
7070

7171
INIT_NS_CLASS_ENTRY(ce, "BSON", "Serializable", php_phongo_serializable_me);
7272
php_phongo_serializable_ce = zend_register_internal_interface(&ce TSRMLS_CC);
73+
zend_class_implements(php_phongo_serializable_ce TSRMLS_CC, 1, php_phongo_type_ce);
7374

7475
return SUCCESS;
7576
}

src/MongoDB/CommandResult.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ PHP_METHOD(CommandResult, getResponseDocument)
150150

151151

152152
if (intern->result.firstBatch) {
153-
php_phongo_bson_state state = {NULL, {NULL, NULL}};
153+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
154154

155155
state.zchild = return_value;
156156
bson_to_zval(bson_get_data(intern->result.firstBatch), intern->result.firstBatch->len, &state);

src/MongoDB/QueryResult.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ PHP_METHOD(QueryResult, __construct)
7272
PHP_METHOD(QueryResult, setTypemap)
7373
{
7474
php_phongo_writeresult_t *intern;
75+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
7576
zend_error_handling error_handling;
77+
zval *typemap = NULL;
7678
(void)return_value_ptr; (void)return_value_used;
77-
zval *typemap = NULL;
78-
php_phongo_bson_state state = {NULL, {NULL, NULL} };
7979

8080

8181
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling TSRMLS_CC);

src/bson.c

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,14 @@ bool php_phongo_bson_visit_binary(const bson_iter_t *iter __attribute__((unused)
207207
TSRMLS_FETCH();
208208
zval *zchild = NULL;
209209

210+
if (v_subtype == 0x80) {
211+
((php_phongo_bson_state *)data)->odm = zend_fetch_class((char *)v_binary, v_binary_len, ZEND_FETCH_CLASS_AUTO|ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
212+
if (((php_phongo_bson_state *)data)->odm) {
213+
return false;
214+
}
215+
/* Couldn't resolve the classname, resolve the type as binary */
216+
}
217+
210218
MAKE_STD_ZVAL(zchild);
211219
php_phongo_new_binary_from_binary_and_subtype(zchild, (const char *)v_binary, v_binary_len, v_subtype TSRMLS_CC);
212220

@@ -453,28 +461,28 @@ bool php_phongo_bson_visit_document(const bson_iter_t *iter __attribute__((unuse
453461
TSRMLS_FETCH();
454462

455463
if (bson_iter_init(&child, v_document)) {
456-
php_phongo_bson_state state = {NULL, {NULL, NULL} };
464+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
457465

458466
state.map = ((php_phongo_bson_state *)data)->map;
459467

460468
MAKE_STD_ZVAL(state.zchild);
461469
array_init(state.zchild);
462470

463471
if (!bson_iter_visit_all(&child, &php_bson_visitors, &state)) {
464-
if (state.map.document) {
465-
zval tmp_val = *state.zchild;
466-
467-
object_and_properties_init(state.zchild, state.map.document, NULL);
468-
zend_call_method_with_1_params(&state.zchild, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, &tmp_val);
469-
zval_dtor(&tmp_val);
472+
if (state.map.document || state.odm) {
473+
zval *obj = NULL;
474+
475+
MAKE_STD_ZVAL(obj);
476+
object_init_ex(obj, state.odm ? state.odm : state.map.document);
477+
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
478+
add_assoc_zval(retval, key, obj);
479+
zval_ptr_dtor(&state.zchild);
470480
} else {
471481
object_and_properties_init(state.zchild, zend_standard_class_def, Z_ARRVAL_P(state.zchild));
482+
add_assoc_zval(retval, key, state.zchild);
483+
Z_SET_REFCOUNT_P(state.zchild, 1);
472484
}
473-
474-
add_assoc_zval(retval, key, state.zchild);
475485
}
476-
477-
Z_SET_REFCOUNT_P(state.zchild, 1);
478486
}
479487

480488
return false;
@@ -488,7 +496,7 @@ bool php_phongo_bson_visit_array(const bson_iter_t *iter __attribute__((unused))
488496
TSRMLS_FETCH();
489497

490498
if (bson_iter_init(&child, v_array)) {
491-
php_phongo_bson_state state = {NULL, {NULL, NULL} };
499+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
492500

493501
state.map = ((php_phongo_bson_state *)data)->map;
494502

@@ -497,18 +505,20 @@ bool php_phongo_bson_visit_array(const bson_iter_t *iter __attribute__((unused))
497505

498506
if (!bson_iter_visit_all(&child, &php_bson_visitors, &state)) {
499507

500-
if (state.map.array) {
501-
zval tmp_val = *state.zchild;
508+
if (state.map.array || state.odm) {
509+
zval *obj = NULL;
502510

503-
object_and_properties_init(state.zchild, state.map.array, NULL);
504-
zend_call_method_with_1_params(&state.zchild, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, &tmp_val);
505-
zval_dtor(&tmp_val);
511+
MAKE_STD_ZVAL(obj);
512+
object_init_ex(obj, state.odm ? state.odm : state.map.array);
513+
zend_call_method_with_1_params(&obj, NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, state.zchild);
514+
add_assoc_zval(retval, key, obj);
515+
zval_ptr_dtor(&state.zchild);
516+
} else {
517+
add_assoc_zval(retval, key, state.zchild);
518+
Z_SET_REFCOUNT_P(state.zchild, 1);
506519
}
507-
508-
add_assoc_zval(retval, key, state.zchild);
509520
}
510521

511-
Z_SET_REFCOUNT_P(state.zchild, 1);
512522
}
513523

514524
return false;
@@ -585,6 +595,9 @@ void object_to_bson(zval *object, const char *key, long key_len, bson_t *bson TS
585595
}
586596

587597
bson_append_array_begin(bson, key, key_len, &child);
598+
if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce TSRMLS_CC)) {
599+
bson_append_binary(&child, "__", -1, 0x80, (const uint8_t *)Z_OBJCE_P(object)->name, strlen(Z_OBJCE_P(object)->name));
600+
}
588601
zval_to_bson(retval, PHONGO_BSON_NONE, &child, NULL TSRMLS_CC);
589602
bson_append_array_end(bson, &child);
590603

@@ -888,7 +901,7 @@ PHP_FUNCTION(toArray)
888901
char *data;
889902
int data_len;
890903
zval *typemap = NULL;
891-
php_phongo_bson_state state = {NULL, {NULL, NULL} };
904+
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
892905

893906
(void)return_value_ptr; (void)this_ptr; (void)return_value_used; /* We don't use these */
894907

tests/bson/bson-encode-002.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ BSON encoding: Encoding objects into BSON representation
66
<?php
77
require_once "tests/utils/basic.inc";
88

9-
class MyClass implements BSON\Serializable, BSON\Unserializable, BSON\Type {
9+
class MyClass implements BSON\Serializable, BSON\Unserializable {
1010
function bsonSerialize() {
1111
return array(
1212
"random" => "class",
@@ -17,7 +17,7 @@ class MyClass implements BSON\Serializable, BSON\Unserializable, BSON\Type {
1717
var_dump(__METHOD__, $data);
1818
}
1919
}
20-
class MyClass2 implements BSON\Serializable, BSON\Unserializable, BSON\Type {
20+
class MyClass2 implements BSON\Serializable, BSON\Unserializable {
2121
function bsonSerialize() {
2222
return array(
2323
1, 2, 3,

0 commit comments

Comments
 (0)