/* * Copyright 2014-present MongoDB, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bson/bson.h" #include #include #include #include "php_phongo.h" #include "phongo_bson.h" #include "phongo_bson_encode.h" #include "phongo_compat.h" #include "phongo_error.h" #undef MONGOC_LOG_DOMAIN #define MONGOC_LOG_DOMAIN "PHONGO-BSON" #if SIZEOF_ZEND_LONG == 8 #define BSON_APPEND_INT(b, key, keylen, val) \ if (val > INT32_MAX || val < INT32_MIN) { \ bson_append_int64(b, key, keylen, val); \ } else { \ bson_append_int32(b, key, keylen, val); \ } #elif SIZEOF_ZEND_LONG == 4 #define BSON_APPEND_INT(b, key, keylen, val) \ bson_append_int32(b, key, keylen, val) #else #error Unsupported architecture (integers are neither 32-bit nor 64-bit) #endif /* Forwards declarations */ 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); 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); /* Determines whether the argument should be serialized as a BSON array or * document. IS_ARRAY is returned if the argument's keys are a sequence of * integers starting at zero; otherwise, IS_OBJECT is returned. */ static int php_phongo_is_array_or_document(zval* val) { HashTable* ht_data = HASH_OF(val); int count; if (Z_TYPE_P(val) != IS_ARRAY) { if (Z_TYPE_P(val) == IS_OBJECT && instanceof_function(Z_OBJCE_P(val), php_phongo_packedarray_ce)) { return IS_ARRAY; } return IS_OBJECT; } count = ht_data ? zend_hash_num_elements(ht_data) : 0; if (count > 0) { zend_string* key; zend_ulong index, idx; idx = 0; ZEND_HASH_FOREACH_KEY(ht_data, index, key) { if (key) { return IS_OBJECT; } else { if (index != idx) { return IS_OBJECT; } } idx++; } ZEND_HASH_FOREACH_END(); } else { return Z_TYPE_P(val); } return IS_ARRAY; } /* Checks the return type of a bsonSerialize() method. Returns true on * success; otherwise, throws an exception and returns false. * * TODO: obsolete once the tentative return type in Serializable::bsonSerialize is enforced. */ static inline bool phongo_check_bson_serialize_return_type(zval* retval, zend_class_entry* ce) { if (instanceof_function(ce, php_phongo_persistable_ce)) { // Instances of Persistable must return an array, stdClass, or MongoDB\BSON\Document if ( Z_TYPE_P(retval) != IS_ARRAY && !(Z_TYPE_P(retval) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(retval), zend_standard_class_def) || instanceof_function(Z_OBJCE_P(retval), php_phongo_document_ce)))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected %s::%s() to return an array, stdClass, or %s, %s given", ZSTR_VAL(ce->name), BSON_SERIALIZE_FUNC_NAME, ZSTR_VAL(php_phongo_document_ce->name), zend_zval_type_name(retval)); return false; } return true; } if (instanceof_function(ce, php_phongo_serializable_ce)) { // Instances of Serializable must return an array, stdClass, MongoDB\BSON\Document, or MongoDB\BSON\PackedArray if ( Z_TYPE_P(retval) != IS_ARRAY && !(Z_TYPE_P(retval) == IS_OBJECT && (instanceof_function(Z_OBJCE_P(retval), zend_standard_class_def) || instanceof_function(Z_OBJCE_P(retval), php_phongo_document_ce) || instanceof_function(Z_OBJCE_P(retval), php_phongo_packedarray_ce)))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected %s::%s() to return an array, stdClass, %s, or %s, %s given", ZSTR_VAL(ce->name), BSON_SERIALIZE_FUNC_NAME, ZSTR_VAL(php_phongo_document_ce->name), ZSTR_VAL(php_phongo_packedarray_ce->name), zend_zval_type_name(retval)); return false; } return true; } phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Expected to receive instance of %s, %s given", ZSTR_VAL(php_phongo_serializable_ce->name), ZSTR_VAL(ce->name)); return false; } /* Appends the array or object argument to the BSON document. * * For instances of MongoDB\BSON\Document, raw BSON data is appended as document. * For instances of MongoDB\BSON\PackedArray, raw BSON data is appended as array. * For instances of MongoDB\BSON\Serializable, the return value of bsonSerialize() * will be appended as an embedded document. * Other MongoDB\BSON\Type instances will be appended as the appropriate BSON * type. * Other array or object values will be appended as an embedded document. */ 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) { if (Z_TYPE_P(object) == IS_OBJECT && instanceof_function(Z_OBJCE_P(object), php_phongo_type_ce)) { if (instanceof_function(Z_OBJCE_P(object), php_phongo_document_ce)) { php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(object); bson_append_document(bson, key, key_len, intern->bson); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_packedarray_ce)) { php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(object); bson_append_array(bson, key, key_len, intern->bson); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_serializable_ce)) { zval obj_data; bson_t child; zend_call_method_with_0_params(Z_OBJ_P(object), NULL, NULL, BSON_SERIALIZE_FUNC_NAME, &obj_data); if (Z_ISUNDEF(obj_data)) { /* zend_call_method() failed or bsonSerialize() threw an * exception. Either way, there is nothing else to do. */ return; } if (!phongo_check_bson_serialize_return_type(&obj_data, Z_OBJCE_P(object))) { // Exception already thrown zval_ptr_dtor(&obj_data); return; } /* Persistable objects must always be serialized as BSON documents; * otherwise, infer based on bsonSerialize()'s return value. */ if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce) || php_phongo_is_array_or_document(&obj_data) != IS_ARRAY) { bson_append_document_begin(bson, key, key_len, &child); if (instanceof_function(Z_OBJCE_P(object), php_phongo_persistable_ce)) { bson_append_binary(&child, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(object)->name->val, Z_OBJCE_P(object)->name->len); } php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, &child, NULL); bson_append_document_end(bson, &child); } else { bson_append_array_begin(bson, key, key_len, &child); php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, &child, NULL); bson_append_array_end(bson, &child); } zval_ptr_dtor(&obj_data); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_objectid_ce)) { bson_oid_t oid; php_phongo_objectid_t* intern = Z_OBJECTID_OBJ_P(object); bson_oid_init_from_string(&oid, intern->oid); bson_append_oid(bson, key, key_len, &oid); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_utcdatetime_ce)) { php_phongo_utcdatetime_t* intern = Z_UTCDATETIME_OBJ_P(object); bson_append_date_time(bson, key, key_len, intern->milliseconds); return; } // TODO: confirm that this handles binary vector if (instanceof_function(Z_OBJCE_P(object), php_phongo_binary_ce)) { php_phongo_binary_t* intern = Z_BINARY_OBJ_P(object); bson_append_binary(bson, key, key_len, intern->type, (const uint8_t*) intern->data, (uint32_t) intern->data_len); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_decimal128_ce)) { php_phongo_decimal128_t* intern = Z_DECIMAL128_OBJ_P(object); bson_append_decimal128(bson, key, key_len, &intern->decimal); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_int64_ce)) { php_phongo_int64_t* intern = Z_INT64_OBJ_P(object); bson_append_int64(bson, key, key_len, intern->integer); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_regex_ce)) { php_phongo_regex_t* intern = Z_REGEX_OBJ_P(object); bson_append_regex(bson, key, key_len, intern->pattern, intern->flags); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_javascript_ce)) { php_phongo_javascript_t* intern = Z_JAVASCRIPT_OBJ_P(object); if (intern->scope) { bson_append_code_with_scope(bson, key, key_len, intern->code, intern->scope); } else { bson_append_code(bson, key, key_len, intern->code); } return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_timestamp_ce)) { php_phongo_timestamp_t* intern = Z_TIMESTAMP_OBJ_P(object); bson_append_timestamp(bson, key, key_len, intern->timestamp, intern->increment); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_maxkey_ce)) { bson_append_maxkey(bson, key, key_len); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_minkey_ce)) { bson_append_minkey(bson, key, key_len); return; } /* Deprecated types */ if (instanceof_function(Z_OBJCE_P(object), php_phongo_dbpointer_ce)) { bson_oid_t oid; php_phongo_dbpointer_t* intern = Z_DBPOINTER_OBJ_P(object); bson_oid_init_from_string(&oid, intern->id); bson_append_dbpointer(bson, key, key_len, intern->ref, &oid); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_symbol_ce)) { php_phongo_symbol_t* intern = Z_SYMBOL_OBJ_P(object); bson_append_symbol(bson, key, key_len, intern->symbol, intern->symbol_len); return; } if (instanceof_function(Z_OBJCE_P(object), php_phongo_undefined_ce)) { bson_append_undefined(bson, key, key_len); return; } phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Unexpected %s instance: %s", ZSTR_VAL(php_phongo_type_ce->name), ZSTR_VAL(Z_OBJCE_P(object)->name)); return; } if (Z_TYPE_P(object) == IS_OBJECT && Z_OBJCE_P(object)->ce_flags & ZEND_ACC_ENUM) { if (Z_OBJCE_P(object)->enum_backing_type == IS_UNDEF) { char* path_string = php_phongo_field_path_as_string(field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Non-backed enum %s cannot be serialized for field path \"%s\"", ZSTR_VAL(Z_OBJCE_P(object)->name), path_string); efree(path_string); return; } php_phongo_bson_append(bson, field_path, flags, key, key_len, zend_enum_fetch_case_value(Z_OBJ_P(object))); return; } { bson_t child; bson_append_document_begin(bson, key, key_len, &child); php_phongo_zval_to_bson_internal(object, field_path, flags, &child, NULL); bson_append_document_end(bson, &child); } } /* Appends the zval argument to the BSON document. If the argument is an object, * or an array that should be serialized as an embedded document, this function * will defer to php_phongo_bson_append_object(). */ 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) { php_phongo_field_path_write_item_at_current_level(field_path, key); try_again: switch (Z_TYPE_P(entry)) { case IS_NULL: bson_append_null(bson, key, key_len); break; case IS_TRUE: bson_append_bool(bson, key, key_len, true); break; case IS_FALSE: bson_append_bool(bson, key, key_len, false); break; case IS_LONG: BSON_APPEND_INT(bson, key, key_len, Z_LVAL_P(entry)); break; case IS_DOUBLE: bson_append_double(bson, key, key_len, Z_DVAL_P(entry)); break; case IS_STRING: if (bson_utf8_validate(Z_STRVAL_P(entry), Z_STRLEN_P(entry), true)) { bson_append_utf8(bson, key, key_len, Z_STRVAL_P(entry), Z_STRLEN_P(entry)); } else { char* path_string = php_phongo_field_path_as_string(field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected invalid UTF-8 for field path \"%s\": %s", path_string, Z_STRVAL_P(entry)); efree(path_string); } break; case IS_ARRAY: if (php_phongo_is_array_or_document(entry) == IS_ARRAY) { bson_t child; HashTable* tmp_ht = HASH_OF(entry); if (!php_phongo_zend_hash_apply_protection_begin(tmp_ht)) { char* path_string = php_phongo_field_path_as_string(field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected recursion for field path \"%s\"", path_string); efree(path_string); break; } if (!php_phongo_field_path_push(field_path, NULL, PHONGO_FIELD_PATH_ITEM_ARRAY)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Nesting level too deep"); php_phongo_zend_hash_apply_protection_end(tmp_ht); break; } bson_append_array_begin(bson, key, key_len, &child); php_phongo_zval_to_bson_internal(entry, field_path, flags, &child, NULL); php_phongo_field_path_pop(field_path); bson_append_array_end(bson, &child); php_phongo_zend_hash_apply_protection_end(tmp_ht); break; } PHONGO_BREAK_INTENTIONALLY_MISSING case IS_OBJECT: { HashTable* tmp_ht = HASH_OF(entry); if (!php_phongo_zend_hash_apply_protection_begin(tmp_ht)) { char* path_string = php_phongo_field_path_as_string(field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected recursion for field path \"%s\"", path_string); efree(path_string); break; } if (Z_TYPE_P(entry) == IS_OBJECT && instanceof_function(Z_OBJCE_P(entry), php_phongo_packedarray_ce)) { if (!php_phongo_field_path_push(field_path, NULL, PHONGO_FIELD_PATH_ITEM_ARRAY)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Nesting level too deep"); php_phongo_zend_hash_apply_protection_end(tmp_ht); break; } } else { if (!php_phongo_field_path_push(field_path, NULL, PHONGO_FIELD_PATH_ITEM_DOCUMENT)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Nesting level too deep"); php_phongo_zend_hash_apply_protection_end(tmp_ht); break; } } php_phongo_bson_append_object(bson, field_path, flags, key, key_len, entry); php_phongo_field_path_pop(field_path); php_phongo_zend_hash_apply_protection_end(tmp_ht); break; } case IS_REFERENCE: ZVAL_DEREF(entry); goto try_again; default: { char* path_string = php_phongo_field_path_as_string(field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "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))); efree(path_string); } } } /* This is based on bson_copy_to_excluding_noinit() and is necessary because * bson_copy_to() cannot be used with a bson_t allocated with bson_new(). */ static void phongo_bson_copy_to_noinit(const bson_t* src, bson_t* dst) { bson_iter_t iter; if (bson_iter_init(&iter, src)) { while (bson_iter_next(&iter)) { if (!bson_append_iter(dst, NULL, 0, &iter)) { /* This should not be able to happen since we are copying from * within a valid bson_t. */ phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Error copying \"%s\" field from source document", bson_iter_key(&iter)); return; } } } } 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) { HashTable* ht_data = NULL; zval obj_data; /* If we will be encoding a class that may contain protected and private * properties, we'll need to filter them out later. */ bool ht_data_from_properties = false; /* If the object is an instance of MongoDB\BSON\Persistable, we will need to * inject the PHP class name as a BSON key and ignore any existing key in * the return value of bsonSerialize(). */ bool skip_odm_field = false; ZVAL_UNDEF(&obj_data); switch (Z_TYPE_P(data)) { case IS_OBJECT: /* Short-circuit MongoDB\BSON\Document and MongoDB\BSON\PackedArray instances - copy the data */ if (instanceof_function(Z_OBJCE_P(data), php_phongo_document_ce)) { php_phongo_document_t* intern = Z_DOCUMENT_OBJ_P(data); bson_iter_t iter; phongo_bson_copy_to_noinit(intern->bson, bson); // Check if the document instance already has an _id field if (flags & PHONGO_BSON_ADD_ID && bson_iter_init_find(&iter, bson, "_id")) { flags &= ~PHONGO_BSON_ADD_ID; } goto done; } if (instanceof_function(Z_OBJCE_P(data), php_phongo_packedarray_ce)) { /* If we are at the root-level, PackedArray instances should be * prohibited unless PHONGO_BSON_ALLOW_ROOT_ARRAY is set. */ bool is_root_level = (field_path->size == 0); if (is_root_level && !(flags & PHONGO_BSON_ALLOW_ROOT_ARRAY)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "%s cannot be serialized as a root document", ZSTR_VAL(Z_OBJCE_P(data)->name)); return; } php_phongo_packedarray_t* intern = Z_PACKEDARRAY_OBJ_P(data); phongo_bson_copy_to_noinit(intern->bson, bson); goto done; } /* For any MongoDB\BSON\Serializable, invoke the bsonSerialize method * and work with the result. */ if (instanceof_function(Z_OBJCE_P(data), php_phongo_serializable_ce)) { zend_call_method_with_0_params(Z_OBJ_P(data), NULL, NULL, BSON_SERIALIZE_FUNC_NAME, &obj_data); if (Z_ISUNDEF(obj_data)) { /* zend_call_method() failed or bsonSerialize() threw an * exception. Either way, there is nothing else to do. */ return; } if (!phongo_check_bson_serialize_return_type(&obj_data, Z_OBJCE_P(data))) { // Exception already thrown goto cleanup; } if (instanceof_function(Z_OBJCE_P(data), php_phongo_persistable_ce)) { bson_append_binary(bson, PHONGO_ODM_FIELD_NAME, -1, 0x80, (const uint8_t*) Z_OBJCE_P(data)->name->val, Z_OBJCE_P(data)->name->len); /* Ensure that we ignore an existing key with the same name * if one exists in the bsonSerialize() return value. */ skip_odm_field = true; } // If bsonSerialize() returns a BSON document or packedArray instance, recurse to copy data over directly if (Z_TYPE(obj_data) == IS_OBJECT && (instanceof_function(Z_OBJCE(obj_data), php_phongo_document_ce) || instanceof_function(Z_OBJCE(obj_data), php_phongo_packedarray_ce))) { php_phongo_zval_to_bson_internal(&obj_data, field_path, flags, bson, bson_out); goto done; } ht_data = HASH_OF(&obj_data); break; } /* For the error handling that follows, we can safely assume that we * are at the root level, since php_phongo_bson_append_object would * have already been called for a non-root level. */ if (Z_OBJCE_P(data)->ce_flags & ZEND_ACC_ENUM) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Enum %s cannot be serialized as a root element", ZSTR_VAL(Z_OBJCE_P(data)->name)); return; } if (instanceof_function(Z_OBJCE_P(data), php_phongo_type_ce)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "%s instance %s cannot be serialized as a root element", ZSTR_VAL(php_phongo_type_ce->name), ZSTR_VAL(Z_OBJCE_P(data)->name)); return; } ht_data = Z_OBJ_HT_P(data)->get_properties(Z_OBJ_P(data)); ht_data_from_properties = true; break; case IS_ARRAY: ht_data = HASH_OF(data); break; default: return; } { zend_string* string_key = NULL; zend_ulong num_key = 0; zval* value; ZEND_HASH_FOREACH_KEY_VAL_IND(ht_data, num_key, string_key, value) { if (string_key) { if (ht_data_from_properties) { /* Skip protected and private properties */ if (ZSTR_VAL(string_key)[0] == '\0' && ZSTR_LEN(string_key) > 0) { continue; } } if (strlen(ZSTR_VAL(string_key)) != ZSTR_LEN(string_key)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "BSON keys cannot contain null bytes. Unexpected null byte after \"%s\".", ZSTR_VAL(string_key)); goto cleanup; } if (skip_odm_field && !strcmp(ZSTR_VAL(string_key), PHONGO_ODM_FIELD_NAME)) { continue; } if (flags & PHONGO_BSON_ADD_ID) { if (!strcmp(ZSTR_VAL(string_key), "_id")) { flags &= ~PHONGO_BSON_ADD_ID; } } } /* Ensure we're working with a string key */ if (!string_key) { string_key = zend_long_to_str(num_key); } else { zend_string_addref(string_key); } php_phongo_bson_append(bson, field_path, flags & ~PHONGO_BSON_ADD_ID, ZSTR_VAL(string_key), strlen(ZSTR_VAL(string_key)), value); zend_string_release(string_key); } ZEND_HASH_FOREACH_END(); } done: if (flags & PHONGO_BSON_ADD_ID) { bson_oid_t oid; bson_oid_init(&oid, NULL); bson_append_oid(bson, "_id", strlen("_id"), &oid); } if (flags & PHONGO_BSON_RETURN_ID && bson_out) { bson_iter_t iter; *bson_out = bson_new(); if (bson_iter_init_find(&iter, bson, "_id") && !bson_append_iter(*bson_out, NULL, 0, &iter)) { /* This should not be able to happen since we are copying from * within a valid bson_t. */ phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Error copying \"_id\" field from encoded document"); goto cleanup; } } cleanup: if (!Z_ISUNDEF(obj_data)) { zval_ptr_dtor(&obj_data); } } /* Converts the array or object argument to a BSON document. If the object is an * instance of MongoDB\BSON\Serializable, the return value of bsonSerialize() * will be used. */ void php_phongo_zval_to_bson(zval* data, php_phongo_bson_flags_t flags, bson_t* bson, bson_t** bson_out) { php_phongo_field_path* field_path = php_phongo_field_path_alloc(false); php_phongo_zval_to_bson_internal(data, field_path, flags, bson, bson_out); php_phongo_field_path_free(field_path); } static void phongo_zval_to_bson_value_ex(zval* data, php_phongo_bson_flags_t flags, bson_value_t* value) { bson_iter_t iter; bson_t bson = BSON_INITIALIZER; zval data_object; array_init_size(&data_object, 1); add_assoc_zval(&data_object, "data", data); Z_TRY_ADDREF_P(data); php_phongo_zval_to_bson(&data_object, flags, &bson, NULL); if (bson_iter_init_find(&iter, &bson, "data")) { bson_value_copy(bson_iter_value(&iter), value); } bson_destroy(&bson); zval_ptr_dtor(&data_object); } /* Converts the argument to a bson_value_t. If the object is an instance of * MongoDB\BSON\Serializable, the return value of bsonSerialize() will be * used. It is the caller's responsibility to call bson_value_destroy. * * On success, the zval will be populated and true will be returned. On error, * an exception will have been thrown and false will be returned. */ bool phongo_zval_to_bson_value(zval* data, bson_value_t* value) { zend_long lvalue; ZVAL_DEREF(data); switch (Z_TYPE_P(data)) { case IS_UNDEF: case IS_NULL: value->value_type = BSON_TYPE_NULL; return true; case IS_FALSE: value->value_type = BSON_TYPE_BOOL; value->value.v_bool = false; return true; case IS_TRUE: value->value_type = BSON_TYPE_BOOL; value->value.v_bool = true; return true; case IS_LONG: lvalue = Z_LVAL_P(data); if (lvalue > INT32_MAX || lvalue < INT32_MIN) { value->value_type = BSON_TYPE_INT64; value->value.v_int64 = lvalue; } else { value->value_type = BSON_TYPE_INT32; value->value.v_int32 = (int32_t) lvalue; } return true; case IS_DOUBLE: value->value_type = BSON_TYPE_DOUBLE; value->value.v_double = Z_DVAL_P(data); return true; case IS_STRING: value->value_type = BSON_TYPE_UTF8; value->value.v_utf8.len = Z_STRLEN_P(data); /* Duplicate string as bson_value_t is expected to own values */ value->value.v_utf8.str = bson_malloc(value->value.v_utf8.len + 1); memcpy(value->value.v_utf8.str, Z_STRVAL_P(data), value->value.v_utf8.len); value->value.v_utf8.str[value->value.v_utf8.len] = '\0'; return true; case IS_ARRAY: case IS_OBJECT: /* Use php_phongo_zval_to_bson internally to convert arrays and documents */ phongo_zval_to_bson_value_ex(data, PHONGO_BSON_NONE, value); return true; } phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Unsupported type %s", zend_zval_type_name(data)); return false; }