⚝
One Hat Cyber Team
⚝
Your IP:
216.73.216.19
Server IP:
178.33.27.10
Server:
Linux cpanel.dev-unit.com 3.10.0-1160.108.1.el7.x86_64 #1 SMP Thu Jan 25 16:17:31 UTC 2024 x86_64
Server Software:
Apache/2.4.57 (Unix) OpenSSL/1.0.2k-fips
PHP Version:
8.2.11
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
proc
/
self
/
root
/
usr
/
local
/
src
/
mongodb-1.11.1
/
src
/
View File Name :
bson.c
/* * 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 <php.h> #include <Zend/zend_hash.h> #include <Zend/zend_interfaces.h> #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php_phongo.h" #include "php_bson.h" #include "phongo_compat.h" #include "php_array_api.h" #define DEBUG 0 #undef MONGOC_LOG_DOMAIN #define MONGOC_LOG_DOMAIN "PHONGO-BSON" #define PHONGO_IS_CLASS_INSTANTIATABLE(ce) \ (!(ce->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_IMPLICIT_ABSTRACT_CLASS | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) #define PHONGO_BSON_STATE_ZCHILD(state) (&((php_phongo_bson_state*) (state))->zchild) #define PHONGO_FIELD_PATH_EXPANSION 8 /* Forward declarations */ static bool php_phongo_bson_visit_document(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_t* v_document, void* data); static bool php_phongo_bson_visit_array(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_t* v_document, void* data); /* Path builder */ char* php_phongo_field_path_as_string(php_phongo_field_path* field_path) { size_t length = 1; /* NULL character */ size_t i; char* path; char* ptr; if (!field_path) { return estrdup(""); } if (!field_path->elements) { return estrdup(""); } for (i = 0; i <= field_path->size; i++) { if (!field_path->elements[i]) { continue; } length += (1 + strlen(field_path->elements[i])); } path = emalloc(length); ptr = path; for (i = 0; i <= field_path->size; i++) { if (!field_path->elements[i]) { continue; } strcpy(ptr, field_path->elements[i]); ptr += strlen(field_path->elements[i]); ptr[0] = '.'; ptr++; } ptr[-1] = '\0'; return path; } php_phongo_field_path* php_phongo_field_path_alloc(bool owns_elements) { php_phongo_field_path* tmp = ecalloc(1, sizeof(php_phongo_field_path)); tmp->ref_count = 1; tmp->owns_elements = owns_elements; return tmp; } void php_phongo_field_path_free(php_phongo_field_path* field_path) { if (field_path->owns_elements) { size_t i; for (i = 0; i < field_path->size; i++) { efree(field_path->elements[i]); } } if (field_path->elements) { efree(field_path->elements); } if (field_path->element_types) { efree(field_path->element_types); } efree(field_path); } static void php_phongo_field_path_ensure_allocation(php_phongo_field_path* field_path, size_t level) { if (level >= field_path->allocated_size) { size_t i; field_path->allocated_size = field_path->size + PHONGO_FIELD_PATH_EXPANSION; field_path->elements = erealloc(field_path->elements, sizeof(char*) * field_path->allocated_size); field_path->element_types = erealloc(field_path->element_types, sizeof(php_phongo_bson_field_path_item_types) * field_path->allocated_size); for (i = level; i < field_path->allocated_size; i++) { field_path->elements[i] = NULL; field_path->element_types[i] = PHONGO_FIELD_PATH_ITEM_NONE; } } } void php_phongo_field_path_write_item_at_current_level(php_phongo_field_path* field_path, const char* element) { php_phongo_field_path_ensure_allocation(field_path, field_path->size); if (field_path->owns_elements) { field_path->elements[field_path->size] = estrdup(element); } else { field_path->elements[field_path->size] = (char*) element; } } void php_phongo_field_path_write_type_at_current_level(php_phongo_field_path* field_path, php_phongo_bson_field_path_item_types element_type) { php_phongo_field_path_ensure_allocation(field_path, field_path->size); field_path->element_types[field_path->size] = element_type; } bool php_phongo_field_path_push(php_phongo_field_path* field_path, const char* element, php_phongo_bson_field_path_item_types element_type) { php_phongo_field_path_write_item_at_current_level(field_path, element); php_phongo_field_path_write_type_at_current_level(field_path, element_type); field_path->size++; return true; } bool php_phongo_field_path_pop(php_phongo_field_path* field_path) { php_phongo_field_path_ensure_allocation(field_path, field_path->size); field_path->elements[field_path->size] = NULL; field_path->element_types[field_path->size] = PHONGO_FIELD_PATH_ITEM_NONE; field_path->size--; field_path->elements[field_path->size] = NULL; field_path->element_types[field_path->size] = PHONGO_FIELD_PATH_ITEM_NONE; return true; } inline static bool php_phongo_bson_state_is_initialized(php_phongo_bson_state* state) { return state->field_path != NULL; } void php_phongo_bson_state_ctor(php_phongo_bson_state* state) { state->field_path = php_phongo_field_path_alloc(false); } void php_phongo_bson_state_copy_ctor(php_phongo_bson_state* dst, php_phongo_bson_state* src) { dst->map = src->map; if (src->field_path) { src->field_path->ref_count++; } dst->field_path = src->field_path; } void php_phongo_bson_state_dtor(php_phongo_bson_state* state) { if (state->field_path) { state->field_path->ref_count--; if (state->field_path->ref_count < 1) { php_phongo_field_path_free(state->field_path); } state->field_path = NULL; } } /* }}} */ static void php_phongo_bson_visit_corrupt(const bson_iter_t* iter ARG_UNUSED, void* data ARG_UNUSED) /* {{{ */ { mongoc_log(MONGOC_LOG_LEVEL_WARNING, MONGOC_LOG_DOMAIN, "Corrupt BSON data detected!"); } /* }}} */ static void php_phongo_bson_visit_unsupported_type(const bson_iter_t* iter ARG_UNUSED, const char* key, uint32_t v_type_code, void* data ARG_UNUSED) /* {{{ */ { php_phongo_bson_state* state = (php_phongo_bson_state*) data; char* path_string; php_phongo_field_path_write_item_at_current_level(state->field_path, key); path_string = php_phongo_field_path_as_string(state->field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected unknown BSON type 0x%02hhx for field path \"%s\". Are you using the latest driver?", (unsigned char) v_type_code, path_string); efree(path_string); } /* }}} */ static bool php_phongo_bson_visit_double(const bson_iter_t* iter ARG_UNUSED, const char* key, double v_double, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (state->is_visiting_array) { add_next_index_double(retval, v_double); } else { add_assoc_double(retval, key, v_double); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_utf8(const bson_iter_t* iter ARG_UNUSED, const char* key, size_t v_utf8_len, const char* v_utf8, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (state->is_visiting_array) { ADD_NEXT_INDEX_STRINGL(retval, v_utf8, v_utf8_len); } else { ADD_ASSOC_STRING_EX(retval, key, strlen(key), v_utf8, v_utf8_len); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_binary_from_binary_and_type(zval* object, const char* data, size_t data_len, bson_subtype_t type) /* {{{ */ { php_phongo_binary_t* intern; object_init_ex(object, php_phongo_binary_ce); intern = Z_BINARY_OBJ_P(object); intern->data = estrndup(data, data_len); intern->data_len = data_len; intern->type = (uint8_t) type; } /* }}} */ static bool php_phongo_bson_visit_binary(const bson_iter_t* iter ARG_UNUSED, const char* key, bson_subtype_t v_subtype, size_t v_binary_len, const uint8_t* v_binary, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (v_subtype == 0x80 && strcmp(key, PHONGO_ODM_FIELD_NAME) == 0) { zend_string* zs_classname = zend_string_init((const char*) v_binary, v_binary_len, 0); zend_class_entry* found_ce = zend_fetch_class(zs_classname, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); zend_string_release(zs_classname); if (found_ce && PHONGO_IS_CLASS_INSTANTIATABLE(found_ce) && instanceof_function(found_ce, php_phongo_persistable_ce)) { ((php_phongo_bson_state*) data)->odm = found_ce; } } { zval zchild; php_phongo_bson_new_binary_from_binary_and_type(&zchild, (const char*) v_binary, v_binary_len, v_subtype); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_undefined(const bson_iter_t* iter, const char* key, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; object_init_ex(&zchild, php_phongo_undefined_ce); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_objectid_new_from_oid(zval* object, const bson_oid_t* oid) /* {{{ */ { php_phongo_objectid_t* intern; object_init_ex(object, php_phongo_objectid_ce); intern = Z_OBJECTID_OBJ_P(object); bson_oid_to_string(oid, intern->oid); intern->initialized = true; } /* }}} */ static bool php_phongo_bson_visit_oid(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_oid_t* v_oid, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_objectid_new_from_oid(&zchild, v_oid); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_bool(const bson_iter_t* iter ARG_UNUSED, const char* key, bool v_bool, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (state->is_visiting_array) { add_next_index_bool(retval, v_bool); } else { add_assoc_bool(retval, key, v_bool); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_utcdatetime_from_epoch(zval* object, int64_t msec_since_epoch) /* {{{ */ { php_phongo_utcdatetime_t* intern; object_init_ex(object, php_phongo_utcdatetime_ce); intern = Z_UTCDATETIME_OBJ_P(object); intern->milliseconds = msec_since_epoch; intern->initialized = true; } /* }}} */ static bool php_phongo_bson_visit_date_time(const bson_iter_t* iter ARG_UNUSED, const char* key, int64_t msec_since_epoch, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_utcdatetime_from_epoch(&zchild, msec_since_epoch); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_decimal128(zval* object, const bson_decimal128_t* decimal) /* {{{ */ { php_phongo_decimal128_t* intern; object_init_ex(object, php_phongo_decimal128_ce); intern = Z_DECIMAL128_OBJ_P(object); memcpy(&intern->decimal, decimal, sizeof(bson_decimal128_t)); intern->initialized = true; } /* }}} */ static bool php_phongo_bson_visit_decimal128(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_decimal128_t* decimal, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_decimal128(&zchild, decimal); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_null(const bson_iter_t* iter ARG_UNUSED, const char* key, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (state->is_visiting_array) { add_next_index_null(retval); } else { add_assoc_null(retval, key); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_regex_from_regex_and_options(zval* object, const char* pattern, const char* flags) /* {{{ */ { php_phongo_regex_t* intern; object_init_ex(object, php_phongo_regex_ce); intern = Z_REGEX_OBJ_P(object); intern->pattern_len = strlen(pattern); intern->pattern = estrndup(pattern, intern->pattern_len); intern->flags_len = strlen(flags); intern->flags = estrndup(flags, intern->flags_len); } /* }}} */ static bool php_phongo_bson_visit_regex(const bson_iter_t* iter ARG_UNUSED, const char* key, const char* v_regex, const char* v_options, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_regex_from_regex_and_options(&zchild, v_regex, v_options); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_symbol(zval* object, const char* symbol, size_t symbol_len) /* {{{ */ { php_phongo_symbol_t* intern; object_init_ex(object, php_phongo_symbol_ce); intern = Z_SYMBOL_OBJ_P(object); intern->symbol = estrndup(symbol, symbol_len); intern->symbol_len = symbol_len; } /* }}} */ static bool php_phongo_bson_visit_symbol(const bson_iter_t* iter, const char* key, size_t v_symbol_len, const char* v_symbol, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_symbol(&zchild, v_symbol, v_symbol_len); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_new_javascript_from_javascript_and_scope(zval* object, const char* code, size_t code_len, const bson_t* scope) /* {{{ */ { php_phongo_javascript_t* intern; if (scope) { php_phongo_bson_state state; bool valid_scope; PHONGO_BSON_INIT_STATE(state); valid_scope = php_phongo_bson_to_zval_ex(bson_get_data(scope), scope->len, &state); zval_ptr_dtor(&state.zchild); if (!valid_scope) { return false; } } object_init_ex(object, php_phongo_javascript_ce); intern = Z_JAVASCRIPT_OBJ_P(object); intern->code = estrndup(code, code_len); intern->code_len = code_len; intern->scope = scope ? bson_copy(scope) : NULL; return true; } /* }}} */ static bool php_phongo_bson_new_javascript_from_javascript(zval* object, const char* code, size_t code_len) /* {{{ */ { return php_phongo_bson_new_javascript_from_javascript_and_scope(object, code, code_len, NULL); } /* }}} */ static bool php_phongo_bson_visit_code(const bson_iter_t* iter ARG_UNUSED, const char* key, size_t v_code_len, const char* v_code, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; if (!php_phongo_bson_new_javascript_from_javascript(&zchild, v_code, v_code_len)) { return true; } if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static void php_phongo_bson_new_dbpointer(zval* object, const char* ref, size_t ref_len, const bson_oid_t* oid) /* {{{ */ { php_phongo_dbpointer_t* intern; object_init_ex(object, php_phongo_dbpointer_ce); intern = Z_DBPOINTER_OBJ_P(object); intern->ref = estrndup(ref, ref_len); intern->ref_len = ref_len; bson_oid_to_string(oid, intern->id); } /* }}} */ static bool php_phongo_bson_visit_dbpointer(const bson_iter_t* iter, const char* key, size_t namespace_len, const char* namespace, const bson_oid_t* oid, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_dbpointer(&zchild, namespace, namespace_len, oid); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_codewscope(const bson_iter_t* iter ARG_UNUSED, const char* key, size_t v_code_len, const char* v_code, const bson_t* v_scope, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; if (!php_phongo_bson_new_javascript_from_javascript_and_scope(&zchild, v_code, v_code_len, v_scope)) { return true; } if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_int32(const bson_iter_t* iter ARG_UNUSED, const char* key, int32_t v_int32, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; if (state->is_visiting_array) { add_next_index_long(retval, v_int32); } else { add_assoc_long(retval, key, v_int32); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_timestamp(const bson_iter_t* iter ARG_UNUSED, const char* key, uint32_t v_timestamp, uint32_t v_increment, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; php_phongo_bson_new_timestamp_from_increment_and_timestamp(&zchild, v_increment, v_timestamp); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_int64(const bson_iter_t* iter ARG_UNUSED, const char* key, int64_t v_int64, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; php_phongo_field_path_write_item_at_current_level(state->field_path, key); if (state->is_visiting_array) { ADD_NEXT_INDEX_INT64(retval, v_int64); } else { ADD_ASSOC_INT64(retval, key, v_int64); } return false; } /* }}} */ static bool php_phongo_bson_visit_maxkey(const bson_iter_t* iter ARG_UNUSED, const char* key, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; object_init_ex(&zchild, php_phongo_maxkey_ce); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static bool php_phongo_bson_visit_minkey(const bson_iter_t* iter ARG_UNUSED, const char* key, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); php_phongo_bson_state* state = (php_phongo_bson_state*) data; zval zchild; object_init_ex(&zchild, php_phongo_minkey_ce); if (state->is_visiting_array) { add_next_index_zval(retval, &zchild); } else { ADD_ASSOC_ZVAL(retval, key, &zchild); } php_phongo_field_path_write_item_at_current_level(state->field_path, key); return false; } /* }}} */ static const bson_visitor_t php_bson_visitors = { NULL /* php_phongo_bson_visit_before*/, NULL /*php_phongo_bson_visit_after*/, php_phongo_bson_visit_corrupt, php_phongo_bson_visit_double, php_phongo_bson_visit_utf8, php_phongo_bson_visit_document, php_phongo_bson_visit_array, php_phongo_bson_visit_binary, php_phongo_bson_visit_undefined, php_phongo_bson_visit_oid, php_phongo_bson_visit_bool, php_phongo_bson_visit_date_time, php_phongo_bson_visit_null, php_phongo_bson_visit_regex, php_phongo_bson_visit_dbpointer, php_phongo_bson_visit_code, php_phongo_bson_visit_symbol, php_phongo_bson_visit_codewscope, php_phongo_bson_visit_int32, php_phongo_bson_visit_timestamp, php_phongo_bson_visit_int64, php_phongo_bson_visit_maxkey, php_phongo_bson_visit_minkey, php_phongo_bson_visit_unsupported_type, php_phongo_bson_visit_decimal128, { NULL } }; static inline bool map_element_matches_field_path(php_phongo_field_path_map_element* map_element, php_phongo_field_path* current) { size_t i; if (map_element->entry->size != current->size) { return false; } for (i = 0; i < current->size; i++) { if (strcmp(map_element->entry->elements[i], "$") == 0) { continue; } if (strcmp(map_element->entry->elements[i], current->elements[i]) != 0) { return false; } } return true; } static php_phongo_field_path_map_element* map_find_field_path_entry(php_phongo_bson_state* state) { size_t i; /* Loop over all field path mappings, and for each, try to see whether it matches the current path */ for (i = 0; i < state->map.field_paths.size; i++) { if (map_element_matches_field_path(state->map.field_paths.map[i], state->field_path)) { return state->map.field_paths.map[i]; } } return NULL; } static void php_phongo_handle_field_path_entry_for_compound_type(php_phongo_bson_state* state, php_phongo_bson_typemap_types* type, zend_class_entry** ce) { php_phongo_field_path_map_element* entry = map_find_field_path_entry(state); if (entry) { switch (entry->node_type) { case PHONGO_TYPEMAP_NATIVE_ARRAY: case PHONGO_TYPEMAP_NATIVE_OBJECT: *type = entry->node_type; break; case PHONGO_TYPEMAP_CLASS: *type = entry->node_type; *ce = entry->node_ce; break; default: /* Do nothing - pacify compiler */ break; } } } static bool php_phongo_bson_visit_document(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_t* v_document, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); bson_iter_t child; php_phongo_bson_state* parent_state = (php_phongo_bson_state*) data; php_phongo_field_path_push(parent_state->field_path, key, PHONGO_FIELD_PATH_ITEM_DOCUMENT); if (bson_iter_init(&child, v_document)) { php_phongo_bson_state state; PHONGO_BSON_INIT_STATE(state); php_phongo_bson_state_copy_ctor(&state, parent_state); array_init(&state.zchild); if (!bson_iter_visit_all(&child, &php_bson_visitors, &state) && !child.err_off) { /* Check for entries in the fieldPath type map key, and use them to * override the default ones for this type */ php_phongo_handle_field_path_entry_for_compound_type(&state, &state.map.document_type, &state.map.document); /* If php_phongo_bson_visit_binary() finds an ODM class, it should * supersede a default type map and named document class. */ if (state.odm && state.map.document_type == PHONGO_TYPEMAP_NONE) { state.map.document_type = PHONGO_TYPEMAP_CLASS; } switch (state.map.document_type) { case PHONGO_TYPEMAP_NATIVE_ARRAY: if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &state.zchild); } else { ADD_ASSOC_ZVAL(retval, key, &state.zchild); } break; case PHONGO_TYPEMAP_CLASS: { zval obj; object_init_ex(&obj, state.odm ? state.odm : state.map.document); zend_call_method_with_1_params(PHONGO_COMPAT_OBJ_P(&obj), NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, &state.zchild); if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &obj); } else { ADD_ASSOC_ZVAL(retval, key, &obj); } zval_ptr_dtor(&state.zchild); break; } case PHONGO_TYPEMAP_NATIVE_OBJECT: default: convert_to_object(&state.zchild); if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &state.zchild); } else { ADD_ASSOC_ZVAL(retval, key, &state.zchild); } } } else { /* Iteration stopped prematurely due to corruption or a failed * visitor. Free state.zchild, which we just initialized, and return * true to stop iteration for our parent context. */ zval_ptr_dtor(&state.zchild); php_phongo_bson_state_dtor(&state); return true; } php_phongo_bson_state_dtor(&state); php_phongo_field_path_pop(parent_state->field_path); } return false; } /* }}} */ static bool php_phongo_bson_visit_array(const bson_iter_t* iter ARG_UNUSED, const char* key, const bson_t* v_array, void* data) /* {{{ */ { zval* retval = PHONGO_BSON_STATE_ZCHILD(data); bson_iter_t child; php_phongo_bson_state* parent_state = (php_phongo_bson_state*) data; php_phongo_field_path_push(parent_state->field_path, key, PHONGO_FIELD_PATH_ITEM_ARRAY); if (bson_iter_init(&child, v_array)) { php_phongo_bson_state state; PHONGO_BSON_INIT_STATE(state); php_phongo_bson_state_copy_ctor(&state, parent_state); /* Note that we are visiting an array, so element visitors know to use * add_next_index() (i.e. disregard BSON keys) instead of add_assoc() * when building the PHP array. */ state.is_visiting_array = true; array_init(&state.zchild); if (!bson_iter_visit_all(&child, &php_bson_visitors, &state) && !child.err_off) { /* Check for entries in the fieldPath type map key, and use them to * override the default ones for this type */ php_phongo_handle_field_path_entry_for_compound_type(&state, &state.map.array_type, &state.map.array); switch (state.map.array_type) { case PHONGO_TYPEMAP_CLASS: { zval obj; object_init_ex(&obj, state.map.array); zend_call_method_with_1_params(PHONGO_COMPAT_OBJ_P(&obj), NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, &state.zchild); if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &obj); } else { ADD_ASSOC_ZVAL(retval, key, &obj); } zval_ptr_dtor(&state.zchild); break; } case PHONGO_TYPEMAP_NATIVE_OBJECT: convert_to_object(&state.zchild); if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &state.zchild); } else { ADD_ASSOC_ZVAL(retval, key, &state.zchild); } break; case PHONGO_TYPEMAP_NATIVE_ARRAY: default: if (((php_phongo_bson_state*) data)->is_visiting_array) { add_next_index_zval(retval, &state.zchild); } else { ADD_ASSOC_ZVAL(retval, key, &state.zchild); } break; } } else { /* Iteration stopped prematurely due to corruption or a failed * visitor. Free state.zchild, which we just initialized, and return * true to stop iteration for our parent context. */ zval_ptr_dtor(&state.zchild); php_phongo_bson_state_dtor(&state); return true; } php_phongo_bson_state_dtor(&state); php_phongo_field_path_pop(parent_state->field_path); } return false; } /* }}} */ /* Converts a BSON document to a PHP value using the default typemap. */ bool php_phongo_bson_to_zval(const unsigned char* data, int data_len, zval* zv) /* {{{ */ { bool retval; php_phongo_bson_state state; PHONGO_BSON_INIT_STATE(state); retval = php_phongo_bson_to_zval_ex(data, data_len, &state); ZVAL_ZVAL(zv, &state.zchild, 1, 1); return retval; } /* }}} */ /* Converts a BSON value to a ZVAL. */ bool php_phongo_bson_value_to_zval(const bson_value_t* value, zval* zv) /* {{{ */ { bson_t bson = BSON_INITIALIZER; php_phongo_bson_state state; zval* return_value; bool retval = false; PHONGO_BSON_INIT_STATE(state); state.map.root_type = PHONGO_TYPEMAP_NATIVE_ARRAY; bson_append_value(&bson, "data", 4, value); if (!php_phongo_bson_to_zval_ex(bson_get_data(&bson), bson.len, &state)) { /* Exception already thrown */ goto cleanup; } retval = true; return_value = php_array_fetchc(&state.zchild, "data"); if (return_value) { ZVAL_ZVAL(zv, return_value, 1, 0); } cleanup: zval_ptr_dtor(&state.zchild); return retval; } /* }}} */ /* Converts a BSON document to a PHP value according to the typemap specified in * the state argument. * * On success, the result will be set on the state argument and true will be * returned. On error, an exception will have been thrown and false will be * returned. * * Note: the result zval in the state argument will always be initialized for * PHP 5.x so that the caller may always zval_ptr_dtor() it. The zval is left * as-is on PHP 7; however, it should have the type undefined if the state * was initialized to zero. */ bool php_phongo_bson_to_zval_ex(const unsigned char* data, int data_len, php_phongo_bson_state* state) /* {{{ */ { bson_reader_t* reader = NULL; bson_iter_t iter; const bson_t* b; bool eof = false; bool retval = false; bool must_dtor_state = false; if (!php_phongo_bson_state_is_initialized(state)) { php_phongo_bson_state_ctor(state); must_dtor_state = true; } reader = bson_reader_new_from_data(data, data_len); if (!(b = bson_reader_read(reader, NULL))) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Could not read document from BSON reader"); goto cleanup; } if (!bson_iter_init(&iter, b)) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Could not initialize BSON iterator"); goto cleanup; } /* We initialize an array because it will either be returned as-is (native * array in type map), passed to bsonUnserialize() (ODM class), or used to * initialize a stdClass object (native object in type map). */ array_init(&state->zchild); if (bson_iter_visit_all(&iter, &php_bson_visitors, state) || iter.err_off) { /* Iteration stopped prematurely due to corruption or a failed visitor. * While we free the reader, state->zchild should be left as-is, since * the calling code may want to zval_ptr_dtor() it. If an exception has * been thrown already (due to an unsupported BSON type for example, * don't overwrite with a generic exception message. */ if (!EG(exception)) { char* path = php_phongo_field_path_as_string(state->field_path); phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected corrupt BSON data for field path '%s' at offset %d", path, iter.err_off); efree(path); } goto cleanup; } /* If php_phongo_bson_visit_binary() finds an ODM class, it should supersede * a default type map and named root class. */ if (state->odm && state->map.root_type == PHONGO_TYPEMAP_NONE) { state->map.root_type = PHONGO_TYPEMAP_CLASS; } switch (state->map.root_type) { case PHONGO_TYPEMAP_NATIVE_ARRAY: /* Nothing to do here */ break; case PHONGO_TYPEMAP_CLASS: { zval obj; object_init_ex(&obj, state->odm ? state->odm : state->map.root); zend_call_method_with_1_params(PHONGO_COMPAT_OBJ_P(&obj), NULL, NULL, BSON_UNSERIALIZE_FUNC_NAME, NULL, &state->zchild); zval_ptr_dtor(&state->zchild); ZVAL_COPY_VALUE(&state->zchild, &obj); break; } case PHONGO_TYPEMAP_NATIVE_OBJECT: default: convert_to_object(&state->zchild); } if (bson_reader_read(reader, &eof) || !eof) { phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Reading document did not exhaust input buffer"); goto cleanup; } retval = true; cleanup: if (reader) { bson_reader_destroy(reader); } if (must_dtor_state) { php_phongo_bson_state_dtor(state); } return retval; } /* }}} */ /* Fetches a zend_class_entry for the given class name and checks that it is * also instantiatable and implements a specified interface. Returns the class * on success; otherwise, NULL is returned and an exception is thrown. */ static zend_class_entry* php_phongo_bson_state_fetch_class(const char* classname, int classname_len, zend_class_entry* interface_ce) /* {{{ */ { zend_string* zs_classname = zend_string_init(classname, classname_len, 0); zend_class_entry* found_ce = zend_fetch_class(zs_classname, ZEND_FETCH_CLASS_AUTO | ZEND_FETCH_CLASS_SILENT); zend_string_release(zs_classname); if (!found_ce) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Class %s does not exist", classname); } else if (!PHONGO_IS_CLASS_INSTANTIATABLE(found_ce)) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Class %s is not instantiatable", classname); } else if (!instanceof_function(found_ce, interface_ce)) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "Class %s does not implement %s", classname, ZSTR_VAL(interface_ce->name)); } else { return found_ce; } return NULL; } /* }}} */ /* Parses a BSON type (i.e. array, document, or root). On success, the type and * type_ce output arguments will be assigned and true will be returned; * otherwise, false is returned and an exception is thrown. */ static bool php_phongo_bson_state_parse_type(zval* options, const char* name, php_phongo_bson_typemap_types* type, zend_class_entry** type_ce) /* {{{ */ { char* classname; int classname_len; zend_bool classname_free = 0; bool retval = true; classname = php_array_fetch_string(options, name, &classname_len, &classname_free); if (!classname_len) { goto cleanup; } if (!strcasecmp(classname, "array")) { *type = PHONGO_TYPEMAP_NATIVE_ARRAY; *type_ce = NULL; } else if (!strcasecmp(classname, "stdclass") || !strcasecmp(classname, "object")) { *type = PHONGO_TYPEMAP_NATIVE_OBJECT; *type_ce = NULL; } else { if ((*type_ce = php_phongo_bson_state_fetch_class(classname, classname_len, php_phongo_unserializable_ce))) { *type = PHONGO_TYPEMAP_CLASS; } else { retval = false; } } cleanup: if (classname_free) { efree(classname); } return retval; } /* }}} */ static void field_path_map_element_set_info(php_phongo_field_path_map_element* element, php_phongo_bson_typemap_types type, zend_class_entry* ce) { element->node_type = type; element->node_ce = ce; } static void map_add_field_path_element(php_phongo_bson_typemap* map, php_phongo_field_path_map_element* element) { /* Make sure we have allocated enough */ if (map->field_paths.allocated_size < map->field_paths.size + 1) { map->field_paths.allocated_size += PHONGO_FIELD_PATH_EXPANSION; map->field_paths.map = erealloc(map->field_paths.map, sizeof(php_phongo_field_path_map_element) * map->field_paths.allocated_size); } map->field_paths.map[map->field_paths.size] = element; map->field_paths.size++; } static php_phongo_field_path_map_element* field_path_map_element_alloc(void) { php_phongo_field_path_map_element* tmp = ecalloc(1, sizeof(php_phongo_field_path_map_element)); tmp->entry = php_phongo_field_path_alloc(true); return tmp; } static void field_path_map_element_dtor(php_phongo_field_path_map_element* element) { php_phongo_field_path_free(element->entry); efree(element); } bool php_phongo_bson_state_add_field_path(php_phongo_bson_typemap* map, char* field_path_original, php_phongo_bson_typemap_types type, zend_class_entry* ce) { char* ptr = NULL; char* segment_end = NULL; php_phongo_field_path_map_element* field_path_map_element; if (field_path_original[0] == '.') { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "A 'fieldPaths' key may not start with a '.'"); return false; } if (field_path_original[strlen(field_path_original) - 1] == '.') { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "A 'fieldPaths' key may not end with a '.'"); return false; } field_path_map_element = field_path_map_element_alloc(); ptr = field_path_original; /* Loop over all the segments. A segment is delimited by a "." */ while ((segment_end = strchr(ptr, '.')) != NULL) { char* tmp = NULL; /* Bail out if we have an empty segment */ if (ptr == segment_end) { field_path_map_element_dtor(field_path_map_element); phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "A 'fieldPaths' key may not have an empty segment"); return false; } tmp = calloc(1, segment_end - ptr + 1); memcpy(tmp, ptr, segment_end - ptr); php_phongo_field_path_push(field_path_map_element->entry, tmp, PHONGO_FIELD_PATH_ITEM_NONE); free(tmp); ptr = segment_end + 1; } /* Add the last (or single) element */ php_phongo_field_path_push(field_path_map_element->entry, ptr, PHONGO_FIELD_PATH_ITEM_NONE); field_path_map_element_set_info(field_path_map_element, type, ce); map_add_field_path_element(map, field_path_map_element); return true; } void php_phongo_bson_typemap_dtor(php_phongo_bson_typemap* map) { size_t i; if (map->field_paths.map) { for (i = 0; i < map->field_paths.size; i++) { field_path_map_element_dtor(map->field_paths.map[i]); } efree(map->field_paths.map); } map->field_paths.map = NULL; } /* Loops over each element in the fieldPaths array (if exists, and is an * array), and then checks whether each element is a valid type mapping */ bool php_phongo_bson_state_parse_fieldpaths(zval* typemap, php_phongo_bson_typemap* map) /* {{{ */ { zval* fieldpaths = NULL; HashTable* ht_data; if (!php_array_existsc(typemap, "fieldPaths")) { return true; } fieldpaths = php_array_fetchc_array(typemap, "fieldPaths"); if (!fieldpaths) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "The 'fieldPaths' element is not an array"); return false; } ht_data = HASH_OF(fieldpaths); { zend_string* string_key = NULL; zend_ulong num_key = 0; zval* property; ZEND_HASH_FOREACH_KEY_VAL(ht_data, num_key, string_key, property) { zend_class_entry* map_ce = NULL; php_phongo_bson_typemap_types map_type; if (!string_key) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "The 'fieldPaths' element is not an associative array"); return false; } if (strcmp(ZSTR_VAL(string_key), "") == 0) { phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT, "The 'fieldPaths' element may not be an empty string"); return false; } if (!php_phongo_bson_state_parse_type(fieldpaths, ZSTR_VAL(string_key), &map_type, &map_ce)) { return false; } if (!php_phongo_bson_state_add_field_path(map, ZSTR_VAL(string_key), map_type, map_ce)) { return false; } } ZEND_HASH_FOREACH_END(); } return true; } /* }}} */ #if DEBUG static void print_node_info(php_phongo_field_path_node* ptr, int level) { printf("%*sNAME: %s\n", level * 4, "", ptr->name); printf("%*s- type:", level * 4, ""); switch (ptr->node_type) { case PHONGO_TYPEMAP_NONE: printf(" none (unset)\n"); break; case PHONGO_TYPEMAP_CLASS: printf(" class (%s)\n", ZSTR_VAL(ptr->node_ce->name)); break; case PHONGO_TYPEMAP_NATIVE_ARRAY: printf(" array\n"); break; case PHONGO_TYPEMAP_NATIVE_OBJECT: printf(" stdClass\n"); break; } } static void print_map_list(php_phongo_field_path_node* node, int level) { php_phongo_field_path_node* ptr = node->children; if (!ptr) { return; } do { print_node_info(ptr, level); if (ptr->children) { printf("%*s- children:\n", level * 4, ""); print_map_list(ptr, level + 1); } ptr = ptr->next; } while (ptr); } #endif /* Applies the array argument to a typemap struct. Returns true on success; * otherwise, false is returned an an exception is thrown. */ bool php_phongo_bson_typemap_to_state(zval* typemap, php_phongo_bson_typemap* map) /* {{{ */ { if (!typemap) { return true; } if (!php_phongo_bson_state_parse_type(typemap, "array", &map->array_type, &map->array) || !php_phongo_bson_state_parse_type(typemap, "document", &map->document_type, &map->document) || !php_phongo_bson_state_parse_type(typemap, "root", &map->root_type, &map->root) || !php_phongo_bson_state_parse_fieldpaths(typemap, map)) { /* Exception should already have been thrown */ return false; } #if DEBUG print_map_list(&map->field_path_map, 0); #endif return true; } /* }}} */ void php_phongo_bson_new_timestamp_from_increment_and_timestamp(zval* object, uint32_t increment, uint32_t timestamp) /* {{{ */ { php_phongo_timestamp_t* intern; object_init_ex(object, php_phongo_timestamp_ce); intern = Z_TIMESTAMP_OBJ_P(object); intern->increment = increment; intern->timestamp = timestamp; intern->initialized = true; } /* }}} */ void php_phongo_bson_new_int64(zval* object, int64_t integer) /* {{{ */ { php_phongo_int64_t* intern; object_init_ex(object, php_phongo_int64_ce); intern = Z_INT64_OBJ_P(object); intern->integer = integer; intern->initialized = true; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */