mirror of
https://github.com/macintoshplus/mongo-php-driver.git
synced 2026-03-29 04:12:23 +02:00
This adds common validation for read preference tag sets when specified through either the Manager constructor's URI options array or ReadPreference constructor. An additional test case for a malformed tag set has been added to the Manager::__construct() error test for read preference options. Additionally, the ReadPreference::__construct() error test has been split up to test for mode and tagSet errors separately. Note: we cannot test for the exceptions for bson_init_static() and mongoc_read_prefs_is_valid(), since those points will never be hit in normal operation.
2273 lines
68 KiB
C
2273 lines
68 KiB
C
/*
|
|
+---------------------------------------------------------------------------+
|
|
| PHP Driver for MongoDB |
|
|
+---------------------------------------------------------------------------+
|
|
| Copyright 2013-2015 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. |
|
|
+---------------------------------------------------------------------------+
|
|
| Copyright (c) 2014-2015 MongoDB, Inc. |
|
|
+---------------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
/* External libs */
|
|
#include "bson.h"
|
|
#include "mongoc.h"
|
|
|
|
|
|
/* PHP Core stuff */
|
|
#include <php.h>
|
|
#include <php_ini.h>
|
|
#include <ext/standard/info.h>
|
|
#include <ext/standard/file.h>
|
|
#include <Zend/zend_hash.h>
|
|
#include <Zend/zend_interfaces.h>
|
|
#include <Zend/zend_exceptions.h>
|
|
#include <ext/spl/spl_iterators.h>
|
|
#include <ext/spl/spl_exceptions.h>
|
|
#include <ext/standard/php_var.h>
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
# include <Zend/zend_smart_str.h>
|
|
#else
|
|
# include <ext/standard/php_smart_str.h>
|
|
#endif
|
|
|
|
/* Stream wrapper */
|
|
#include <main/php_streams.h>
|
|
#include <main/php_network.h>
|
|
/* Debug log writing */
|
|
#include <main/php_open_temporary_file.h>
|
|
/* For formating timestamp in the log */
|
|
#include <ext/date/php_date.h>
|
|
/* String manipulation */
|
|
#include <Zend/zend_string.h>
|
|
/* PHP array helpers */
|
|
#include "php_array_api.h"
|
|
|
|
/* Our Compatability header */
|
|
#include "phongo_compat.h"
|
|
|
|
/* Our stuffz */
|
|
#include "php_phongo.h"
|
|
#include "php_bson.h"
|
|
|
|
#undef MONGOC_LOG_DOMAIN
|
|
#define MONGOC_LOG_DOMAIN "PHONGO"
|
|
|
|
#define PHONGO_DEBUG_INI "mongodb.debug"
|
|
#define PHONGO_DEBUG_INI_DEFAULT ""
|
|
|
|
ZEND_DECLARE_MODULE_GLOBALS(mongodb)
|
|
#if PHP_VERSION_ID >= 70000
|
|
#if defined(ZTS) && defined(COMPILE_DL_MONGODB)
|
|
ZEND_TSRMLS_CACHE_DEFINE();
|
|
#endif
|
|
#endif
|
|
|
|
php_phongo_server_description_type_map_t
|
|
php_phongo_server_description_type_map[PHONGO_SERVER_DESCRIPTION_TYPES] = {
|
|
{ PHONGO_SERVER_UNKNOWN, "Unknown" },
|
|
{ PHONGO_SERVER_STANDALONE, "Standalone" },
|
|
{ PHONGO_SERVER_MONGOS, "Mongos" },
|
|
{ PHONGO_SERVER_POSSIBLE_PRIMARY, "PossiblePrimary" },
|
|
{ PHONGO_SERVER_RS_PRIMARY, "RSPrimary" },
|
|
{ PHONGO_SERVER_RS_SECONDARY, "RSSecondary" },
|
|
{ PHONGO_SERVER_RS_ARBITER, "RSArbiter" },
|
|
{ PHONGO_SERVER_RS_OTHER, "RSOther" },
|
|
{ PHONGO_SERVER_RS_GHOST, "RSGhost" },
|
|
};
|
|
|
|
/* {{{ phongo_std_object_handlers */
|
|
zend_object_handlers phongo_std_object_handlers;
|
|
|
|
PHONGO_API zend_object_handlers *phongo_get_std_object_handlers(void)
|
|
{
|
|
return &phongo_std_object_handlers;
|
|
}
|
|
/* }}} */
|
|
|
|
/* Forward declarations */
|
|
static bool phongo_split_namespace(const char *namespace, char **dbname, char **cname);
|
|
|
|
/* {{{ Error reporting and logging */
|
|
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_WRITE_FAILED:
|
|
return php_phongo_bulkwriteexception_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(uint32_t /* mongoc_error_domain_t */ domain, uint32_t /* mongoc_error_code_t */ code)
|
|
{
|
|
switch(code) {
|
|
case 50: /* ExceededTimeLimit */
|
|
return php_phongo_executiontimeoutexception_ce;
|
|
case MONGOC_ERROR_STREAM_SOCKET:
|
|
case MONGOC_ERROR_SERVER_SELECTION_FAILURE:
|
|
return php_phongo_connectiontimeoutexception_ce;
|
|
case MONGOC_ERROR_CLIENT_AUTHENTICATE:
|
|
return php_phongo_authenticationexception_ce;
|
|
|
|
case MONGOC_ERROR_STREAM_INVALID_TYPE:
|
|
case MONGOC_ERROR_STREAM_INVALID_STATE:
|
|
case MONGOC_ERROR_STREAM_NAME_RESOLUTION:
|
|
case MONGOC_ERROR_STREAM_CONNECT:
|
|
case MONGOC_ERROR_STREAM_NOT_ESTABLISHED:
|
|
return php_phongo_connectionexception_ce;
|
|
case MONGOC_ERROR_CLIENT_NOT_READY:
|
|
case MONGOC_ERROR_CLIENT_TOO_BIG:
|
|
case MONGOC_ERROR_CLIENT_TOO_SMALL:
|
|
case MONGOC_ERROR_CLIENT_GETNONCE:
|
|
case MONGOC_ERROR_CLIENT_NO_ACCEPTABLE_PEER:
|
|
case MONGOC_ERROR_CLIENT_IN_EXHAUST:
|
|
case MONGOC_ERROR_PROTOCOL_INVALID_REPLY:
|
|
case MONGOC_ERROR_PROTOCOL_BAD_WIRE_VERSION:
|
|
case MONGOC_ERROR_CURSOR_INVALID_CURSOR:
|
|
case MONGOC_ERROR_QUERY_FAILURE:
|
|
/*case MONGOC_ERROR_PROTOCOL_ERROR:*/
|
|
case MONGOC_ERROR_BSON_INVALID:
|
|
case MONGOC_ERROR_MATCHER_INVALID:
|
|
case MONGOC_ERROR_NAMESPACE_INVALID:
|
|
case MONGOC_ERROR_COMMAND_INVALID_ARG:
|
|
case MONGOC_ERROR_COLLECTION_INSERT_FAILED:
|
|
case MONGOC_ERROR_GRIDFS_INVALID_FILENAME:
|
|
case MONGOC_ERROR_QUERY_COMMAND_NOT_FOUND:
|
|
case MONGOC_ERROR_QUERY_NOT_TAILABLE:
|
|
return php_phongo_runtimeexception_ce;
|
|
}
|
|
switch (domain) {
|
|
case MONGOC_ERROR_CLIENT:
|
|
case MONGOC_ERROR_STREAM:
|
|
case MONGOC_ERROR_PROTOCOL:
|
|
case MONGOC_ERROR_CURSOR:
|
|
case MONGOC_ERROR_QUERY:
|
|
case MONGOC_ERROR_INSERT:
|
|
case MONGOC_ERROR_SASL:
|
|
case MONGOC_ERROR_BSON:
|
|
case MONGOC_ERROR_MATCHER:
|
|
case MONGOC_ERROR_NAMESPACE:
|
|
case MONGOC_ERROR_COMMAND:
|
|
case MONGOC_ERROR_COLLECTION:
|
|
case MONGOC_ERROR_GRIDFS:
|
|
/* FIXME: We don't have the Exceptions mocked yet.. */
|
|
#if 0
|
|
return phongo_ce_mongo_connection_exception;
|
|
#endif
|
|
default:
|
|
return php_phongo_runtimeexception_ce;
|
|
}
|
|
}
|
|
void phongo_throw_exception(php_phongo_error_domain_t domain TSRMLS_DC, 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 TSRMLS_CC);
|
|
efree(message);
|
|
va_end(args);
|
|
}
|
|
void phongo_throw_exception_from_bson_error_t(bson_error_t *error TSRMLS_DC)
|
|
{
|
|
zend_throw_exception(phongo_exception_from_mongoc_domain(error->domain, error->code), error->message, error->code TSRMLS_CC);
|
|
}
|
|
static void php_phongo_log(mongoc_log_level_t log_level, const char *log_domain, const char *message, void *user_data)
|
|
{
|
|
PHONGO_TSRMLS_FETCH_FROM_CTX(user_data);
|
|
(void)user_data;
|
|
|
|
switch(log_level) {
|
|
case MONGOC_LOG_LEVEL_ERROR:
|
|
case MONGOC_LOG_LEVEL_CRITICAL:
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "%s", message);
|
|
return;
|
|
|
|
case MONGOC_LOG_LEVEL_WARNING:
|
|
case MONGOC_LOG_LEVEL_MESSAGE:
|
|
case MONGOC_LOG_LEVEL_INFO:
|
|
case MONGOC_LOG_LEVEL_DEBUG:
|
|
case MONGOC_LOG_LEVEL_TRACE:
|
|
{
|
|
time_t t;
|
|
phongo_char *dt;
|
|
|
|
time(&t);
|
|
dt = php_format_date((char *)"Y-m-d\\TH:i:sP", strlen("Y-m-d\\TH:i:sP"), t, 0 TSRMLS_CC);
|
|
|
|
fprintf(MONGODB_G(debug_fd), "[%s] %10s: %-8s> %s\n", ZSTR_VAL(dt), log_domain, mongoc_log_level_str(log_level), message);
|
|
fflush(MONGODB_G(debug_fd));
|
|
efree(dt);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ Init objects */
|
|
static void phongo_cursor_init(zval *return_value, zval *manager, mongoc_cursor_t *cursor, zval *readPreference TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *intern;
|
|
|
|
object_init_ex(return_value, php_phongo_cursor_ce);
|
|
|
|
intern = Z_CURSOR_OBJ_P(return_value);
|
|
intern->cursor = cursor;
|
|
intern->server_id = mongoc_cursor_get_hint(cursor);
|
|
intern->client = Z_MANAGER_OBJ_P(manager)->client;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_COPY(&intern->manager, manager);
|
|
#else
|
|
Z_ADDREF_P(manager);
|
|
intern->manager = manager;
|
|
#endif
|
|
|
|
if (readPreference) {
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_ZVAL(&intern->read_preference, readPreference, 1, 0);
|
|
#else
|
|
Z_ADDREF_P(readPreference);
|
|
intern->read_preference = readPreference;
|
|
#endif
|
|
}
|
|
} /* }}} */
|
|
|
|
static void phongo_cursor_init_for_command(zval *return_value, zval *manager, mongoc_cursor_t *cursor, const char *db, zval *command, zval *readPreference TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *intern;
|
|
|
|
phongo_cursor_init(return_value, manager, cursor, readPreference TSRMLS_CC);
|
|
intern = Z_CURSOR_OBJ_P(return_value);
|
|
|
|
intern->database = estrdup(db);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_ZVAL(&intern->command, command, 1, 0);
|
|
#else
|
|
Z_ADDREF_P(command);
|
|
intern->command = command;
|
|
#endif
|
|
} /* }}} */
|
|
|
|
static void phongo_cursor_init_for_query(zval *return_value, zval *manager, mongoc_cursor_t *cursor, const char *namespace, zval *query, zval *readPreference TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *intern;
|
|
|
|
phongo_cursor_init(return_value, manager, cursor, readPreference TSRMLS_CC);
|
|
intern = Z_CURSOR_OBJ_P(return_value);
|
|
|
|
/* namespace has already been validated by phongo_execute_query() */
|
|
phongo_split_namespace(namespace, &intern->database, &intern->collection);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_ZVAL(&intern->query, query, 1, 0);
|
|
#else
|
|
Z_ADDREF_P(query);
|
|
intern->query = query;
|
|
#endif
|
|
} /* }}} */
|
|
|
|
void phongo_server_init(zval *return_value, zval *manager, int server_id TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_server_t *server;
|
|
|
|
object_init_ex(return_value, php_phongo_server_ce);
|
|
|
|
server = Z_SERVER_OBJ_P(return_value);
|
|
server->server_id = server_id;
|
|
server->client = Z_MANAGER_OBJ_P(manager)->client;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_COPY(&server->manager, manager);
|
|
#else
|
|
Z_ADDREF_P(manager);
|
|
server->manager = manager;
|
|
#endif
|
|
}
|
|
/* }}} */
|
|
|
|
void phongo_readconcern_init(zval *return_value, const mongoc_read_concern_t *read_concern TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_readconcern_t *intern;
|
|
|
|
object_init_ex(return_value, php_phongo_readconcern_ce);
|
|
|
|
intern = Z_READCONCERN_OBJ_P(return_value);
|
|
intern->read_concern = mongoc_read_concern_copy(read_concern);
|
|
}
|
|
/* }}} */
|
|
|
|
void phongo_readpreference_init(zval *return_value, const mongoc_read_prefs_t *read_prefs TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_readpreference_t *intern;
|
|
|
|
object_init_ex(return_value, php_phongo_readpreference_ce);
|
|
|
|
intern = Z_READPREFERENCE_OBJ_P(return_value);
|
|
intern->read_preference = mongoc_read_prefs_copy(read_prefs);
|
|
}
|
|
/* }}} */
|
|
|
|
void phongo_writeconcern_init(zval *return_value, const mongoc_write_concern_t *write_concern TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_writeconcern_t *intern;
|
|
|
|
object_init_ex(return_value, php_phongo_writeconcern_ce);
|
|
|
|
intern = Z_WRITECONCERN_OBJ_P(return_value);
|
|
intern->write_concern = mongoc_write_concern_copy(write_concern);
|
|
}
|
|
/* }}} */
|
|
|
|
int32_t phongo_bson_find_as_int32(bson_t *bson, const char *key, int32_t fallback)
|
|
{
|
|
bson_iter_t iter;
|
|
|
|
if (bson_iter_init_find(&iter, bson, key) && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
return bson_iter_int32(&iter);
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
bool phongo_bson_find_as_bool(bson_t *bson, const char *key, bool fallback)
|
|
{
|
|
bson_iter_t iter;
|
|
|
|
if (bson_iter_init_find(&iter, bson, key) && BSON_ITER_HOLDS_BOOL(&iter)) {
|
|
return bson_iter_bool(&iter);
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
void phongo_bson_iter_as_document(const bson_iter_t *iter, uint32_t *document_len, const uint8_t **document)
|
|
{
|
|
*document = NULL;
|
|
*document_len = 0;
|
|
|
|
if (BSON_ITER_HOLDS_DOCUMENT(iter) || BSON_ITER_HOLDS_ARRAY(iter)) {
|
|
memcpy (document_len, (iter->raw + iter->d1), sizeof (*document_len));
|
|
*document_len = BSON_UINT32_FROM_LE (*document_len);
|
|
*document = (iter->raw + iter->d1);
|
|
}
|
|
}
|
|
|
|
bool phongo_query_init(php_phongo_query_t *query, bson_t *filter, bson_t *options TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
|
|
if (options) {
|
|
query->batch_size = phongo_bson_find_as_int32(options, "batchSize", 0);
|
|
query->limit = phongo_bson_find_as_int32(options, "limit", 0);
|
|
query->skip = phongo_bson_find_as_int32(options, "skip", 0);
|
|
|
|
query->flags = 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "tailable", false) ? MONGOC_QUERY_TAILABLE_CURSOR : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "slaveOk", false) ? MONGOC_QUERY_SLAVE_OK : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "oplogReplay", false) ? MONGOC_QUERY_OPLOG_REPLAY : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "noCursorTimeout", false) ? MONGOC_QUERY_NO_CURSOR_TIMEOUT : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "awaitData", false) ? MONGOC_QUERY_AWAIT_DATA : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "exhaust", false) ? MONGOC_QUERY_EXHAUST : 0;
|
|
query->flags |= phongo_bson_find_as_bool(options, "partial", false) ? MONGOC_QUERY_PARTIAL : 0;
|
|
|
|
|
|
if (bson_iter_init_find(&iter, options, "modifiers")) {
|
|
uint32_t len = 0;
|
|
const uint8_t *data = NULL;
|
|
|
|
if (! (BSON_ITER_HOLDS_DOCUMENT (&iter) || BSON_ITER_HOLDS_ARRAY (&iter))) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected modifiers to be array or object, %d given", bson_iter_type(&iter));
|
|
return false;
|
|
}
|
|
|
|
bson_iter_document(&iter, &len, &data);
|
|
if (len) {
|
|
bson_t tmp;
|
|
|
|
bson_init_static(&tmp, data, len);
|
|
bson_copy_to_excluding_noinit(&tmp, query->query, "not-used-value", NULL);
|
|
bson_destroy (&tmp);
|
|
}
|
|
}
|
|
|
|
if (bson_iter_init_find(&iter, options, "projection")) {
|
|
uint32_t len = 0;
|
|
const uint8_t *data = NULL;
|
|
|
|
if (! (BSON_ITER_HOLDS_DOCUMENT (&iter) || BSON_ITER_HOLDS_ARRAY (&iter))) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected projection to be array or object, %d given", bson_iter_type(&iter));
|
|
return false;
|
|
}
|
|
|
|
bson_iter_document(&iter, &len, &data);
|
|
if (len) {
|
|
query->selector = bson_new_from_data(data, len);
|
|
}
|
|
}
|
|
|
|
if (bson_iter_init_find(&iter, options, "sort")) {
|
|
uint32_t len = 0;
|
|
const uint8_t *data = NULL;
|
|
|
|
if (! (BSON_ITER_HOLDS_DOCUMENT (&iter) || BSON_ITER_HOLDS_ARRAY (&iter))) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Expected sort to be array or object, %d given", bson_iter_type(&iter));
|
|
return false;
|
|
}
|
|
|
|
phongo_bson_iter_as_document(&iter, &len, &data);
|
|
if (len) {
|
|
bson_t tmp;
|
|
|
|
bson_init_static(&tmp, data, len);
|
|
bson_append_document(query->query, "$orderby", -1, &tmp);
|
|
bson_destroy(&tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
BSON_APPEND_DOCUMENT(query->query, "$query", filter);
|
|
return true;
|
|
} /* }}} */
|
|
|
|
zend_bool phongo_writeconcernerror_init(zval *return_value, bson_t *bson TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
php_phongo_writeconcernerror_t *writeconcernerror;
|
|
|
|
writeconcernerror = Z_WRITECONCERNERROR_OBJ_P(return_value);
|
|
|
|
if (bson_iter_init_find(&iter, bson, "code") && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
writeconcernerror->code = bson_iter_int32(&iter);
|
|
}
|
|
if (bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
uint32_t errmsg_len;
|
|
const char *err_msg = bson_iter_utf8(&iter, &errmsg_len);
|
|
|
|
writeconcernerror->message = estrndup(err_msg, errmsg_len);
|
|
}
|
|
if (bson_iter_init_find(&iter, bson, "errInfo") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
|
|
uint32_t len;
|
|
const uint8_t *data;
|
|
|
|
bson_iter_document(&iter, &len, &data);
|
|
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
|
|
if (!phongo_bson_to_zval(data, len, &writeconcernerror->info)) {
|
|
zval_ptr_dtor(&writeconcernerror->info);
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_UNDEF(&writeconcernerror->info);
|
|
#else
|
|
writeconcernerror->info = NULL;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
zend_bool phongo_writeerror_init(zval *return_value, bson_t *bson TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
php_phongo_writeerror_t *writeerror;
|
|
|
|
writeerror = Z_WRITEERROR_OBJ_P(return_value);
|
|
|
|
if (bson_iter_init_find(&iter, bson, "code") && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
writeerror->code = bson_iter_int32(&iter);
|
|
}
|
|
if (bson_iter_init_find(&iter, bson, "errmsg") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
uint32_t errmsg_len;
|
|
const char *err_msg = bson_iter_utf8(&iter, &errmsg_len);
|
|
|
|
writeerror->message = estrndup(err_msg, errmsg_len);
|
|
}
|
|
if (bson_iter_init_find(&iter, bson, "errInfo")) {
|
|
bson_t info;
|
|
|
|
bson_init(&info);
|
|
bson_append_iter(&info, NULL, 0, &iter);
|
|
|
|
if (!phongo_bson_to_zval(bson_get_data(&info), info.len, &writeerror->info)) {
|
|
zval_ptr_dtor(&writeerror->info);
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_UNDEF(&writeerror->info);
|
|
#else
|
|
writeerror->info = NULL;
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
}
|
|
if (bson_iter_init_find(&iter, bson, "index") && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
writeerror->index = bson_iter_int32(&iter);
|
|
}
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
php_phongo_writeresult_t *phongo_writeresult_init(zval *return_value, bson_t *reply, zval *manager, int server_id TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_writeresult_t *writeresult;
|
|
|
|
object_init_ex(return_value, php_phongo_writeresult_ce);
|
|
|
|
writeresult = Z_WRITERESULT_OBJ_P(return_value);
|
|
writeresult->reply = bson_copy(reply);
|
|
writeresult->server_id = server_id;
|
|
writeresult->client = Z_MANAGER_OBJ_P(manager)->client;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_COPY(&writeresult->manager, manager);
|
|
#else
|
|
Z_ADDREF_P(manager);
|
|
writeresult->manager = manager;
|
|
#endif
|
|
|
|
return writeresult;
|
|
} /* }}} */
|
|
/* }}} */
|
|
|
|
/* {{{ CRUD */
|
|
/* Splits a namespace name into the database and collection names, allocated with estrdup. */
|
|
static bool phongo_split_namespace(const char *namespace, char **dbname, char **cname) /* {{{ */
|
|
{
|
|
char *dot = strchr(namespace, '.');
|
|
|
|
if (!dot) {
|
|
return false;
|
|
}
|
|
|
|
if (cname) {
|
|
*cname = estrdup(namespace + (dot - namespace) + 1);
|
|
}
|
|
if (dbname) {
|
|
*dbname = estrndup(namespace, dot - namespace);
|
|
}
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
mongoc_bulk_operation_t *phongo_bulkwrite_init(zend_bool ordered) { /* {{{ */
|
|
return mongoc_bulk_operation_new(ordered);
|
|
} /* }}} */
|
|
|
|
bool phongo_execute_write(zval *manager, const char *namespace, php_phongo_bulkwrite_t *bulk_write, const mongoc_write_concern_t *write_concern, int server_id, zval *return_value, int return_value_used TSRMLS_DC) /* {{{ */
|
|
{
|
|
mongoc_client_t *client;
|
|
bson_error_t error;
|
|
int success;
|
|
bson_t reply = BSON_INITIALIZER;
|
|
mongoc_bulk_operation_t *bulk = bulk_write->bulk;
|
|
php_phongo_writeresult_t *writeresult;
|
|
|
|
client = Z_MANAGER_OBJ_P(manager)->client;
|
|
|
|
/* Since BulkWrite objects can currently be executed multiple times, ensure
|
|
* that the database and collection name are freed before we overwrite them.
|
|
* This may be removed once PHPC-676 is implemented. */
|
|
if (bulk_write->database) {
|
|
efree(bulk_write->database);
|
|
}
|
|
|
|
if (bulk_write->collection) {
|
|
efree(bulk_write->collection);
|
|
}
|
|
|
|
if (!phongo_split_namespace(namespace, &bulk_write->database, &bulk_write->collection)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s: %s", "Invalid namespace provided", namespace);
|
|
return false;
|
|
}
|
|
|
|
mongoc_bulk_operation_set_database(bulk, bulk_write->database);
|
|
mongoc_bulk_operation_set_collection(bulk, bulk_write->collection);
|
|
mongoc_bulk_operation_set_client(bulk, client);
|
|
|
|
/* If a write concern was not specified, libmongoc will use the client's
|
|
* write concern; however, we should still fetch it for the write result. */
|
|
if (write_concern) {
|
|
mongoc_bulk_operation_set_write_concern(bulk, write_concern);
|
|
} else {
|
|
write_concern = mongoc_client_get_write_concern(client);
|
|
}
|
|
|
|
if (server_id > 0) {
|
|
mongoc_bulk_operation_set_hint(bulk, server_id);
|
|
}
|
|
|
|
success = mongoc_bulk_operation_execute(bulk, &reply, &error);
|
|
bulk_write->executed = true;
|
|
|
|
/* Write succeeded and the user doesn't care for the results */
|
|
if (success && !return_value_used) {
|
|
bson_destroy(&reply);
|
|
return true;
|
|
}
|
|
|
|
/* Check for connection related exceptions */
|
|
if (EG(exception)) {
|
|
bson_destroy(&reply);
|
|
return false;
|
|
}
|
|
|
|
writeresult = phongo_writeresult_init(return_value, &reply, manager, mongoc_bulk_operation_get_hint(bulk) TSRMLS_CC);
|
|
writeresult->write_concern = mongoc_write_concern_copy(write_concern);
|
|
|
|
/* The Write failed */
|
|
if (!success) {
|
|
if (error.domain == MONGOC_ERROR_COMMAND || error.domain == MONGOC_ERROR_WRITE_CONCERN) {
|
|
phongo_throw_exception(PHONGO_ERROR_WRITE_FAILED TSRMLS_CC, "%s", error.message);
|
|
phongo_add_exception_prop(ZEND_STRL("writeResult"), return_value TSRMLS_CC);
|
|
} else {
|
|
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
|
|
}
|
|
}
|
|
|
|
bson_destroy(&reply);
|
|
|
|
return success;
|
|
} /* }}} */
|
|
|
|
/* Advance the cursor and return whether there is an error. On error, the cursor
|
|
* will be destroyed and an exception will be thrown. */
|
|
static bool phongo_advance_cursor_and_check_for_error(mongoc_cursor_t *cursor TSRMLS_DC)
|
|
{
|
|
const bson_t *doc;
|
|
|
|
if (!mongoc_cursor_next(cursor, &doc)) {
|
|
bson_error_t error;
|
|
|
|
/* Check for connection related exceptions */
|
|
if (EG(exception)) {
|
|
mongoc_cursor_destroy(cursor);
|
|
return false;
|
|
}
|
|
|
|
/* Could simply be no docs, which is not an error */
|
|
if (mongoc_cursor_error(cursor, &error)) {
|
|
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
|
|
mongoc_cursor_destroy(cursor);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
int phongo_execute_query(zval *manager, const char *namespace, zval *zquery, zval *zreadPreference, int server_id, zval *return_value, int return_value_used TSRMLS_DC) /* {{{ */
|
|
{
|
|
mongoc_client_t *client;
|
|
const php_phongo_query_t *query;
|
|
mongoc_cursor_t *cursor;
|
|
char *dbname;
|
|
char *collname;
|
|
mongoc_collection_t *collection;
|
|
|
|
client = Z_MANAGER_OBJ_P(manager)->client;
|
|
|
|
if (!phongo_split_namespace(namespace, &dbname, &collname)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "%s: %s", "Invalid namespace provided", namespace);
|
|
return false;
|
|
}
|
|
collection = mongoc_client_get_collection(client, dbname, collname);
|
|
efree(dbname);
|
|
efree(collname);
|
|
|
|
query = Z_QUERY_OBJ_P(zquery);
|
|
|
|
if (query->read_concern) {
|
|
mongoc_collection_set_read_concern(collection, query->read_concern);
|
|
}
|
|
|
|
cursor = mongoc_collection_find(collection, query->flags, query->skip, query->limit, query->batch_size, query->query, query->selector, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC));
|
|
mongoc_collection_destroy(collection);
|
|
|
|
/* mongoc issues a warning we need to catch somehow */
|
|
if (!cursor) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "%s", "FIXME: Couldn't create cursor...");
|
|
return false;
|
|
}
|
|
|
|
if (server_id > 0 && !mongoc_cursor_set_hint(cursor, server_id)) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "%s", "Could not set cursor server_id");
|
|
return false;
|
|
}
|
|
|
|
if (!phongo_advance_cursor_and_check_for_error(cursor TSRMLS_CC)) {
|
|
return false;
|
|
}
|
|
|
|
if (!return_value_used) {
|
|
mongoc_cursor_destroy(cursor);
|
|
return true;
|
|
}
|
|
|
|
phongo_cursor_init_for_query(return_value, manager, cursor, namespace, zquery, zreadPreference TSRMLS_CC);
|
|
return true;
|
|
} /* }}} */
|
|
|
|
int phongo_execute_command(zval *manager, const char *db, zval *zcommand, zval *zreadPreference, int server_id, zval *return_value, int return_value_used TSRMLS_DC) /* {{{ */
|
|
{
|
|
mongoc_client_t *client;
|
|
const php_phongo_command_t *command;
|
|
mongoc_cursor_t *cursor;
|
|
bson_iter_t iter;
|
|
|
|
client = Z_MANAGER_OBJ_P(manager)->client;
|
|
command = Z_COMMAND_OBJ_P(zcommand);
|
|
|
|
cursor = mongoc_client_command(client, db, MONGOC_QUERY_NONE, 0, 1, 0, command->bson, NULL, phongo_read_preference_from_zval(zreadPreference TSRMLS_CC));
|
|
|
|
if (server_id > 0 && !mongoc_cursor_set_hint(cursor, server_id)) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "%s", "Could not set cursor server_id");
|
|
return false;
|
|
}
|
|
|
|
if (!phongo_advance_cursor_and_check_for_error(cursor TSRMLS_CC)) {
|
|
return false;
|
|
}
|
|
|
|
if (!return_value_used) {
|
|
mongoc_cursor_destroy(cursor);
|
|
return true;
|
|
}
|
|
|
|
if (bson_iter_init_find(&iter, mongoc_cursor_current(cursor), "cursor") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
|
|
mongoc_cursor_t *cmd_cursor;
|
|
|
|
/* According to mongoc_cursor_new_from_command_reply(), the reply bson_t
|
|
* is ultimately destroyed on both success and failure. Use bson_copy()
|
|
* to create a writable copy of the const bson_t we fetched above. */
|
|
cmd_cursor = mongoc_cursor_new_from_command_reply(client, bson_copy(mongoc_cursor_current(cursor)), mongoc_cursor_get_hint(cursor));
|
|
mongoc_cursor_destroy(cursor);
|
|
|
|
if (!phongo_advance_cursor_and_check_for_error(cmd_cursor TSRMLS_CC)) {
|
|
return false;
|
|
}
|
|
|
|
phongo_cursor_init_for_command(return_value, manager, cmd_cursor, db, zcommand, zreadPreference TSRMLS_CC);
|
|
return true;
|
|
}
|
|
|
|
phongo_cursor_init_for_command(return_value, manager, cursor, db, zcommand, zreadPreference TSRMLS_CC);
|
|
return true;
|
|
} /* }}} */
|
|
|
|
/* }}} */
|
|
|
|
/* {{{ mongoc types from from_zval */
|
|
const mongoc_write_concern_t* phongo_write_concern_from_zval(zval *zwrite_concern TSRMLS_DC) /* {{{ */
|
|
{
|
|
if (zwrite_concern) {
|
|
php_phongo_writeconcern_t *intern = Z_WRITECONCERN_OBJ_P(zwrite_concern);
|
|
|
|
if (intern) {
|
|
return intern->write_concern;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
} /* }}} */
|
|
|
|
const mongoc_read_concern_t* phongo_read_concern_from_zval(zval *zread_concern TSRMLS_DC) /* {{{ */
|
|
{
|
|
if (zread_concern) {
|
|
php_phongo_readconcern_t *intern = Z_READCONCERN_OBJ_P(zread_concern);
|
|
|
|
if (intern) {
|
|
return intern->read_concern;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
} /* }}} */
|
|
|
|
const mongoc_read_prefs_t* phongo_read_preference_from_zval(zval *zread_preference TSRMLS_DC) /* {{{ */
|
|
{
|
|
if (zread_preference) {
|
|
php_phongo_readpreference_t *intern = Z_READPREFERENCE_OBJ_P(zread_preference);
|
|
|
|
if (intern) {
|
|
return intern->read_preference;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
} /* }}} */
|
|
/* }}} */
|
|
|
|
/* {{{ phongo zval from mongoc types */
|
|
void php_phongo_cursor_id_new_from_id(zval *object, int64_t cursorid TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursorid_t *intern;
|
|
|
|
object_init_ex(object, php_phongo_cursorid_ce);
|
|
|
|
intern = Z_CURSORID_OBJ_P(object);
|
|
intern->id = cursorid;
|
|
} /* }}} */
|
|
|
|
void php_phongo_objectid_new_from_oid(zval *object, const bson_oid_t *oid TSRMLS_DC) /* {{{ */
|
|
{
|
|
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;
|
|
} /* }}} */
|
|
|
|
php_phongo_server_description_type_t php_phongo_server_description_type(mongoc_server_description_t *sd)
|
|
{
|
|
const char* name = mongoc_server_description_type(sd);
|
|
int i;
|
|
|
|
for (i = 0; i < PHONGO_SERVER_DESCRIPTION_TYPES; i++) {
|
|
if (!strcmp(name, php_phongo_server_description_type_map[i].name)) {
|
|
return php_phongo_server_description_type_map[i].type;
|
|
}
|
|
}
|
|
|
|
return PHONGO_SERVER_UNKNOWN;
|
|
}
|
|
|
|
void php_phongo_server_to_zval(zval *retval, mongoc_server_description_t *sd) /* {{{ */
|
|
{
|
|
mongoc_host_list_t *host = mongoc_server_description_host(sd);
|
|
const bson_t *is_master = mongoc_server_description_ismaster(sd);
|
|
bson_iter_t iter;
|
|
|
|
array_init(retval);
|
|
|
|
ADD_ASSOC_STRING(retval, "host", host->host);
|
|
ADD_ASSOC_LONG_EX(retval, "port", host->port);
|
|
ADD_ASSOC_LONG_EX(retval, "type", php_phongo_server_description_type(sd));
|
|
ADD_ASSOC_BOOL_EX(retval, "is_primary", !strcmp(mongoc_server_description_type(sd), php_phongo_server_description_type_map[PHONGO_SERVER_RS_PRIMARY].name));
|
|
ADD_ASSOC_BOOL_EX(retval, "is_secondary", !strcmp(mongoc_server_description_type(sd), php_phongo_server_description_type_map[PHONGO_SERVER_RS_SECONDARY].name));
|
|
ADD_ASSOC_BOOL_EX(retval, "is_arbiter", !strcmp(mongoc_server_description_type(sd), php_phongo_server_description_type_map[PHONGO_SERVER_RS_ARBITER].name));
|
|
ADD_ASSOC_BOOL_EX(retval, "is_hidden", bson_iter_init_find_case(&iter, is_master, "hidden") && bson_iter_as_bool(&iter));
|
|
ADD_ASSOC_BOOL_EX(retval, "is_passive", bson_iter_init_find_case(&iter, is_master, "passive") && bson_iter_as_bool(&iter));
|
|
|
|
if (bson_iter_init_find(&iter, is_master, "tags") && BSON_ITER_HOLDS_DOCUMENT(&iter)) {
|
|
const uint8_t *bytes;
|
|
uint32_t len;
|
|
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
|
|
|
|
/* Use native arrays for debugging output */
|
|
state.map.root_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
state.map.document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
|
|
bson_iter_document(&iter, &len, &bytes);
|
|
phongo_bson_to_zval_ex(bytes, len, &state);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ADD_ASSOC_ZVAL_EX(retval, "tags", &state.zchild);
|
|
#else
|
|
ADD_ASSOC_ZVAL_EX(retval, "tags", state.zchild);
|
|
#endif
|
|
}
|
|
|
|
{
|
|
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
|
|
/* Use native arrays for debugging output */
|
|
state.map.root_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
state.map.document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
|
|
phongo_bson_to_zval_ex(bson_get_data(is_master), is_master->len, &state);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ADD_ASSOC_ZVAL_EX(retval, "last_is_master", &state.zchild);
|
|
#else
|
|
ADD_ASSOC_ZVAL_EX(retval, "last_is_master", state.zchild);
|
|
#endif
|
|
}
|
|
ADD_ASSOC_LONG_EX(retval, "round_trip_time", (phongo_long) mongoc_server_description_round_trip_time(sd));
|
|
|
|
} /* }}} */
|
|
|
|
void php_phongo_read_concern_to_zval(zval *retval, const mongoc_read_concern_t *read_concern) /* {{{ */
|
|
{
|
|
const char *level = mongoc_read_concern_get_level(read_concern);
|
|
|
|
array_init_size(retval, 1);
|
|
|
|
if (level) {
|
|
ADD_ASSOC_STRING(retval, "level", (char *)level);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(retval, "level");
|
|
}
|
|
} /* }}} */
|
|
|
|
/* Checks if tags is valid to set on a mongoc_read_prefs_t. It may be null or an
|
|
* array of one or more documents. */
|
|
bool php_phongo_read_preference_tags_are_valid(const bson_t *tags) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
|
|
if (bson_empty0(tags)) {
|
|
return true;
|
|
}
|
|
|
|
if (!bson_iter_init(&iter, tags)) {
|
|
return false;
|
|
}
|
|
|
|
while (bson_iter_next(&iter)) {
|
|
if (!BSON_ITER_HOLDS_DOCUMENT(&iter) && !BSON_ITER_HOLDS_ARRAY(&iter)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
void php_phongo_read_preference_to_zval(zval *retval, const mongoc_read_prefs_t *read_prefs) /* {{{ */
|
|
{
|
|
const bson_t *tags = mongoc_read_prefs_get_tags(read_prefs);
|
|
|
|
array_init_size(retval, 2);
|
|
|
|
ADD_ASSOC_LONG_EX(retval, "mode", mongoc_read_prefs_get_mode(read_prefs));
|
|
|
|
if (tags->len) {
|
|
php_phongo_bson_state state = PHONGO_BSON_STATE_INITIALIZER;
|
|
/* Use native arrays for debugging output */
|
|
state.map.root_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
state.map.document_type = PHONGO_TYPEMAP_NATIVE_ARRAY;
|
|
|
|
phongo_bson_to_zval_ex(bson_get_data(tags), tags->len, &state);
|
|
#if PHP_VERSION_ID >= 70000
|
|
ADD_ASSOC_ZVAL_EX(retval, "tags", &state.zchild);
|
|
#else
|
|
ADD_ASSOC_ZVAL_EX(retval, "tags", state.zchild);
|
|
#endif
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(retval, "tags");
|
|
}
|
|
} /* }}} */
|
|
|
|
void php_phongo_write_concern_to_zval(zval *retval, const mongoc_write_concern_t *write_concern) /* {{{ */
|
|
{
|
|
const char *wtag = mongoc_write_concern_get_wtag(write_concern);
|
|
const int32_t w = mongoc_write_concern_get_w(write_concern);
|
|
|
|
array_init_size(retval, 4);
|
|
|
|
if (wtag) {
|
|
ADD_ASSOC_STRING(retval, "w", (char *)wtag);
|
|
} else if (mongoc_write_concern_get_wmajority(write_concern)) {
|
|
ADD_ASSOC_STRING(retval, "w", (char *)PHONGO_WRITE_CONCERN_W_MAJORITY);
|
|
} else if (w != MONGOC_WRITE_CONCERN_W_DEFAULT) {
|
|
ADD_ASSOC_LONG_EX(retval, "w", w);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(retval, "w");
|
|
}
|
|
|
|
ADD_ASSOC_BOOL_EX(retval, "wmajority", mongoc_write_concern_get_wmajority(write_concern));
|
|
ADD_ASSOC_LONG_EX(retval, "wtimeout", mongoc_write_concern_get_wtimeout(write_concern));
|
|
|
|
if (mongoc_write_concern_journal_is_set(write_concern)) {
|
|
ADD_ASSOC_BOOL_EX(retval, "journal", mongoc_write_concern_get_journal(write_concern));
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(retval, "journal");
|
|
}
|
|
} /* }}} */
|
|
/* }}} */
|
|
|
|
|
|
static mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
mongoc_uri_t *uri;
|
|
|
|
uri = mongoc_uri_new(uri_string);
|
|
MONGOC_DEBUG("Connection string: '%s'", uri_string);
|
|
|
|
if (!uri) {
|
|
return NULL;
|
|
}
|
|
|
|
if (options && bson_iter_init(&iter, options)) {
|
|
while (bson_iter_next (&iter)) {
|
|
const char *key = bson_iter_key(&iter);
|
|
|
|
/* Skip read preference and write concern options, as those must be
|
|
* processed after the mongoc_client_t is constructed. */
|
|
if (!strcasecmp(key, "journal") ||
|
|
!strcasecmp(key, "readpreference") ||
|
|
!strcasecmp(key, "readpreferencetags") ||
|
|
!strcasecmp(key, "safe") ||
|
|
!strcasecmp(key, "slaveok") ||
|
|
!strcasecmp(key, "w") ||
|
|
!strcasecmp(key, "wtimeoutms") ||
|
|
!strcasecmp(key, "maxstalenessms") ||
|
|
!strcasecmp(key, "appname")
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
if (mongoc_uri_option_is_bool(key)) {
|
|
mongoc_uri_set_option_as_bool (uri, key, bson_iter_as_bool(&iter));
|
|
}
|
|
else if (mongoc_uri_option_is_int32(key) && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
mongoc_uri_set_option_as_int32 (uri, key, bson_iter_int32 (&iter));
|
|
}
|
|
else if (mongoc_uri_option_is_utf8(key) && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
mongoc_uri_set_option_as_utf8(uri, key, bson_iter_utf8 (&iter, NULL));
|
|
}
|
|
else if (BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
const char *value = bson_iter_utf8 (&iter, NULL);
|
|
|
|
if (!strcasecmp(key, "username")) {
|
|
mongoc_uri_set_username(uri, value);
|
|
} else if (!strcasecmp(key, "password")) {
|
|
mongoc_uri_set_password(uri, value);
|
|
} else if (!strcasecmp(key, "database")) {
|
|
mongoc_uri_set_database(uri, value);
|
|
} else if (!strcasecmp(key, "authsource")) {
|
|
mongoc_uri_set_auth_source(uri, value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return uri;
|
|
} /* }}} */
|
|
|
|
static bool php_phongo_apply_rc_options_to_uri(mongoc_uri_t *uri, bson_t *options TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
mongoc_read_concern_t *new_rc;
|
|
const mongoc_read_concern_t *old_rc;
|
|
|
|
if (!(old_rc = mongoc_uri_get_read_concern(uri))) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "mongoc_uri_t does not have a read concern");
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Return early if there are no options to apply */
|
|
if (bson_empty0(options)) {
|
|
return true;
|
|
}
|
|
|
|
if (!bson_iter_init_find_case(&iter, options, "readconcernlevel")) {
|
|
return true;
|
|
}
|
|
|
|
new_rc = mongoc_read_concern_copy(old_rc);
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "readconcernlevel") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
const char *str = bson_iter_utf8(&iter, NULL);
|
|
|
|
mongoc_read_concern_set_level(new_rc, str);
|
|
}
|
|
|
|
mongoc_uri_set_read_concern(uri, new_rc);
|
|
mongoc_read_concern_destroy(new_rc);
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
static bool php_phongo_apply_rp_options_to_uri(mongoc_uri_t *uri, bson_t *options TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
mongoc_read_prefs_t *new_rp;
|
|
const mongoc_read_prefs_t *old_rp;
|
|
|
|
if (!(old_rp = mongoc_uri_get_read_prefs_t(uri))) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "mongoc_uri_t does not have a read preference");
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Return early if there are no options to apply */
|
|
if (bson_empty0(options)) {
|
|
return true;
|
|
}
|
|
|
|
if (!bson_iter_init_find_case(&iter, options, "slaveok") &&
|
|
!bson_iter_init_find_case(&iter, options, "readpreference") &&
|
|
!bson_iter_init_find_case(&iter, options, "readpreferencetags") &&
|
|
!bson_iter_init_find_case(&iter, options, "maxstalenessms")
|
|
) {
|
|
return true;
|
|
}
|
|
|
|
new_rp = mongoc_read_prefs_copy(old_rp);
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "slaveok") && BSON_ITER_HOLDS_BOOL(&iter)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "readpreference") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
const char *str = bson_iter_utf8(&iter, NULL);
|
|
|
|
if (0 == strcasecmp("primary", str)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY);
|
|
} else if (0 == strcasecmp("primarypreferred", str)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_PRIMARY_PREFERRED);
|
|
} else if (0 == strcasecmp("secondary", str)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY);
|
|
} else if (0 == strcasecmp("secondarypreferred", str)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_SECONDARY_PREFERRED);
|
|
} else if (0 == strcasecmp("nearest", str)) {
|
|
mongoc_read_prefs_set_mode(new_rp, MONGOC_READ_NEAREST);
|
|
} else {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported readPreference value: '%s'", str);
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "readpreferencetags") && BSON_ITER_HOLDS_ARRAY(&iter)) {
|
|
bson_t tags;
|
|
uint32_t len;
|
|
const uint8_t *data;
|
|
|
|
bson_iter_array(&iter, &len, &data);
|
|
|
|
if (!bson_init_static(&tags, data, len)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Could not initialize BSON structure for read preference tags");
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!php_phongo_read_preference_tags_are_valid(&tags)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference tags must be an array of zero or more documents");
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
|
|
mongoc_read_prefs_set_tags(new_rp, &tags);
|
|
}
|
|
|
|
if (mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY &&
|
|
!bson_empty(mongoc_read_prefs_get_tags(new_rp))) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Primary read preference mode conflicts with tags");
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Handle maxStalenessMS, and make sure it is not combined with primary
|
|
* readPreference */
|
|
if (bson_iter_init_find_case(&iter, options, "maxstalenessms") && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
if (mongoc_read_prefs_get_mode(new_rp) == MONGOC_READ_PRIMARY) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Primary read preference mode conflicts with maxStalenessMS");
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
|
|
mongoc_read_prefs_set_max_staleness_ms(new_rp, bson_iter_int32(&iter));
|
|
}
|
|
|
|
/* This may be redundant in light of the last check (primary with tags), but
|
|
* we'll check anyway in case additional validation is implemented. */
|
|
if (!mongoc_read_prefs_is_valid(new_rp)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Read preference is not valid");
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return false;
|
|
}
|
|
|
|
mongoc_uri_set_read_prefs_t(uri, new_rp);
|
|
mongoc_read_prefs_destroy(new_rp);
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
static bool php_phongo_apply_wc_options_to_uri(mongoc_uri_t *uri, bson_t *options TSRMLS_DC) /* {{{ */
|
|
{
|
|
bson_iter_t iter;
|
|
int32_t wtimeoutms;
|
|
mongoc_write_concern_t *new_wc;
|
|
const mongoc_write_concern_t *old_wc;
|
|
|
|
if (!(old_wc = mongoc_uri_get_write_concern(uri))) {
|
|
phongo_throw_exception(PHONGO_ERROR_MONGOC_FAILED TSRMLS_CC, "mongoc_uri_t does not have a write concern");
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Return early if there are no options to apply */
|
|
if (bson_empty0(options)) {
|
|
return true;
|
|
}
|
|
|
|
if (!bson_iter_init_find_case(&iter, options, "journal") &&
|
|
!bson_iter_init_find_case(&iter, options, "safe") &&
|
|
!bson_iter_init_find_case(&iter, options, "w") &&
|
|
!bson_iter_init_find_case(&iter, options, "wtimeoutms")) {
|
|
return true;
|
|
}
|
|
|
|
wtimeoutms = mongoc_write_concern_get_wtimeout(old_wc);
|
|
|
|
new_wc = mongoc_write_concern_copy(old_wc);
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "safe") && BSON_ITER_HOLDS_BOOL(&iter)) {
|
|
mongoc_write_concern_set_w(new_wc, bson_iter_bool(&iter) ? 1 : MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED);
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "wtimeoutms") && BSON_ITER_HOLDS_INT32(&iter)) {
|
|
wtimeoutms = bson_iter_int32(&iter);
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "journal") && BSON_ITER_HOLDS_BOOL(&iter)) {
|
|
mongoc_write_concern_set_journal(new_wc, bson_iter_bool(&iter));
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, options, "w")) {
|
|
if (BSON_ITER_HOLDS_INT32(&iter)) {
|
|
int32_t value = bson_iter_int32(&iter);
|
|
|
|
switch (value) {
|
|
case MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED:
|
|
case MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED:
|
|
mongoc_write_concern_set_w(new_wc, value);
|
|
break;
|
|
|
|
default:
|
|
if (value > 0) {
|
|
mongoc_write_concern_set_w(new_wc, value);
|
|
break;
|
|
}
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Unsupported w value: %d", value);
|
|
mongoc_write_concern_destroy(new_wc);
|
|
|
|
return false;
|
|
}
|
|
} else if (BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
const char *str = bson_iter_utf8(&iter, NULL);
|
|
|
|
if (0 == strcasecmp(PHONGO_WRITE_CONCERN_W_MAJORITY, str)) {
|
|
mongoc_write_concern_set_wmajority(new_wc, wtimeoutms);
|
|
} else {
|
|
mongoc_write_concern_set_wtag(new_wc, str);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Only set wtimeout if it's still applicable; otherwise, clear it. */
|
|
if (mongoc_write_concern_get_w(new_wc) > 1 ||
|
|
mongoc_write_concern_get_wmajority(new_wc) ||
|
|
mongoc_write_concern_get_wtag(new_wc)) {
|
|
mongoc_write_concern_set_wtimeout(new_wc, wtimeoutms);
|
|
} else {
|
|
mongoc_write_concern_set_wtimeout(new_wc, 0);
|
|
}
|
|
|
|
if (mongoc_write_concern_get_journal(new_wc)) {
|
|
int32_t w = mongoc_write_concern_get_w(new_wc);
|
|
|
|
if (w == MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED || w == MONGOC_WRITE_CONCERN_W_ERRORS_IGNORED) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Journal conflicts with w value: %d", w);
|
|
mongoc_write_concern_destroy(new_wc);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* This may be redundant in light of the last check (unacknowledged w with
|
|
journal), but we'll check anyway in case additional validation is
|
|
implemented. */
|
|
if (!mongoc_write_concern_is_valid(new_wc)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Write concern is not valid");
|
|
mongoc_write_concern_destroy(new_wc);
|
|
|
|
return false;
|
|
}
|
|
|
|
mongoc_uri_set_write_concern(uri, new_wc);
|
|
mongoc_write_concern_destroy(new_wc);
|
|
|
|
return true;
|
|
} /* }}} */
|
|
|
|
static inline char *php_phongo_fetch_ssl_opt_string(zval *zoptions, const char *key, int key_len)
|
|
{
|
|
int plen;
|
|
zend_bool pfree;
|
|
char *pval, *value;
|
|
|
|
pval = php_array_fetchl_string(zoptions, key, key_len, &plen, &pfree);
|
|
value = pfree ? pval : estrndup(pval, plen);
|
|
|
|
return value;
|
|
}
|
|
|
|
static mongoc_ssl_opt_t *php_phongo_make_ssl_opt(zval *zoptions TSRMLS_DC)
|
|
{
|
|
mongoc_ssl_opt_t *ssl_opt;
|
|
|
|
if (!zoptions) {
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT)
|
|
if (php_array_existsc(zoptions, "ca_dir")) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"ca_dir\" option is not supported by Secure Channel and Secure Transport");
|
|
return NULL;
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "capath")) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"capath\" option is not supported by Secure Channel and Secure Transport");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
#if defined(MONGOC_ENABLE_SSL_LIBRESSL) || defined(MONGOC_ENABLE_SSL_SECURE_TRANSPORT)
|
|
if (php_array_existsc(zoptions, "crl_file")) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "\"crl_file\" option is not supported by LibreSSL and Secure Transport");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
ssl_opt = ecalloc(1, sizeof(mongoc_ssl_opt_t));
|
|
|
|
/* Check canonical option names first and fall back to SSL context options
|
|
* for backwards compatibility. */
|
|
if (php_array_existsc(zoptions, "allow_invalid_hostname")) {
|
|
ssl_opt->allow_invalid_hostname = php_array_fetchc_bool(zoptions, "allow_invalid_hostname");
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "weak_cert_validation")) {
|
|
ssl_opt->weak_cert_validation = php_array_fetchc_bool(zoptions, "weak_cert_validation");
|
|
} else if (php_array_existsc(zoptions, "allow_self_signed")) {
|
|
ssl_opt->weak_cert_validation = php_array_fetchc_bool(zoptions, "allow_self_signed");
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "pem_file")) {
|
|
ssl_opt->pem_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("pem_file"));
|
|
} else if (php_array_existsc(zoptions, "local_cert")) {
|
|
ssl_opt->pem_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("local_cert"));
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "pem_pwd")) {
|
|
ssl_opt->pem_pwd = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("pem_pwd"));
|
|
} else if (php_array_existsc(zoptions, "passphrase")) {
|
|
ssl_opt->pem_pwd = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("passphrase"));
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "ca_file")) {
|
|
ssl_opt->ca_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("ca_file"));
|
|
} else if (php_array_existsc(zoptions, "cafile")) {
|
|
ssl_opt->ca_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("cafile"));
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "ca_dir")) {
|
|
ssl_opt->ca_dir = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("ca_dir"));
|
|
} else if (php_array_existsc(zoptions, "capath")) {
|
|
ssl_opt->ca_dir = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("capath"));
|
|
}
|
|
|
|
if (php_array_existsc(zoptions, "crl_file")) {
|
|
ssl_opt->crl_file = php_phongo_fetch_ssl_opt_string(zoptions, ZEND_STRL("crl_file"));
|
|
}
|
|
|
|
return ssl_opt;
|
|
}
|
|
|
|
static void php_phongo_free_ssl_opt(mongoc_ssl_opt_t *ssl_opt)
|
|
{
|
|
if (ssl_opt->pem_file) {
|
|
str_efree(ssl_opt->pem_file);
|
|
}
|
|
|
|
if (ssl_opt->pem_pwd) {
|
|
str_efree(ssl_opt->pem_pwd);
|
|
}
|
|
|
|
if (ssl_opt->ca_file) {
|
|
str_efree(ssl_opt->ca_file);
|
|
}
|
|
|
|
if (ssl_opt->ca_dir) {
|
|
str_efree(ssl_opt->ca_dir);
|
|
}
|
|
|
|
if (ssl_opt->crl_file) {
|
|
str_efree(ssl_opt->crl_file);
|
|
}
|
|
|
|
efree(ssl_opt);
|
|
}
|
|
|
|
/* Creates a hash for a client by concatenating the URI string with serialized
|
|
* options arrays. On success, a persistent string is returned (i.e. pefree()
|
|
* should be used to free it) and hash_len will be set to the string's length.
|
|
* On error, an exception will have been thrown and NULL will be returned. */
|
|
static char *php_phongo_manager_make_client_hash(const char *uri_string, zval *options, zval *driverOptions, size_t *hash_len TSRMLS_DC)
|
|
{
|
|
char *hash = NULL;
|
|
smart_str var_buf = {0};
|
|
php_serialize_data_t var_hash;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval args;
|
|
|
|
array_init_size(&args, 3);
|
|
ADD_ASSOC_STRING(&args, "uri", (char *) uri_string);
|
|
|
|
if (options) {
|
|
ADD_ASSOC_ZVAL_EX(&args, "options", options);
|
|
Z_ADDREF_P(options);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(&args, "options");
|
|
}
|
|
|
|
if (driverOptions) {
|
|
ADD_ASSOC_ZVAL_EX(&args, "driverOptions", driverOptions);
|
|
Z_ADDREF_P(driverOptions);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(&args, "driverOptions");
|
|
}
|
|
|
|
PHP_VAR_SERIALIZE_INIT(var_hash);
|
|
php_var_serialize(&var_buf, &args, &var_hash);
|
|
PHP_VAR_SERIALIZE_DESTROY(var_hash);
|
|
|
|
if (!EG(exception)) {
|
|
*hash_len = ZSTR_LEN(var_buf.s);
|
|
hash = pestrndup(ZSTR_VAL(var_buf.s), *hash_len, 1);
|
|
}
|
|
|
|
zval_ptr_dtor(&args);
|
|
#else
|
|
zval *args;
|
|
|
|
MAKE_STD_ZVAL(args);
|
|
array_init_size(args, 3);
|
|
ADD_ASSOC_STRING(args, "uri", (char *) uri_string);
|
|
|
|
if (options) {
|
|
ADD_ASSOC_ZVAL_EX(args, "options", options);
|
|
Z_ADDREF_P(options);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(args, "options");
|
|
}
|
|
|
|
if (driverOptions) {
|
|
ADD_ASSOC_ZVAL_EX(args, "driverOptions", driverOptions);
|
|
Z_ADDREF_P(driverOptions);
|
|
} else {
|
|
ADD_ASSOC_NULL_EX(args, "driverOptions");
|
|
}
|
|
|
|
PHP_VAR_SERIALIZE_INIT(var_hash);
|
|
php_var_serialize(&var_buf, &args, &var_hash TSRMLS_CC);
|
|
PHP_VAR_SERIALIZE_DESTROY(var_hash);
|
|
|
|
if (!EG(exception)) {
|
|
*hash_len = var_buf.len;
|
|
hash = pestrndup(var_buf.c, *hash_len, 1);
|
|
}
|
|
|
|
zval_ptr_dtor(&args);
|
|
#endif
|
|
|
|
smart_str_free(&var_buf);
|
|
|
|
return hash;
|
|
}
|
|
|
|
static mongoc_client_t *php_phongo_make_mongo_client(const mongoc_uri_t *uri, mongoc_ssl_opt_t *ssl_opt TSRMLS_DC) /* {{{ */
|
|
{
|
|
const char *mongoc_version, *bson_version;
|
|
mongoc_client_t *client;
|
|
|
|
#ifdef HAVE_SYSTEM_LIBMONGOC
|
|
mongoc_version = mongoc_get_version();
|
|
#else
|
|
mongoc_version = "bundled";
|
|
#endif
|
|
|
|
#ifdef HAVE_SYSTEM_LIBBSON
|
|
bson_version = bson_get_version();
|
|
#else
|
|
bson_version = "bundled";
|
|
#endif
|
|
|
|
MONGOC_DEBUG("Creating Manager, phongo-%s[%s] - mongoc-%s(%s), libbson-%s(%s), php-%s",
|
|
MONGODB_VERSION_S,
|
|
MONGODB_STABILITY_S,
|
|
MONGOC_VERSION_S,
|
|
mongoc_version,
|
|
BSON_VERSION_S,
|
|
bson_version,
|
|
PHP_VERSION
|
|
);
|
|
client = mongoc_client_new_from_uri(uri);
|
|
|
|
if (!client) {
|
|
return NULL;
|
|
}
|
|
|
|
if (mongoc_uri_get_ssl(uri) && ssl_opt) {
|
|
mongoc_client_set_ssl_opts(client, ssl_opt);
|
|
}
|
|
|
|
return client;
|
|
} /* }}} */
|
|
|
|
void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string, zval *options, zval *driverOptions TSRMLS_DC) /* {{{ */
|
|
{
|
|
char *hash = NULL;
|
|
size_t hash_len = 0;
|
|
bson_t bson_options = BSON_INITIALIZER;
|
|
mongoc_uri_t *uri = NULL;
|
|
mongoc_ssl_opt_t *ssl_opt = NULL;
|
|
bson_iter_t iter;
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval *client_ptr;
|
|
zval new_client_ptr;
|
|
#else
|
|
mongoc_client_t **client_ptr;
|
|
#endif
|
|
|
|
if (!(hash = php_phongo_manager_make_client_hash(uri_string, options, driverOptions, &hash_len TSRMLS_CC))) {
|
|
/* Exception should already have been thrown and there is nothing to free */
|
|
return;
|
|
}
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
if ((client_ptr = zend_hash_str_find(&MONGODB_G(clients), hash, hash_len)) && Z_TYPE_P(client_ptr) == IS_PTR) {
|
|
MONGOC_DEBUG("Found client for hash: %s\n", hash);
|
|
manager->client = (mongoc_client_t *)Z_PTR_P(client_ptr);
|
|
goto cleanup;
|
|
}
|
|
#else
|
|
if (zend_hash_find(&MONGODB_G(clients), hash, hash_len + 1, (void**) &client_ptr) == SUCCESS) {
|
|
MONGOC_DEBUG("Found client for hash: %s\n", hash);
|
|
manager->client = *client_ptr;
|
|
goto cleanup;
|
|
}
|
|
#endif
|
|
|
|
if (options) {
|
|
phongo_zval_to_bson(options, PHONGO_BSON_NONE, &bson_options, NULL TSRMLS_CC);
|
|
}
|
|
|
|
/* An exception may be thrown during BSON conversion */
|
|
if (EG(exception)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(uri = php_phongo_make_uri(uri_string, &bson_options))) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Failed to parse MongoDB URI: '%s'", uri_string);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!php_phongo_apply_rc_options_to_uri(uri, &bson_options TSRMLS_CC) ||
|
|
!php_phongo_apply_rp_options_to_uri(uri, &bson_options TSRMLS_CC) ||
|
|
!php_phongo_apply_wc_options_to_uri(uri, &bson_options TSRMLS_CC)) {
|
|
/* Exception should already have been thrown */
|
|
goto cleanup;
|
|
}
|
|
|
|
if (bson_iter_init_find_case(&iter, &bson_options, "appname") && BSON_ITER_HOLDS_UTF8(&iter)) {
|
|
const char *str = bson_iter_utf8(&iter, NULL);
|
|
|
|
if (!mongoc_uri_set_appname(uri, str)) {
|
|
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "Invalid appname value: '%s'", str);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
ssl_opt = php_phongo_make_ssl_opt(driverOptions TSRMLS_CC);
|
|
|
|
/* An exception may be thrown during SSL option creation */
|
|
if (EG(exception)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
manager->client = php_phongo_make_mongo_client(uri, ssl_opt TSRMLS_CC);
|
|
|
|
if (!manager->client) {
|
|
phongo_throw_exception(PHONGO_ERROR_RUNTIME TSRMLS_CC, "Failed to create Manager from URI: '%s'", uri_string);
|
|
goto cleanup;
|
|
}
|
|
|
|
MONGOC_DEBUG("Created client hash: %s\n", hash);
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_PTR(&new_client_ptr, manager->client);
|
|
zend_hash_str_update(&MONGODB_G(clients), hash, hash_len, &new_client_ptr);
|
|
#else
|
|
zend_hash_update(&MONGODB_G(clients), hash, hash_len + 1, &manager->client, sizeof(mongoc_client_t *), NULL);
|
|
#endif
|
|
|
|
cleanup:
|
|
if (hash) {
|
|
pefree(hash, 1);
|
|
}
|
|
|
|
bson_destroy(&bson_options);
|
|
|
|
if (uri) {
|
|
mongoc_uri_destroy(uri);
|
|
}
|
|
|
|
if (ssl_opt) {
|
|
php_phongo_free_ssl_opt(ssl_opt);
|
|
}
|
|
} /* }}} */
|
|
|
|
void php_phongo_new_utcdatetime_from_epoch(zval *object, int64_t msec_since_epoch TSRMLS_DC) /* {{{ */
|
|
{
|
|
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;
|
|
} /* }}} */
|
|
|
|
void php_phongo_new_timestamp_from_increment_and_timestamp(zval *object, uint32_t increment, uint32_t timestamp TSRMLS_DC) /* {{{ */
|
|
{
|
|
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_new_javascript_from_javascript(int init, zval *object, const char *code, size_t code_len TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_new_javascript_from_javascript_and_scope(init, object, code, code_len, NULL TSRMLS_CC);
|
|
} /* }}} */
|
|
void php_phongo_new_javascript_from_javascript_and_scope(int init, zval *object, const char *code, size_t code_len, const bson_t *scope TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_javascript_t *intern;
|
|
|
|
if (init) {
|
|
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;
|
|
} /* }}} */
|
|
void php_phongo_new_binary_from_binary_and_type(zval *object, const char *data, size_t data_len, bson_subtype_t type TSRMLS_DC) /* {{{ */
|
|
{
|
|
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;
|
|
} /* }}} */
|
|
|
|
void php_phongo_new_decimal128(zval *object, const bson_decimal128_t *decimal TSRMLS_DC) /* {{{ */
|
|
{
|
|
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;
|
|
} /* }}} */
|
|
|
|
void php_phongo_new_regex_from_regex_and_options(zval *object, const char *pattern, const char *flags TSRMLS_DC) /* {{{ */
|
|
{
|
|
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 void php_phongo_cursor_free_current(php_phongo_cursor_t *cursor) /* {{{ */
|
|
{
|
|
if (!Z_ISUNDEF(cursor->visitor_data.zchild)) {
|
|
zval_ptr_dtor(&cursor->visitor_data.zchild);
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_UNDEF(&cursor->visitor_data.zchild);
|
|
#else
|
|
cursor->visitor_data.zchild = NULL;
|
|
#endif
|
|
}
|
|
} /* }}} */
|
|
|
|
void php_phongo_cursor_free(php_phongo_cursor_t *cursor) /* {{{ */
|
|
{
|
|
if (cursor->cursor) {
|
|
mongoc_cursor_destroy(cursor->cursor);
|
|
cursor->cursor = NULL;
|
|
}
|
|
|
|
php_phongo_cursor_free_current(cursor);
|
|
} /* }}} */
|
|
|
|
/* {{{ Iterator */
|
|
static void php_phongo_cursor_iterator_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_iterator *cursor_it = (php_phongo_cursor_iterator *)iter;
|
|
|
|
if (!Z_ISUNDEF(cursor_it->intern.data)) {
|
|
#if PHP_VERSION_ID >= 70000
|
|
zval_ptr_dtor(&cursor_it->intern.data);
|
|
#else
|
|
zval_ptr_dtor((zval**)&cursor_it->intern.data);
|
|
cursor_it->intern.data = NULL;
|
|
#endif
|
|
}
|
|
|
|
#if PHP_VERSION_ID < 70000
|
|
efree(cursor_it);
|
|
#endif
|
|
} /* }}} */
|
|
|
|
static int php_phongo_cursor_iterator_valid(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *cursor = ((php_phongo_cursor_iterator *)iter)->cursor;
|
|
|
|
if (!Z_ISUNDEF(cursor->visitor_data.zchild)) {
|
|
return SUCCESS;
|
|
}
|
|
|
|
return FAILURE;
|
|
} /* }}} */
|
|
|
|
#if PHP_VERSION_ID < 50500
|
|
static int php_phongo_cursor_iterator_get_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *cursor = ((php_phongo_cursor_iterator *)iter)->cursor;
|
|
|
|
*int_key = (ulong) cursor->current;
|
|
return HASH_KEY_IS_LONG;
|
|
} /* }}} */
|
|
#else
|
|
static void php_phongo_cursor_iterator_get_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *cursor = ((php_phongo_cursor_iterator *)iter)->cursor;
|
|
|
|
ZVAL_LONG(key, cursor->current);
|
|
} /* }}} */
|
|
#endif
|
|
|
|
#if PHP_VERSION_ID < 70000
|
|
static void php_phongo_cursor_iterator_get_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *cursor = ((php_phongo_cursor_iterator *)iter)->cursor;
|
|
|
|
*data = &cursor->visitor_data.zchild;
|
|
} /* }}} */
|
|
#else
|
|
static zval* php_phongo_cursor_iterator_get_current_data(zend_object_iterator *iter) /* {{{ */
|
|
{
|
|
php_phongo_cursor_t *cursor = ((php_phongo_cursor_iterator *)iter)->cursor;
|
|
|
|
return &cursor->visitor_data.zchild;
|
|
} /* }}} */
|
|
#endif
|
|
|
|
static void php_phongo_cursor_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_iterator *cursor_it = (php_phongo_cursor_iterator *)iter;
|
|
php_phongo_cursor_t *cursor = cursor_it->cursor;
|
|
const bson_t *doc;
|
|
|
|
php_phongo_cursor_free_current(cursor);
|
|
cursor->current++;
|
|
|
|
if (mongoc_cursor_next(cursor->cursor, &doc)) {
|
|
phongo_bson_to_zval_ex(bson_get_data(doc), doc->len, &cursor->visitor_data);
|
|
} else {
|
|
bson_error_t error;
|
|
|
|
if (mongoc_cursor_error(cursor->cursor, &error)) {
|
|
/* Intentionally not destroying the cursor as it will happen
|
|
* naturally now that there are no more results */
|
|
phongo_throw_exception_from_bson_error_t(&error TSRMLS_CC);
|
|
}
|
|
}
|
|
} /* }}} */
|
|
|
|
static void php_phongo_cursor_iterator_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_iterator *cursor_it = (php_phongo_cursor_iterator *)iter;
|
|
php_phongo_cursor_t *cursor = cursor_it->cursor;
|
|
const bson_t *doc;
|
|
|
|
if (cursor->current > 0) {
|
|
phongo_throw_exception(PHONGO_ERROR_LOGIC TSRMLS_CC, "Cursors cannot rewind after starting iteration");
|
|
return;
|
|
}
|
|
|
|
php_phongo_cursor_free_current(cursor);
|
|
|
|
doc = mongoc_cursor_current(cursor->cursor);
|
|
|
|
if (doc) {
|
|
phongo_bson_to_zval_ex(bson_get_data(doc), doc->len, &cursor->visitor_data);
|
|
}
|
|
} /* }}} */
|
|
|
|
/* iterator handler table */
|
|
zend_object_iterator_funcs php_phongo_cursor_iterator_funcs = {
|
|
php_phongo_cursor_iterator_dtor,
|
|
php_phongo_cursor_iterator_valid,
|
|
php_phongo_cursor_iterator_get_current_data,
|
|
php_phongo_cursor_iterator_get_current_key,
|
|
php_phongo_cursor_iterator_move_forward,
|
|
php_phongo_cursor_iterator_rewind,
|
|
NULL /* invalidate_current is not used */
|
|
};
|
|
|
|
zend_object_iterator *php_phongo_cursor_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
|
|
{
|
|
php_phongo_cursor_iterator *cursor_it = NULL;
|
|
php_phongo_cursor_t *cursor = Z_CURSOR_OBJ_P(object);
|
|
|
|
if (by_ref) {
|
|
zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
|
|
}
|
|
|
|
if (cursor->got_iterator) {
|
|
phongo_throw_exception(PHONGO_ERROR_LOGIC TSRMLS_CC, "Cursors cannot yield multiple iterators");
|
|
return NULL;
|
|
}
|
|
|
|
cursor->got_iterator = 1;
|
|
|
|
cursor_it = ecalloc(1, sizeof(php_phongo_cursor_iterator));
|
|
#if PHP_VERSION_ID >= 70000
|
|
zend_iterator_init(&cursor_it->intern);
|
|
#endif
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
ZVAL_COPY(&cursor_it->intern.data, object);
|
|
#else
|
|
Z_ADDREF_P(object);
|
|
cursor_it->intern.data = (void*)object;
|
|
#endif
|
|
cursor_it->intern.funcs = &php_phongo_cursor_iterator_funcs;
|
|
cursor_it->cursor = cursor;
|
|
/* cursor_it->current should already be allocated to zero */
|
|
|
|
php_phongo_cursor_free_current(cursor_it->cursor);
|
|
|
|
return &cursor_it->intern;
|
|
} /* }}} */
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ Memory allocation wrappers */
|
|
static void* php_phongo_malloc(size_t num_bytes) /* {{{ */
|
|
{
|
|
return pemalloc(num_bytes, 1);
|
|
} /* }}} */
|
|
|
|
static void* php_phongo_calloc(size_t num_members, size_t num_bytes) /* {{{ */
|
|
{
|
|
return pecalloc(num_members, num_bytes, 1);
|
|
} /* }}} */
|
|
|
|
static void* php_phongo_realloc(void *mem, size_t num_bytes) { /* {{{ */
|
|
return perealloc(mem, num_bytes, 1);
|
|
} /* }}} */
|
|
|
|
static void php_phongo_free(void *mem) /* {{{ */
|
|
{
|
|
if (mem) {
|
|
pefree(mem, 1);
|
|
}
|
|
} /* }}} */
|
|
|
|
/* }}} */
|
|
|
|
#ifdef PHP_DEBUG
|
|
/* LCOV_EXCL_START */
|
|
void _phongo_debug_bson(bson_t *bson)
|
|
{
|
|
char *str;
|
|
size_t str_len;
|
|
|
|
str = bson_as_json(bson, &str_len);
|
|
|
|
php_printf("JSON: %s\n", str);
|
|
bson_free(str);
|
|
}
|
|
/* LCOV_EXCL_STOP */
|
|
#endif
|
|
|
|
/* {{{ M[INIT|SHUTDOWN] R[INIT|SHUTDOWN] G[INIT|SHUTDOWN] MINFO INI */
|
|
|
|
ZEND_INI_MH(OnUpdateDebug)
|
|
{
|
|
void ***ctx = NULL;
|
|
char *tmp_dir = NULL;
|
|
|
|
TSRMLS_SET_CTX(ctx);
|
|
|
|
/* Close any previously open log files */
|
|
if (MONGODB_G(debug_fd)) {
|
|
if (MONGODB_G(debug_fd) != stderr && MONGODB_G(debug_fd) != stdout) {
|
|
fclose(MONGODB_G(debug_fd));
|
|
}
|
|
MONGODB_G(debug_fd) = NULL;
|
|
}
|
|
|
|
if (!new_value || (new_value && !ZSTR_VAL(new_value)[0])
|
|
|| strcasecmp("0", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("off", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("no", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("false", ZSTR_VAL(new_value)) == 0
|
|
) {
|
|
mongoc_log_trace_disable();
|
|
mongoc_log_set_handler(NULL, NULL);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
|
|
#else
|
|
return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
|
|
#endif
|
|
}
|
|
|
|
|
|
if (strcasecmp(ZSTR_VAL(new_value), "stderr") == 0) {
|
|
MONGODB_G(debug_fd) = stderr;
|
|
} else if (strcasecmp(ZSTR_VAL(new_value), "stdout") == 0) {
|
|
MONGODB_G(debug_fd) = stdout;
|
|
} else if (
|
|
strcasecmp("1", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("on", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("yes", ZSTR_VAL(new_value)) == 0
|
|
|| strcasecmp("true", ZSTR_VAL(new_value)) == 0
|
|
) {
|
|
tmp_dir = NULL;
|
|
} else {
|
|
tmp_dir = ZSTR_VAL(new_value);
|
|
}
|
|
|
|
if (!MONGODB_G(debug_fd)) {
|
|
time_t t;
|
|
int fd = -1;
|
|
char *prefix;
|
|
int len;
|
|
phongo_char *filename;
|
|
|
|
time(&t);
|
|
len = spprintf(&prefix, 0, "PHONGO-%ld", t);
|
|
|
|
fd = php_open_temporary_fd(tmp_dir, prefix, &filename TSRMLS_CC);
|
|
if (fd != -1) {
|
|
const char *path = ZSTR_VAL(filename);
|
|
MONGODB_G(debug_fd) = VCWD_FOPEN(path, "a");
|
|
}
|
|
efree(filename);
|
|
efree(prefix);
|
|
close(fd);
|
|
}
|
|
|
|
mongoc_log_trace_enable();
|
|
mongoc_log_set_handler(php_phongo_log, ctx);
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
return OnUpdateString(entry, new_value, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
|
|
#else
|
|
return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
|
|
#endif
|
|
}
|
|
|
|
|
|
/* {{{ INI entries */
|
|
PHP_INI_BEGIN()
|
|
#if PHP_VERSION_ID >= 70000
|
|
STD_PHP_INI_ENTRY(PHONGO_DEBUG_INI, PHONGO_DEBUG_INI_DEFAULT, PHP_INI_ALL, OnUpdateDebug, debug, zend_mongodb_globals, mongodb_globals)
|
|
#else
|
|
{ 0, PHP_INI_ALL, (char *)PHONGO_DEBUG_INI, sizeof(PHONGO_DEBUG_INI), OnUpdateDebug, (void *) XtOffsetOf(zend_mongodb_globals, debug), (void *) &mglo, NULL, (char *)PHONGO_DEBUG_INI_DEFAULT, sizeof(PHONGO_DEBUG_INI_DEFAULT)-1, NULL, 0, 0, 0, NULL },
|
|
#endif
|
|
PHP_INI_END()
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_GINIT_FUNCTION */
|
|
PHP_GINIT_FUNCTION(mongodb)
|
|
{
|
|
bson_mem_vtable_t bsonMemVTable = {
|
|
php_phongo_malloc,
|
|
php_phongo_calloc,
|
|
php_phongo_realloc,
|
|
php_phongo_free,
|
|
};
|
|
#if PHP_VERSION_ID >= 70000
|
|
#if defined(COMPILE_DL_MONGODB) && defined(ZTS)
|
|
ZEND_TSRMLS_CACHE_UPDATE();
|
|
#endif
|
|
#endif
|
|
mongodb_globals->debug_fd = NULL;
|
|
mongodb_globals->bsonMemVTable = bsonMemVTable;
|
|
|
|
}
|
|
/* }}} */
|
|
|
|
#if PHP_VERSION_ID >= 70000
|
|
static void php_phongo_client_dtor(zval *zv)
|
|
{
|
|
mongoc_client_destroy((mongoc_client_t *) Z_PTR_P(zv));
|
|
}
|
|
#else
|
|
static void php_phongo_client_dtor(void *client)
|
|
{
|
|
mongoc_client_destroy(*((mongoc_client_t **) client));
|
|
}
|
|
#endif
|
|
|
|
/* {{{ PHP_MINIT_FUNCTION */
|
|
PHP_MINIT_FUNCTION(mongodb)
|
|
{
|
|
char *php_version_string;
|
|
|
|
(void)type; /* We don't care if we are loaded via dl() or extension= */
|
|
|
|
REGISTER_INI_ENTRIES();
|
|
|
|
/* Initialize libmongoc */
|
|
mongoc_init();
|
|
|
|
/* Set handshake options */
|
|
php_version_string = malloc(4 + sizeof(PHP_VERSION) + 1);
|
|
snprintf(php_version_string, 4 + sizeof(PHP_VERSION) + 1, "PHP %s", PHP_VERSION);
|
|
mongoc_handshake_data_append("ext-mongodb:PHP", MONGODB_VERSION_S, php_version_string);
|
|
free(php_version_string);
|
|
|
|
/* Initialize libbson */
|
|
bson_mem_set_vtable(&MONGODB_G(bsonMemVTable));
|
|
|
|
/* Initialize HashTable for persistent clients */
|
|
zend_hash_init(&MONGODB_G(clients), 0, NULL, php_phongo_client_dtor, 1);
|
|
|
|
/* Prep default object handlers to be used when we register the classes */
|
|
memcpy(&phongo_std_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
|
|
phongo_std_object_handlers.clone_obj = NULL;
|
|
/*
|
|
phongo_std_object_handlers.get_debug_info = NULL;
|
|
phongo_std_object_handlers.compare_objects = NULL;
|
|
phongo_std_object_handlers.cast_object = NULL;
|
|
phongo_std_object_handlers.count_elements = NULL;
|
|
phongo_std_object_handlers.get_closure = NULL;
|
|
*/
|
|
|
|
PHP_MINIT(bson)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
PHP_MINIT(Command)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Cursor)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(CursorId)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Manager)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Query)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ReadConcern)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ReadPreference)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Server)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(BulkWrite)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(WriteConcern)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(WriteConcernError)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(WriteError)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(WriteResult)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
PHP_MINIT(Exception)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(LogicException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(RuntimeException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(UnexpectedValueException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(InvalidArgumentException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ConnectionException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(AuthenticationException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(SSLConnectionException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(WriteException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(BulkWriteException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ExecutionTimeoutException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ConnectionTimeoutException)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
PHP_MINIT(Type)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Serializable)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Unserializable)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Persistable)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Binary)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Decimal128)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Javascript)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(MaxKey)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(MinKey)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(ObjectID)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Regex)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(Timestamp)(INIT_FUNC_ARGS_PASSTHRU);
|
|
PHP_MINIT(UTCDateTime)(INIT_FUNC_ARGS_PASSTHRU);
|
|
|
|
REGISTER_STRING_CONSTANT("MONGODB_VERSION", (char *)MONGODB_VERSION_S, CONST_CS | CONST_PERSISTENT);
|
|
REGISTER_STRING_CONSTANT("MONGODB_STABILITY", (char *)MONGODB_STABILITY_S, CONST_CS | CONST_PERSISTENT);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MSHUTDOWN_FUNCTION */
|
|
PHP_MSHUTDOWN_FUNCTION(mongodb)
|
|
{
|
|
(void)type; /* We don't care if we are loaded via dl() or extension= */
|
|
|
|
/* Destroy HashTable for persistent clients. The HashTable destructor will
|
|
* destroy any mongoc_client_t objects contained within. */
|
|
zend_hash_destroy(&MONGODB_G(clients));
|
|
|
|
bson_mem_restore_vtable();
|
|
/* Cleanup after libmongoc */
|
|
mongoc_cleanup();
|
|
|
|
UNREGISTER_INI_ENTRIES();
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_GSHUTDOWN_FUNCTION */
|
|
PHP_GSHUTDOWN_FUNCTION(mongodb)
|
|
{
|
|
mongodb_globals->debug = NULL;
|
|
if (mongodb_globals->debug_fd) {
|
|
fclose(mongodb_globals->debug_fd);
|
|
mongodb_globals->debug_fd = NULL;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MINFO_FUNCTION */
|
|
PHP_MINFO_FUNCTION(mongodb)
|
|
{
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "mongodb support", "enabled");
|
|
php_info_print_table_row(2, "mongodb version", MONGODB_VERSION_S);
|
|
php_info_print_table_row(2, "mongodb stability", MONGODB_STABILITY_S);
|
|
#ifdef HAVE_SYSTEM_LIBMONGOC
|
|
php_info_print_table_row(2, "libmongoc headers version", MONGOC_VERSION_S);
|
|
php_info_print_table_row(2, "libmongoc library version", mongoc_get_version());
|
|
#else
|
|
/* Bundled libraries, buildtime = runtime */
|
|
php_info_print_table_row(2, "libmongoc version", MONGOC_VERSION_S);
|
|
#endif
|
|
#ifdef HAVE_SYSTEM_LIBBSON
|
|
php_info_print_table_row(2, "libbson headers version", BSON_VERSION_S);
|
|
php_info_print_table_row(2, "libbson library version", bson_get_version());
|
|
#else
|
|
php_info_print_table_row(2, "libbson version", BSON_VERSION_S);
|
|
#endif
|
|
php_info_print_table_end();
|
|
|
|
DISPLAY_INI_ENTRIES();
|
|
}
|
|
/* }}} */
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ mongodb_functions[]
|
|
*/
|
|
ZEND_BEGIN_ARG_INFO_EX(ai_bson_fromPHP, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, value)
|
|
ZEND_END_ARG_INFO();
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(ai_bson_toPHP, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, bson)
|
|
ZEND_END_ARG_INFO();
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(ai_bson_toJSON, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, bson)
|
|
ZEND_END_ARG_INFO();
|
|
|
|
ZEND_BEGIN_ARG_INFO_EX(ai_bson_fromJSON, 0, 0, 1)
|
|
ZEND_ARG_INFO(0, json)
|
|
ZEND_END_ARG_INFO();
|
|
|
|
const zend_function_entry mongodb_functions[] = {
|
|
ZEND_NS_FE("MongoDB\\BSON", fromPHP, ai_bson_fromPHP)
|
|
ZEND_NS_FE("MongoDB\\BSON", toPHP, ai_bson_toPHP)
|
|
ZEND_NS_FE("MongoDB\\BSON", toJSON, ai_bson_toJSON)
|
|
ZEND_NS_FE("MongoDB\\BSON", fromJSON, ai_bson_fromJSON)
|
|
PHP_FE_END
|
|
};
|
|
/* }}} */
|
|
|
|
/* {{{ mongodb_module_entry
|
|
*/
|
|
zend_module_entry mongodb_module_entry = {
|
|
STANDARD_MODULE_HEADER,
|
|
"mongodb",
|
|
mongodb_functions,
|
|
PHP_MINIT(mongodb),
|
|
PHP_MSHUTDOWN(mongodb),
|
|
NULL /* PHP_RINIT(mongodb)*/,
|
|
NULL /* PHP_RSHUTDOWN(mongodb)*/,
|
|
PHP_MINFO(mongodb),
|
|
MONGODB_VERSION,
|
|
PHP_MODULE_GLOBALS(mongodb),
|
|
PHP_GINIT(mongodb),
|
|
PHP_GSHUTDOWN(mongodb),
|
|
NULL,
|
|
STANDARD_MODULE_PROPERTIES_EX
|
|
};
|
|
/* }}} */
|
|
|
|
#ifdef COMPILE_DL_MONGODB
|
|
ZEND_GET_MODULE(mongodb)
|
|
#endif
|
|
|
|
/*
|
|
* 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
|
|
*/
|