mirror of
https://github.com/macintoshplus/mongo-php-driver.git
synced 2026-04-23 16:38:08 +02:00
b0b1fd1b94
* Avoid re-parsing bson when converting to zval A lot of occurrences start out with a bson_t*, then call bson_get_data on it to pass it to php_phongo_bson_to_zval_ex. This however creates a new bson reader and creates a new bson_t* from the given data. This is unnecessary and we should pass the original bson_t* in these instances. * Refactor typemap struct * Extract object initialisation from document visitor * Refactor document and array visitors * Fix wrong signature in array visitor declaration * Rename ce member for consistency * Add exception thrown comment * Fix clang-format
219 lines
6.7 KiB
C
219 lines
6.7 KiB
C
/*
|
|
* Copyright 2022-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 "mongoc/mongoc.h"
|
|
|
|
#include <php.h>
|
|
#include <Zend/zend_exceptions.h>
|
|
|
|
#include "php_phongo.h"
|
|
#include "phongo_error.h"
|
|
|
|
/* This constant is used for determining if a server error for an exceeded query
|
|
* or command should select ExecutionTimeoutException. */
|
|
#define PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT 50
|
|
|
|
void phongo_add_exception_prop(const char* prop, int prop_len, zval* value)
|
|
{
|
|
if (EG(exception)) {
|
|
zval ex;
|
|
ZVAL_OBJ(&ex, EG(exception));
|
|
zend_update_property(Z_OBJCE(ex), PHONGO_COMPAT_OBJ_P(&ex), prop, prop_len, value);
|
|
}
|
|
}
|
|
|
|
zend_class_entry* phongo_exception_from_phongo_domain(php_phongo_error_domain_t domain)
|
|
{
|
|
switch (domain) {
|
|
case PHONGO_ERROR_INVALID_ARGUMENT:
|
|
return php_phongo_invalidargumentexception_ce;
|
|
case PHONGO_ERROR_LOGIC:
|
|
return php_phongo_logicexception_ce;
|
|
case PHONGO_ERROR_RUNTIME:
|
|
return php_phongo_runtimeexception_ce;
|
|
case PHONGO_ERROR_UNEXPECTED_VALUE:
|
|
return php_phongo_unexpectedvalueexception_ce;
|
|
case PHONGO_ERROR_MONGOC_FAILED:
|
|
return php_phongo_runtimeexception_ce;
|
|
case PHONGO_ERROR_CONNECTION_FAILED:
|
|
return php_phongo_connectionexception_ce;
|
|
}
|
|
|
|
MONGOC_ERROR("Resolving unknown phongo error domain: %d", domain);
|
|
return php_phongo_runtimeexception_ce;
|
|
}
|
|
|
|
zend_class_entry* phongo_exception_from_mongoc_domain(mongoc_error_domain_t domain, mongoc_error_code_t code)
|
|
{
|
|
if (domain == MONGOC_ERROR_CLIENT) {
|
|
if (code == MONGOC_ERROR_CLIENT_AUTHENTICATE) {
|
|
return php_phongo_authenticationexception_ce;
|
|
}
|
|
|
|
if (code == MONGOC_ERROR_CLIENT_INVALID_ENCRYPTION_ARG) {
|
|
return php_phongo_invalidargumentexception_ce;
|
|
}
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_COMMAND && code == MONGOC_ERROR_COMMAND_INVALID_ARG) {
|
|
return php_phongo_invalidargumentexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_SERVER) {
|
|
if (code == PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT) {
|
|
return php_phongo_executiontimeoutexception_ce;
|
|
}
|
|
|
|
return php_phongo_serverexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_SERVER_SELECTION && code == MONGOC_ERROR_SERVER_SELECTION_FAILURE) {
|
|
return php_phongo_connectiontimeoutexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_STREAM) {
|
|
if (code == MONGOC_ERROR_STREAM_SOCKET) {
|
|
return php_phongo_connectiontimeoutexception_ce;
|
|
}
|
|
|
|
return php_phongo_connectionexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_WRITE_CONCERN) {
|
|
return php_phongo_serverexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_PROTOCOL && code == MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION) {
|
|
return php_phongo_connectionexception_ce;
|
|
}
|
|
|
|
if (domain == MONGOC_ERROR_CLIENT_SIDE_ENCRYPTION) {
|
|
return php_phongo_encryptionexception_ce;
|
|
}
|
|
|
|
return php_phongo_runtimeexception_ce;
|
|
}
|
|
|
|
void phongo_throw_exception(php_phongo_error_domain_t domain, const char* format, ...)
|
|
{
|
|
va_list args;
|
|
char* message;
|
|
int message_len;
|
|
|
|
va_start(args, format);
|
|
message_len = vspprintf(&message, 0, format, args);
|
|
zend_throw_exception(phongo_exception_from_phongo_domain(domain), message, 0);
|
|
efree(message);
|
|
va_end(args);
|
|
}
|
|
|
|
static int phongo_exception_append_error_labels(zval* labels, const bson_iter_t* iter)
|
|
{
|
|
bson_iter_t error_labels;
|
|
uint32_t label_count = 0;
|
|
|
|
if (!BSON_ITER_HOLDS_ARRAY(iter) || !bson_iter_recurse(iter, &error_labels)) {
|
|
return label_count;
|
|
}
|
|
|
|
while (bson_iter_next(&error_labels)) {
|
|
if (BSON_ITER_HOLDS_UTF8(&error_labels)) {
|
|
const char* error_label;
|
|
uint32_t error_label_len;
|
|
|
|
error_label = bson_iter_utf8(&error_labels, &error_label_len);
|
|
ADD_NEXT_INDEX_STRINGL(labels, error_label, error_label_len);
|
|
label_count++;
|
|
}
|
|
}
|
|
|
|
return label_count;
|
|
}
|
|
|
|
void phongo_exception_add_error_labels(const bson_t* reply)
|
|
{
|
|
bson_iter_t iter, child;
|
|
zval labels;
|
|
uint32_t label_count = 0;
|
|
|
|
if (!reply) {
|
|
return;
|
|
}
|
|
|
|
array_init(&labels);
|
|
|
|
if (bson_iter_init_find(&iter, reply, "errorLabels")) {
|
|
label_count += phongo_exception_append_error_labels(&labels, &iter);
|
|
}
|
|
|
|
if (bson_iter_init_find(&iter, reply, "writeConcernError") && BSON_ITER_HOLDS_DOCUMENT(&iter) &&
|
|
bson_iter_recurse(&iter, &child) && bson_iter_find(&child, "errorLabels")) {
|
|
label_count += phongo_exception_append_error_labels(&labels, &child);
|
|
}
|
|
|
|
/* mongoc_write_result_t always reports writeConcernErrors in an array, so
|
|
* we must iterate this to collect WCE labels for BulkWrite replies. */
|
|
if (bson_iter_init_find(&iter, reply, "writeConcernErrors") && BSON_ITER_HOLDS_ARRAY(&iter) && bson_iter_recurse(&iter, &child)) {
|
|
bson_iter_t wce;
|
|
|
|
while (bson_iter_next(&child)) {
|
|
if (BSON_ITER_HOLDS_DOCUMENT(&child) && bson_iter_recurse(&child, &wce) && bson_iter_find(&wce, "errorLabels")) {
|
|
label_count += phongo_exception_append_error_labels(&labels, &wce);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (label_count > 0) {
|
|
phongo_add_exception_prop(ZEND_STRL("errorLabels"), &labels);
|
|
}
|
|
|
|
zval_ptr_dtor(&labels);
|
|
}
|
|
|
|
void phongo_throw_exception_from_bson_error_t_and_reply(bson_error_t* error, const bson_t* reply)
|
|
{
|
|
/* Server errors (other than ExceededTimeLimit) and write concern errors
|
|
* may use CommandException and report the result document for the
|
|
* failed command. For BC, ExceededTimeLimit errors will continue to use
|
|
* ExcecutionTimeoutException and omit the result document. */
|
|
if (reply && ((error->domain == MONGOC_ERROR_SERVER && error->code != PHONGO_SERVER_ERROR_EXCEEDED_TIME_LIMIT) || error->domain == MONGOC_ERROR_WRITE_CONCERN)) {
|
|
zval zv;
|
|
|
|
zend_throw_exception(php_phongo_commandexception_ce, error->message, error->code);
|
|
if (php_phongo_bson_to_zval(reply, &zv)) {
|
|
phongo_add_exception_prop(ZEND_STRL("resultDocument"), &zv);
|
|
}
|
|
|
|
zval_ptr_dtor(&zv);
|
|
} else {
|
|
zend_throw_exception(phongo_exception_from_mongoc_domain(error->domain, error->code), error->message, error->code);
|
|
}
|
|
phongo_exception_add_error_labels(reply);
|
|
}
|
|
|
|
void phongo_throw_exception_from_bson_error_t(bson_error_t* error)
|
|
{
|
|
phongo_throw_exception_from_bson_error_t_and_reply(error, NULL);
|
|
}
|
|
|
|
#ifndef MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION
|
|
void phongo_throw_exception_no_cse(php_phongo_error_domain_t domain, const char* message)
|
|
{
|
|
phongo_throw_exception(domain, "%s Please recompile with support for libmongocrypt using the with-mongodb-client-side-encryption configure switch.", message);
|
|
}
|
|
#endif /* MONGOC_ENABLE_CLIENT_SIDE_ENCRYPTION */
|