62
62
63
63
#define PHONGO_ODM_FIELD_NAME "__pclass"
64
64
65
+ #define PHONGO_IS_CLASS_INSTANTIATABLE (ce ) \
66
+ (!(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)))
67
+
65
68
PHP_MINIT_FUNCTION (bson )
66
69
{
67
70
(void )type ; /* We don't care if we are loaded via dl() or extension= */
@@ -211,8 +214,12 @@ bool php_phongo_bson_visit_binary(const bson_iter_t *iter ARG_UNUSED, const char
211
214
zval * zchild = NULL ;
212
215
TSRMLS_FETCH ();
213
216
214
- if (v_subtype == 0x80 && strcmp (key , PHONGO_ODM_FIELD_NAME ) == 0 ) {
215
- ((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 );
217
+ if (v_subtype == 0x80 && strcmp (key , PHONGO_ODM_FIELD_NAME ) == 0 ) {
218
+ zend_class_entry * found_ce = zend_fetch_class ((char * )v_binary , v_binary_len , ZEND_FETCH_CLASS_AUTO |ZEND_FETCH_CLASS_SILENT TSRMLS_CC );
219
+
220
+ if (found_ce && PHONGO_IS_CLASS_INSTANTIATABLE (found_ce ) && instanceof_function (found_ce , php_phongo_persistable_ce TSRMLS_CC )) {
221
+ ((php_phongo_bson_state * )data )-> odm = found_ce ;
222
+ }
216
223
}
217
224
218
225
MAKE_STD_ZVAL (zchild );
@@ -463,26 +470,28 @@ bool php_phongo_bson_visit_document(const bson_iter_t *iter ARG_UNUSED, const ch
463
470
array_init (state .zchild );
464
471
465
472
if (!bson_iter_visit_all (& child , & php_bson_visitors , & state )) {
466
- if (state .odm ) {
473
+ /* If php_phongo_bson_visit_binary() finds an ODM class, it should
474
+ * supersede a default type map and named document class. */
475
+ if (state .odm && state .map .document_type == PHONGO_TYPEMAP_NONE ) {
467
476
state .map .document_type = PHONGO_TYPEMAP_CLASS ;
468
477
}
478
+
469
479
switch (state .map .document_type ) {
470
480
case PHONGO_TYPEMAP_NATIVE_ARRAY :
471
481
add_assoc_zval (retval , key , state .zchild );
472
482
Z_SET_REFCOUNT_P (state .zchild , 1 );
473
483
break ;
474
484
475
- case PHONGO_TYPEMAP_CLASS :
476
- if (instanceof_function (state .odm ? state .odm : state .map .document , php_phongo_unserializable_ce TSRMLS_CC )) {
477
- zval * obj = NULL ;
485
+ case PHONGO_TYPEMAP_CLASS : {
486
+ zval * obj = NULL ;
478
487
479
- MAKE_STD_ZVAL (obj );
480
- object_init_ex (obj , state .odm ? state .odm : state .map .document );
481
- zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state .zchild );
482
- add_assoc_zval (retval , key , obj );
483
- zval_ptr_dtor (& state .zchild );
484
- break ;
485
- }
488
+ MAKE_STD_ZVAL (obj );
489
+ object_init_ex (obj , state .odm ? state .odm : state .map .document );
490
+ zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state .zchild );
491
+ add_assoc_zval (retval , key , obj );
492
+ zval_ptr_dtor (& state .zchild );
493
+ break ;
494
+ }
486
495
487
496
case PHONGO_TYPEMAP_NATIVE_OBJECT :
488
497
default :
@@ -514,23 +523,17 @@ bool php_phongo_bson_visit_array(const bson_iter_t *iter ARG_UNUSED, const char
514
523
if (!bson_iter_visit_all (& child , & php_bson_visitors , & state )) {
515
524
516
525
switch (state .map .array_type ) {
517
- case PHONGO_TYPEMAP_CLASS :
518
- if (instanceof_function (state .map .array , php_phongo_unserializable_ce TSRMLS_CC )) {
519
- zval * obj = NULL ;
520
-
521
- MAKE_STD_ZVAL (obj );
522
- object_init_ex (obj , state .map .array );
523
- zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state .zchild );
524
- add_assoc_zval (retval , key , obj );
525
- zval_ptr_dtor (& state .zchild );
526
- break ;
527
- }
528
- /* If the object someehow doesn't implement php_phongo_unserializable_ce then use stdclass.
529
- * This is needed as we need to know how to pass the state.zchild to the class to populate it.
530
- * Not all classes have ctor that accepts first parameter array of values.
531
- */
526
+ case PHONGO_TYPEMAP_CLASS : {
527
+ zval * obj = NULL ;
528
+
529
+ MAKE_STD_ZVAL (obj );
530
+ object_init_ex (obj , state .map .array );
531
+ zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state .zchild );
532
+ add_assoc_zval (retval , key , obj );
533
+ zval_ptr_dtor (& state .zchild );
534
+ break ;
535
+ }
532
536
533
- /* break intentionally omitted */
534
537
case PHONGO_TYPEMAP_NATIVE_OBJECT :
535
538
object_and_properties_init (state .zchild , zend_standard_class_def , Z_ARRVAL_P (state .zchild ));
536
539
add_assoc_zval (retval , key , state .zchild );
@@ -617,8 +620,8 @@ void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key
617
620
return ;
618
621
}
619
622
620
- if (Z_TYPE_P (obj_data ) != IS_ARRAY ) {
621
- phongo_throw_exception (PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC , "Expected %s() to return an array, %s given" , BSON_SERIALIZE_FUNC_NAME , zend_get_type_by_const (Z_TYPE_P (obj_data )));
623
+ if (Z_TYPE_P (obj_data ) != IS_ARRAY && !( Z_TYPE_P ( obj_data ) == IS_OBJECT && instanceof_function ( Z_OBJCE_P ( obj_data ), zend_standard_class_def TSRMLS_CC )) ) {
624
+ phongo_throw_exception (PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC , "Expected %s::%s () to return an array or stdClass , %s given" , Z_OBJCE_P ( object ) -> name , BSON_SERIALIZE_FUNC_NAME , ( Z_TYPE_P ( obj_data ) == IS_OBJECT ? Z_OBJCE_P ( obj_data ) -> name : zend_get_type_by_const (Z_TYPE_P (obj_data ) )));
622
625
zval_ptr_dtor (& obj_data );
623
626
624
627
return ;
@@ -630,14 +633,22 @@ void object_to_bson(zval *object, php_phongo_bson_flags_t flags, const char *key
630
633
tmp_ht -> nApplyCount ++ ;
631
634
}
632
635
633
- bson_append_document_begin (bson , key , key_len , & child );
634
- if (instanceof_function (Z_OBJCE_P (object ), php_phongo_persistable_ce TSRMLS_CC )) {
635
- if (flags & PHONGO_BSON_ADD_CHILD_ODS ) {
636
- bson_append_binary (& child , PHONGO_ODM_FIELD_NAME , -1 , 0x80 , (const uint8_t * )Z_OBJCE_P (object )-> name , strlen (Z_OBJCE_P (object )-> name ));
636
+ /* Persistable objects must always be serialized as BSON documents;
637
+ * otherwise, infer based on bsonSerialize()'s return value. */
638
+ if (instanceof_function (Z_OBJCE_P (object ), php_phongo_persistable_ce TSRMLS_CC ) || php_phongo_is_array_or_document (& obj_data TSRMLS_CC ) == IS_OBJECT ) {
639
+ bson_append_document_begin (bson , key , key_len , & child );
640
+ if (instanceof_function (Z_OBJCE_P (object ), php_phongo_persistable_ce TSRMLS_CC )) {
641
+ if (flags & PHONGO_BSON_ADD_CHILD_ODS ) {
642
+ bson_append_binary (& child , PHONGO_ODM_FIELD_NAME , -1 , 0x80 , (const uint8_t * )Z_OBJCE_P (object )-> name , strlen (Z_OBJCE_P (object )-> name ));
643
+ }
637
644
}
645
+ zval_to_bson (obj_data , flags , & child , NULL TSRMLS_CC );
646
+ bson_append_document_end (bson , & child );
647
+ } else {
648
+ bson_append_array_begin (bson , key , key_len , & child );
649
+ zval_to_bson (obj_data , flags , & child , NULL TSRMLS_CC );
650
+ bson_append_array_end (bson , & child );
638
651
}
639
- zval_to_bson (obj_data , flags , & child , NULL TSRMLS_CC );
640
- bson_append_document_end (bson , & child );
641
652
642
653
if (tmp_ht ) {
643
654
tmp_ht -> nApplyCount -- ;
@@ -779,8 +790,8 @@ PHONGO_API void zval_to_bson(zval *data, php_phongo_bson_flags_t flags, bson_t *
779
790
break ;
780
791
}
781
792
782
- if (Z_TYPE_P (obj_data ) != IS_ARRAY ) {
783
- phongo_throw_exception (PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC , "Expected %s() to return an array, %s given" , BSON_SERIALIZE_FUNC_NAME , zend_get_type_by_const (Z_TYPE_P (obj_data )));
793
+ if (Z_TYPE_P (obj_data ) != IS_ARRAY && !( Z_TYPE_P ( obj_data ) == IS_OBJECT && instanceof_function ( Z_OBJCE_P ( obj_data ), zend_standard_class_def TSRMLS_CC )) ) {
794
+ phongo_throw_exception (PHONGO_ERROR_UNEXPECTED_VALUE TSRMLS_CC , "Expected %s::%s () to return an array or stdClass , %s given" , Z_OBJCE_P ( data ) -> name , BSON_SERIALIZE_FUNC_NAME , ( Z_TYPE_P ( obj_data ) == IS_OBJECT ? Z_OBJCE_P ( obj_data ) -> name : zend_get_type_by_const (Z_TYPE_P (obj_data ) )));
784
795
785
796
break ;
786
797
}
@@ -902,9 +913,9 @@ int bson_to_zval(const unsigned char *data, int data_len, php_phongo_bson_state
902
913
array_init (state -> zchild );
903
914
bson_iter_visit_all (& iter , & php_bson_visitors , state );
904
915
905
- /* If php_phongo_bson_visit_binary() finds an ODM class, it supersedes our
906
- * document type. */
907
- if (state -> odm ) {
916
+ /* If php_phongo_bson_visit_binary() finds an ODM class, it should supersede
917
+ * a default type map and named root class . */
918
+ if (state -> odm && state -> map . root_type == PHONGO_TYPEMAP_NONE ) {
908
919
state -> map .root_type = PHONGO_TYPEMAP_CLASS ;
909
920
}
910
921
@@ -913,19 +924,16 @@ int bson_to_zval(const unsigned char *data, int data_len, php_phongo_bson_state
913
924
/* Nothing to do here */
914
925
break ;
915
926
916
- case PHONGO_TYPEMAP_CLASS :
917
- /* If the class implements Unserializable, initialize the object
918
- * from our array data; otherwise, fall through to native object. */
919
- if (instanceof_function (state -> odm ? state -> odm : state -> map .root , php_phongo_unserializable_ce TSRMLS_CC )) {
920
- zval * obj = NULL ;
921
-
922
- MAKE_STD_ZVAL (obj );
923
- object_init_ex (obj , state -> odm ? state -> odm : state -> map .root );
924
- zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state -> zchild );
925
- zval_ptr_dtor (& state -> zchild );
926
- state -> zchild = obj ;
927
- break ;
928
- }
927
+ case PHONGO_TYPEMAP_CLASS : {
928
+ zval * obj = NULL ;
929
+
930
+ MAKE_STD_ZVAL (obj );
931
+ object_init_ex (obj , state -> odm ? state -> odm : state -> map .root );
932
+ zend_call_method_with_1_params (& obj , NULL , NULL , BSON_UNSERIALIZE_FUNC_NAME , NULL , state -> zchild );
933
+ zval_ptr_dtor (& state -> zchild );
934
+ state -> zchild = obj ;
935
+ break ;
936
+ }
929
937
930
938
case PHONGO_TYPEMAP_NATIVE_OBJECT :
931
939
default :
@@ -964,71 +972,63 @@ PHP_FUNCTION(fromPHP)
964
972
}
965
973
/* }}} */
966
974
975
+ static void apply_classname_to_state (const char * classname , int classname_len , php_phongo_bson_typemap_types * type , zend_class_entry * * type_ce TSRMLS_DC )
976
+ {
977
+ if (!strcasecmp (classname , "array" )) {
978
+ * type = PHONGO_TYPEMAP_NATIVE_ARRAY ;
979
+ * type_ce = NULL ;
980
+ } else if (!strcasecmp (classname , "stdclass" ) || !strcasecmp (classname , "object" )) {
981
+ * type = PHONGO_TYPEMAP_NATIVE_OBJECT ;
982
+ * type_ce = NULL ;
983
+ } else {
984
+ zend_class_entry * found_ce = zend_fetch_class (classname , classname_len , ZEND_FETCH_CLASS_AUTO |ZEND_FETCH_CLASS_SILENT TSRMLS_CC );
985
+
986
+ if (!found_ce ) {
987
+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Class %s does not exist" , classname );
988
+ } else if (!PHONGO_IS_CLASS_INSTANTIATABLE (found_ce )) {
989
+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Class %s is not instantiatable" , classname );
990
+ } else if (!instanceof_function (found_ce , php_phongo_unserializable_ce TSRMLS_CC )) {
991
+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Class %s does not implement %s\\Unserializable" , classname , BSON_NAMESPACE );
992
+ } else {
993
+ * type = PHONGO_TYPEMAP_CLASS ;
994
+ * type_ce = found_ce ;
995
+ }
996
+ }
997
+ }
998
+
967
999
void php_phongo_bson_typemap_to_state (zval * typemap , php_phongo_bson_typemap * map TSRMLS_DC )
968
1000
{
969
1001
if (typemap ) {
970
- char * classname ;
971
- int classname_len ;
972
- zend_bool classname_free = 0 ;
1002
+ char * classname ;
1003
+ int classname_len ;
1004
+ zend_bool classname_free = 0 ;
973
1005
974
1006
classname = php_array_fetchl_string (typemap , "array" , sizeof ("array" )- 1 , & classname_len , & classname_free );
975
1007
if (classname_len ) {
976
- if (!strcasecmp (classname , "array" )) {
977
- map -> array_type = PHONGO_TYPEMAP_NATIVE_ARRAY ;
978
- } else if (!strcasecmp (classname , "stdclass" ) || !strcasecmp (classname , "object" )) {
979
- map -> array_type = PHONGO_TYPEMAP_NATIVE_OBJECT ;
980
- } else {
981
- zend_class_entry * array_ce = zend_fetch_class (classname , classname_len , ZEND_FETCH_CLASS_AUTO TSRMLS_CC );
982
- map -> array_type = PHONGO_TYPEMAP_CLASS ;
983
-
984
- if (instanceof_function (array_ce , php_phongo_unserializable_ce TSRMLS_CC )) {
985
- map -> array = array_ce ;
986
- }
987
- }
988
- if (classname_free ) {
989
- efree (classname );
990
- }
1008
+ apply_classname_to_state (classname , classname_len , & map -> array_type , & map -> array TSRMLS_CC );
1009
+ }
1010
+ if (classname_free ) {
1011
+ str_efree (classname );
991
1012
}
992
1013
993
1014
classname = php_array_fetchl_string (typemap , "document" , sizeof ("document" )- 1 , & classname_len , & classname_free );
994
1015
if (classname_len ) {
995
- if (!strcasecmp (classname , "array" )) {
996
- map -> document_type = PHONGO_TYPEMAP_NATIVE_ARRAY ;
997
- } else if (!strcasecmp (classname , "stdclass" ) || !strcasecmp (classname , "object" )) {
998
- map -> document_type = PHONGO_TYPEMAP_NATIVE_OBJECT ;
999
- } else {
1000
- zend_class_entry * document_ce = zend_fetch_class (classname , classname_len , ZEND_FETCH_CLASS_AUTO TSRMLS_CC );
1001
- map -> document_type = PHONGO_TYPEMAP_CLASS ;
1002
-
1003
- if (instanceof_function (document_ce , php_phongo_unserializable_ce TSRMLS_CC )) {
1004
- map -> document = document_ce ;
1005
- }
1006
- }
1007
- if (classname_free ) {
1008
- efree (classname );
1009
- }
1016
+ apply_classname_to_state (classname , classname_len , & map -> document_type , & map -> document TSRMLS_CC );
1017
+ }
1018
+ if (classname_free ) {
1019
+ str_efree (classname );
1010
1020
}
1011
1021
1012
1022
classname = php_array_fetchl_string (typemap , "root" , sizeof ("root" )- 1 , & classname_len , & classname_free );
1013
1023
if (classname_len ) {
1014
- if (!strcasecmp (classname , "array" )) {
1015
- map -> root_type = PHONGO_TYPEMAP_NATIVE_ARRAY ;
1016
- } else if (!strcasecmp (classname , "stdclass" ) || !strcasecmp (classname , "object" )) {
1017
- map -> root_type = PHONGO_TYPEMAP_NATIVE_OBJECT ;
1018
- } else {
1019
- zend_class_entry * root_ce = zend_fetch_class (classname , classname_len , ZEND_FETCH_CLASS_AUTO TSRMLS_CC );
1020
- map -> root_type = PHONGO_TYPEMAP_CLASS ;
1021
-
1022
- if (instanceof_function (root_ce , php_phongo_unserializable_ce TSRMLS_CC )) {
1023
- map -> root = root_ce ;
1024
- }
1025
- }
1026
- if (classname_free ) {
1027
- efree (classname );
1028
- }
1024
+ apply_classname_to_state (classname , classname_len , & map -> root_type , & map -> root TSRMLS_CC );
1025
+ }
1026
+ if (classname_free ) {
1027
+ str_efree (classname );
1029
1028
}
1030
1029
}
1031
1030
}
1031
+
1032
1032
/* {{{ proto array|object BSON\toPHP(string $bson [, array $typemap = array()])
1033
1033
Returns the PHP representation of a BSON value, optionally converting it into a custom class */
1034
1034
PHP_FUNCTION (toPHP )
0 commit comments