Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1505 lines (1215 sloc) 39.953 kB
//cursor.c
/**
* Copyright 2009-2011 10gen, 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 <php.h>
#include <zend_interfaces.h>
#include <zend_exceptions.h>
#ifdef WIN32
# ifndef int64_t
typedef __int64 int64_t;
# endif
#else
#include <unistd.h>
#include <pthread.h>
#endif
#include <math.h>
#include "php_mongo.h"
#include "bson.h"
#include "db.h"
#include "cursor.h"
#include "collection.h"
#include "mongo_types.h"
#include "util/link.h"
#include "util/rs.h"
#include "util/io.h"
#if WIN32
HANDLE cursor_mutex;
#else
static pthread_mutex_t cursor_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
/* Cursor flags */
#define CURSOR_FLAG_TAILABLE 2
#define CURSOR_FLAG_SLAVE_OKAY 4
#define CURSOR_FLAG_OPLOG_REPLAY 8 /* Don't use */
#define CURSOR_FLAG_NO_CURSOR_TO 16
#define CURSOR_FLAG_AWAIT_DATA 32
#define CURSOR_FLAG_EXHAUST 64 /* Not implemented */
#define CURSOR_FLAG_PARTIAL 128
// externs
extern zend_class_entry *mongo_ce_Id,
*mongo_ce_Mongo,
*mongo_ce_DB,
*mongo_ce_Collection,
*mongo_ce_Exception;
extern int le_pconnection,
le_cursor_list;
extern zend_object_handlers mongo_default_handlers;
zend_class_entry *mongo_ce_CursorException;
ZEND_EXTERN_MODULE_GLOBALS(mongo);
static zend_object_value php_mongo_cursor_new(zend_class_entry *class_type TSRMLS_DC);
static void make_special(mongo_cursor *);
static void kill_cursor(cursor_node *node, zend_rsrc_list_entry *le TSRMLS_DC);
zend_class_entry *mongo_ce_Cursor = NULL;
/* {{{ MongoCursor->__construct
*/
PHP_METHOD(MongoCursor, __construct) {
zval *zlink = 0, *zns = 0, *zquery = 0, *zfields = 0, *empty, *timeout;
zval **data;
mongo_cursor *cursor;
mongo_link *link;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Oz|zz", &zlink,
mongo_ce_Mongo, &zns, &zquery, &zfields) == FAILURE) {
return;
}
if ((zquery && IS_SCALAR_P(zquery)) || (zfields && IS_SCALAR_P(zfields))) {
zend_error(E_WARNING, "MongoCursor::__construct() expects parameters 3 and 4 to be arrays or objects");
return;
}
// if query or fields weren't passed, make them default to an empty array
MAKE_STD_ZVAL(empty);
object_init(empty);
// these are both initialized to the same zval, but that's okay because
// there's no way to change them without creating a new cursor
if (!zquery || (Z_TYPE_P(zquery) == IS_ARRAY && zend_hash_num_elements(HASH_P(zquery)) == 0)) {
zquery = empty;
}
if (!zfields) {
zfields = empty;
}
cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
// db connection
cursor->resource = zlink;
zval_add_ref(&zlink);
// db connection resource
PHP_MONGO_GET_LINK(zlink);
cursor->link = link;
// change ['x', 'y', 'z'] into {'x' : 1, 'y' : 1, 'z' : 1}
if (Z_TYPE_P(zfields) == IS_ARRAY) {
HashPosition pointer;
zval *fields;
MAKE_STD_ZVAL(fields);
array_init(fields);
// fields to return
for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zfields), &pointer);
zend_hash_get_current_data_ex(Z_ARRVAL_P(zfields), (void**) &data, &pointer) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(zfields), &pointer)) {
int key_type, key_len;
ulong index;
char *key;
key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zfields), &key, (uint*)&key_len, &index, NO_DUP, &pointer);
if (key_type == HASH_KEY_IS_LONG) {
if (Z_TYPE_PP(data) == IS_STRING) {
add_assoc_long(fields, Z_STRVAL_PP(data), 1);
}
else {
zval_ptr_dtor(&empty);
zval_ptr_dtor(&fields);
zend_throw_exception(mongo_ce_Exception, "field names must be strings", 0 TSRMLS_CC);
return;
}
}
else {
add_assoc_zval(fields, key, *data);
zval_add_ref(data);
}
}
cursor->fields = fields;
}
// if it's already an object, we don't have to worry
else {
cursor->fields = zfields;
zval_add_ref(&zfields);
}
// ns
convert_to_string(zns);
cursor->ns = estrdup(Z_STRVAL_P(zns));
// query
cursor->query = zquery;
zval_add_ref(&zquery);
// reset iteration pointer, just in case
MONGO_METHOD(MongoCursor, reset, return_value, getThis());
cursor->at = 0;
cursor->num = 0;
cursor->special = 0;
cursor->persist = 0;
timeout = zend_read_static_property(mongo_ce_Cursor, "timeout", strlen("timeout"), NOISY TSRMLS_CC);
cursor->timeout = Z_LVAL_P(timeout);
cursor->opts = link->slave_okay ? (1 << 2) : 0;
// get rid of extra ref
zval_ptr_dtor(&empty);
}
/* }}} */
static void make_special(mongo_cursor *cursor) {
zval *temp;
if (cursor->special) {
return;
}
cursor->special = 1;
temp = cursor->query;
MAKE_STD_ZVAL(cursor->query);
array_init(cursor->query);
add_assoc_zval(cursor->query, "$query", temp);
}
/* {{{ MongoCursor::hasNext
*/
PHP_METHOD(MongoCursor, hasNext) {
buffer buf;
int size;
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
zval *temp;
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
if (!cursor->started_iterating) {
MONGO_METHOD(MongoCursor, doQuery, return_value, getThis());
cursor->started_iterating = 1;
}
if ((cursor->limit > 0 && cursor->at >= cursor->limit) || cursor->num == 0) {
if (cursor->cursor_id != 0) {
mongo_cursor_free_le(cursor, MONGO_CURSOR TSRMLS_CC);
}
RETURN_FALSE;
}
if (cursor->at < cursor->num) {
RETURN_TRUE;
}
else if (cursor->cursor_id == 0) {
RETURN_FALSE;
}
// if we have a cursor_id, we should have a server
else if (cursor->server == 0) {
mongo_cursor_throw(0, 18 TSRMLS_CC, "trying to get more, but cannot find server");
return;
}
// we have to go and check with the db
size = 34+strlen(cursor->ns);
CREATE_BUF(buf, size);
if (FAILURE == php_mongo_write_get_more(&buf, cursor TSRMLS_CC)) {
efree(buf.start);
return;
}
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
if(mongo_say(cursor->server, &buf, temp TSRMLS_CC) == FAILURE) {
efree(buf.start);
mongo_cursor_throw(cursor->server, 1 TSRMLS_CC, Z_STRVAL_P(temp));
zval_ptr_dtor(&temp);
mongo_util_cursor_failed(cursor TSRMLS_CC);
return;
}
efree(buf.start);
if (php_mongo_get_reply(cursor, temp TSRMLS_CC) != SUCCESS) {
zval_ptr_dtor(&temp);
mongo_util_cursor_failed(cursor TSRMLS_CC);
return;
}
zval_ptr_dtor(&temp);
if (cursor->cursor_id == 0) {
mongo_cursor_free_le(cursor, MONGO_CURSOR TSRMLS_CC);
}
// if cursor_id != 0, server should stay the same
if (cursor->flag & 1) {
mongo_cursor_throw(cursor->server, 2 TSRMLS_CC, "cursor not found");
return;
}
// sometimes we'll have a cursor_id but there won't be any more results
if (cursor->at >= cursor->num) {
RETURN_FALSE;
}
// but sometimes there will be
else {
RETURN_TRUE;
}
}
/* }}} */
/* {{{ MongoCursor::getNext
*/
PHP_METHOD(MongoCursor, getNext) {
MONGO_METHOD(MongoCursor, next, return_value, getThis());
// will be null unless there was an error
if (EG(exception) ||
(Z_TYPE_P(return_value) == IS_BOOL && Z_BVAL_P(return_value) == 0)) {
return;
}
MONGO_METHOD(MongoCursor, current, return_value, getThis());
}
/* }}} */
/* {{{ MongoCursor::limit
*/
PHP_METHOD(MongoCursor, limit) {
long l;
preiteration_setup;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &l) == FAILURE) {
return;
}
cursor->limit = l;
RETVAL_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::batchSize
*/
PHP_METHOD(MongoCursor, batchSize) {
long l;
preiteration_setup;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &l) == FAILURE) {
return;
}
cursor->batch_size = l;
RETVAL_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::skip
*/
PHP_METHOD(MongoCursor, skip) {
long l;
preiteration_setup;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &l) == FAILURE) {
return;
}
cursor->skip = l;
RETURN_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::fields
*/
PHP_METHOD(MongoCursor, fields) {
zval *z;
preiteration_setup;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) {
return;
}
if (IS_SCALAR_P(z)) {
zend_error(E_WARNING, "MongoCursor::fields() expects parameter 1 to be an array or object");
return;
}
zval_ptr_dtor(&cursor->fields);
cursor->fields = z;
zval_add_ref(&z);
RETURN_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::dead
*/
PHP_METHOD(MongoCursor, dead) {
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
RETURN_BOOL(cursor->started_iterating && cursor->cursor_id == 0);
}
/* }}} */
/* {{{ Cursor flags
* Sets or unsets the flag <flag>. With mode = -1, the arguments are parsed.
* Otherwise the mode should contain 0 for unsetting and 1 for setting the flag.
*/
static inline void set_cursor_flag(INTERNAL_FUNCTION_PARAMETERS, int flag, int mode)
{
zend_bool z = 1;
preiteration_setup;
if (mode == -1) {
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &z) == FAILURE) {
return;
}
} else {
z = mode;
}
if (z) {
cursor->opts |= flag;
} else {
cursor->opts &= ~flag;
}
RETURN_ZVAL(getThis(), 1, 0);
}
/* {{{ MongoCursor::setFlag(int bit [, bool set])
*/
PHP_METHOD(MongoCursor, setFlag)
{
long bit;
zend_bool set = 1;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|b", &bit, &set) == FAILURE) {
return;
}
/* Prevent bit 3 (CURSOR_FLAG_OPLOG_REPLAY) and bit 6 (CURSOR_FLAG_EXHAUST) from
* being set. The first because it's an internal flag, and the second because
* the driver can't handle this at the moment. */
if (bit == 3 || bit == 6) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The CURSOR_FLAG_OPLOG_REPLAY(3) and CURSOR_FLAG_EXHAUST(6) flags are not supported.");
return;
}
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1 << bit, set);
}
/* }}} */
/* {{{ MongoCursor::tailable(bool flag)
*/
PHP_METHOD(MongoCursor, tailable)
{
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, CURSOR_FLAG_TAILABLE, -1);
}
/* }}} */
/* {{{ MongoCursor::slaveOkay(bool flag)
*/
PHP_METHOD(MongoCursor, slaveOkay)
{
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, CURSOR_FLAG_SLAVE_OKAY, -1);
}
/* }}} */
/* {{{ MongoCursor::immortal(bool flag)
*/
PHP_METHOD(MongoCursor, immortal)
{
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, CURSOR_FLAG_NO_CURSOR_TO, -1);
}
/* }}} */
/* {{{ MongoCursor::awaitData(bool flag)
*/
PHP_METHOD(MongoCursor, awaitData)
{
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, CURSOR_FLAG_AWAIT_DATA, -1);
}
/* }}} */
/* {{{ MongoCursor::partial(bool flag)
*/
PHP_METHOD(MongoCursor, partial)
{
set_cursor_flag(INTERNAL_FUNCTION_PARAM_PASSTHRU, CURSOR_FLAG_PARTIAL, -1);
}
/* }}} */
/* }}} */
/* {{{ MongoCursor::timeout
*/
PHP_METHOD(MongoCursor, timeout) {
long timeout;
mongo_cursor *cursor;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &timeout) == FAILURE) {
return;
}
PHP_MONGO_GET_CURSOR(getThis());
cursor->timeout = timeout;
RETURN_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::addOption
*/
PHP_METHOD(MongoCursor, addOption) {
char *key;
int key_len;
zval *query, *value;
mongo_cursor *cursor;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &key, &key_len, &value) == FAILURE) {
return;
}
cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
if (cursor->started_iterating) {
mongo_cursor_throw(cursor->server, 0 TSRMLS_CC, "cannot modify cursor after beginning iteration");
return;
}
make_special(cursor);
query = cursor->query;
add_assoc_zval(query, key, value);
zval_add_ref(&value);
RETURN_ZVAL(getThis(), 1, 0);
}
/* }}} */
/* {{{ MongoCursor::snapshot
*/
PHP_METHOD(MongoCursor, snapshot) {
zval *snapshot, *yes;
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
MAKE_STD_ZVAL(snapshot);
ZVAL_STRING(snapshot, "$snapshot", 1);
MAKE_STD_ZVAL(yes);
ZVAL_TRUE(yes);
MONGO_METHOD2(MongoCursor, addOption, return_value, getThis(), snapshot, yes);
zval_ptr_dtor(&snapshot);
zval_ptr_dtor(&yes);
}
/* }}} */
/* {{{ MongoCursor->sort(array fields)
*/
PHP_METHOD(MongoCursor, sort) {
zval *orderby, *fields;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &fields) == FAILURE) {
return;
}
if (IS_SCALAR_P(fields)) {
zend_error(E_WARNING, "MongoCursor::sort() expects parameter 1 to be an array");
return;
}
MAKE_STD_ZVAL(orderby);
ZVAL_STRING(orderby, "$orderby", 1);
MONGO_METHOD2(MongoCursor, addOption, return_value, getThis(), orderby, fields);
zval_ptr_dtor(&orderby);
}
/* }}} */
/* {{{ MongoCursor->hint
*/
PHP_METHOD(MongoCursor, hint) {
zval *hint, *fields;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &fields) == FAILURE) {
return;
}
if (IS_SCALAR_P(fields)) {
zend_error(E_WARNING, "MongoCursor::hint() expects parameter 1 to be an array or object");
return;
}
MAKE_STD_ZVAL(hint);
ZVAL_STRING(hint, "$hint", 1);
MONGO_METHOD2(MongoCursor, addOption, return_value, getThis(), hint, fields);
zval_ptr_dtor(&hint);
}
/* }}} */
/* {{{ MongoCursor->getCursorInfo: Return information about the current query (by @crodas)
*/
PHP_METHOD(MongoCursor, info)
{
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
array_init(return_value);
add_assoc_string(return_value, "ns", cursor->ns, 1);
add_assoc_long(return_value, "limit", cursor->limit);
add_assoc_long(return_value, "batchSize", cursor->batch_size);
add_assoc_long(return_value, "skip", cursor->skip);
add_assoc_long(return_value, "flags", cursor->opts);
if (cursor->query) {
add_assoc_zval(return_value, "query", cursor->query);
zval_add_ref(&cursor->query);
} else {
add_assoc_null(return_value, "query");
}
if (cursor->fields) {
add_assoc_zval(return_value, "fields", cursor->fields);
zval_add_ref(&cursor->fields);
} else {
add_assoc_null(return_value, "fields");
}
add_assoc_bool(return_value, "started_iterating", cursor->started_iterating);
if (cursor->started_iterating) {
add_assoc_long(return_value, "id", (long)cursor->cursor_id);
add_assoc_long(return_value, "at", cursor->at);
add_assoc_long(return_value, "numReturned", cursor->num);
add_assoc_string(return_value, "server", cursor->server->label, 1);
}
}
/* }}} */
/* {{{ MongoCursor->explain
*/
PHP_METHOD(MongoCursor, explain) {
int temp_limit;
zval *explain, *yes, *temp = 0;
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
MONGO_METHOD(MongoCursor, reset, return_value, getThis());
// make explain use a hard limit
temp_limit = cursor->limit;
if (cursor->limit > 0) {
cursor->limit *= -1;
}
MAKE_STD_ZVAL(explain);
ZVAL_STRING(explain, "$explain", 1);
MAKE_STD_ZVAL(yes);
ZVAL_TRUE(yes);
MONGO_METHOD2(MongoCursor, addOption, return_value, getThis(), explain, yes);
zval_ptr_dtor(&explain);
zval_ptr_dtor(&yes);
MONGO_METHOD(MongoCursor, getNext, return_value, getThis());
// reset cursor to original state
cursor->limit = temp_limit;
zend_hash_del(HASH_P(cursor->query), "$explain", strlen("$explain")+1);
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
MONGO_METHOD(MongoCursor, reset, temp, getThis());
zval_ptr_dtor(&temp);
}
/* }}} */
/* {{{ MongoCursor->doQuery
*/
PHP_METHOD(MongoCursor, doQuery) {
mongo_cursor *cursor;
PHP_MONGO_GET_CURSOR(getThis());
do {
MONGO_METHOD(MongoCursor, reset, return_value, getThis());
if (mongo_cursor__do_query(getThis(), return_value TSRMLS_CC) == SUCCESS ||
EG(exception)) {
return;
}
} while (mongo_cursor__should_retry(cursor));
if (strcmp(".$cmd", cursor->ns+(strlen(cursor->ns)-5)) == 0) {
mongo_cursor_throw(cursor->server, 19 TSRMLS_CC, "couldn't send command");
return;
}
mongo_cursor_throw(cursor->server, 19 TSRMLS_CC, "max number of retries exhausted, couldn't send query");
}
int mongo_cursor__do_query(zval *this_ptr, zval *return_value TSRMLS_DC) {
mongo_cursor *cursor;
buffer buf;
zval *errmsg;
cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
if (!cursor) {
zend_throw_exception(mongo_ce_Exception,
"The MongoCursor object has not been correctly initialized by its constructor",
0 TSRMLS_CC);
return FAILURE;
}
CREATE_BUF(buf, INITIAL_BUF_SIZE);
if (php_mongo_write_query(&buf, cursor TSRMLS_CC) == FAILURE) {
efree(buf.start);
return FAILURE;
}
MAKE_STD_ZVAL(errmsg);
ZVAL_NULL(errmsg);
// If slave_okay is set, read from a slave.
if ((cursor->link->rs && cursor->opts & CURSOR_FLAG_SLAVE_OKAY &&
(cursor->server = mongo_util_link_get_slave_socket(cursor->link, errmsg TSRMLS_CC)) == 0)) {
// ignore errors and reset errmsg
zval_ptr_dtor(&errmsg);
MAKE_STD_ZVAL(errmsg);
ZVAL_NULL(errmsg);
}
// if getting the slave didn't work (or we're not using a rs), just get master socket
if (cursor->server == 0 &&
(cursor->server = mongo_util_link_get_socket(cursor->link, errmsg TSRMLS_CC)) == 0) {
efree(buf.start);
// if we couldn't connect to the master or the slave
if (cursor->opts & CURSOR_FLAG_SLAVE_OKAY) {
mongo_cursor_throw(0, 14 TSRMLS_CC, "couldn't get a connection to any server");
}
else {
mongo_cursor_throw(0, 14 TSRMLS_CC, Z_STRVAL_P(errmsg));
}
zval_ptr_dtor(&errmsg);
return FAILURE;
}
if (mongo_say(cursor->server, &buf, errmsg TSRMLS_CC) == FAILURE) {
if (Z_TYPE_P(errmsg) == IS_STRING) {
mongo_cursor_throw(cursor->server, 14 TSRMLS_CC, "couldn't send query: %s", Z_STRVAL_P(errmsg));
}
else {
mongo_cursor_throw(cursor->server, 14 TSRMLS_CC, "couldn't send query");
}
efree(buf.start);
zval_ptr_dtor(&errmsg);
return mongo_util_cursor_failed(cursor TSRMLS_CC);
}
efree(buf.start);
if (php_mongo_get_reply(cursor, errmsg TSRMLS_CC) == FAILURE) {
zval_ptr_dtor(&errmsg);
return mongo_util_cursor_failed(cursor TSRMLS_CC);
}
zval_ptr_dtor(&errmsg);
/* we've got something to kill, make a note */
if (cursor->cursor_id != 0) {
php_mongo_create_le(cursor, "cursor_list" TSRMLS_CC);
}
return SUCCESS;
}
/* }}} */
int mongo_util_cursor_failed(mongo_cursor *cursor TSRMLS_DC) {
mongo_server *old = cursor->server;
// kill cursor so that the server stops and the new connection doesn't try
// to kill something it doesn't own
mongo_util_cursor_reset(cursor TSRMLS_CC);
// reset sets cursor->server to 0, so we use "old" here
mongo_util_link_failed(cursor->link, old TSRMLS_CC);
return FAILURE;
}
// ITERATOR FUNCTIONS
/* {{{ MongoCursor->current
*/
PHP_METHOD(MongoCursor, current) {
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
if (cursor->current) {
RETURN_ZVAL(cursor->current, 1, 0);
}
else {
RETURN_NULL();
}
}
/* }}} */
/* {{{ MongoCursor->key
*/
PHP_METHOD(MongoCursor, key) {
zval **id;
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
if (!cursor->current) {
RETURN_NULL();
}
if (cursor->current &&
Z_TYPE_P(cursor->current) == IS_ARRAY &&
zend_hash_find(HASH_P(cursor->current), "_id", 4, (void**)&id) == SUCCESS) {
if (Z_TYPE_PP(id) == IS_OBJECT) {
#if ZEND_MODULE_API_NO >= 20060613
zend_std_cast_object_tostring(*id, return_value, IS_STRING TSRMLS_CC);
#else
zend_std_cast_object_tostring(*id, return_value, IS_STRING, 0 TSRMLS_CC);
#endif /* ZEND_MODULE_API_NO >= 20060613 */
}
else {
RETVAL_ZVAL(*id, 1, 0);
convert_to_string(return_value);
}
}
else {
RETURN_LONG(cursor->at - 1);
}
}
/* }}} */
int mongo_cursor__should_retry(mongo_cursor *cursor) {
int microseconds = 50000, slots = 0, wait_us = 0;
// never retry commands
if (cursor->retry >= 5 ||
strcmp(".$cmd", cursor->ns+(strlen(cursor->ns)-5)) == 0) {
return 0;
}
slots = (int)pow(2.0, cursor->retry++);
wait_us = (rand() % slots) * microseconds;
#ifdef WIN32
// windows sleep takes milliseconds
Sleep(wait_us/1000);
#else
{
// usleep is deprecated
struct timespec wait;
wait.tv_sec = wait_us / 1000000;
wait.tv_nsec = (wait_us % 1000000) * 1000;
nanosleep(&wait, 0);
}
#endif
return 1;
}
/* {{{ MongoCursor->next
*/
PHP_METHOD(MongoCursor, next) {
zval has_next;
mongo_cursor *cursor;
PHP_MONGO_GET_CURSOR(getThis());
if (!cursor->started_iterating) {
MONGO_METHOD(MongoCursor, doQuery, return_value, getThis());
if (EG(exception)) {
return;
}
cursor->started_iterating = 1;
}
// destroy old current
if (cursor->current) {
zval_ptr_dtor(&cursor->current);
cursor->current = 0;
}
// check for results
MONGO_METHOD(MongoCursor, hasNext, &has_next, getThis());
if (EG(exception)) {
return;
}
if (!Z_BVAL(has_next)) {
// we're out of results
RETURN_NULL();
}
// we got more results
if (cursor->at < cursor->num) {
zval **err = 0, **ok = 0;
MAKE_STD_ZVAL(cursor->current);
array_init(cursor->current);
cursor->buf.pos = bson_to_zval((char*)cursor->buf.pos, Z_ARRVAL_P(cursor->current) TSRMLS_CC);
if (EG(exception)) {
zval_ptr_dtor(&cursor->current);
cursor->current = 0;
return;
}
// increment cursor position
cursor->at++;
// check for $err
if (zend_hash_find(Z_ARRVAL_P(cursor->current), "$err", strlen("$err")+1, (void**)&err) == SUCCESS ||
// getLastError can return an error here
(zend_hash_find(Z_ARRVAL_P(cursor->current), "err", strlen("err")+1, (void**)&err) == SUCCESS &&
Z_TYPE_PP(err) == IS_STRING)) {
zval **code_z, *exception;
// default error code
int code = 4;
if (zend_hash_find(Z_ARRVAL_P(cursor->current), "code", strlen("code")+1, (void**)&code_z) == SUCCESS) {
// check for not master
if (Z_TYPE_PP(code_z) == IS_LONG) {
code = Z_LVAL_PP(code_z);
}
else if (Z_TYPE_PP(code_z) == IS_DOUBLE) {
code = (int)Z_DVAL_PP(code_z);
}
// else code == 4
// this shouldn't be necessary after 1.7.* is standard, it forces
// failover in case the master steps down.
// not master: 10107
// not master and slaveok=false (more recent): 13435
// not master or secondary: 13436
if (cursor->link->rs && (code == 10107 || code == 13435 || code == 13436 || code == 10058)) {
mongo_util_link_master_failed(cursor->link TSRMLS_CC);
}
}
exception = mongo_cursor_throw(cursor->server, code TSRMLS_CC, Z_STRVAL_PP(err));
zend_update_property(mongo_ce_CursorException, exception, "doc", strlen("doc"), cursor->current TSRMLS_CC);
zval_ptr_dtor(&cursor->current);
cursor->current = 0;
RETURN_FALSE;
}
}
RETURN_NULL();
}
/* }}} */
/* {{{ MongoCursor->rewind
*/
PHP_METHOD(MongoCursor, rewind) {
MONGO_METHOD(MongoCursor, reset, return_value, getThis());
MONGO_METHOD(MongoCursor, next, return_value, getThis());
}
/* }}} */
/* {{{ MongoCursor->valid
*/
PHP_METHOD(MongoCursor, valid) {
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
RETURN_BOOL(cursor->current);
}
/* }}} */
/* {{{ MongoCursor->reset
*/
PHP_METHOD(MongoCursor, reset) {
mongo_cursor *cursor = (mongo_cursor*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(cursor->link, MongoCursor);
mongo_util_cursor_reset(cursor TSRMLS_CC);
}
void mongo_util_cursor_reset(mongo_cursor *cursor TSRMLS_DC) {
cursor->buf.pos = cursor->buf.start;
if (cursor->current) {
zval_ptr_dtor(&cursor->current);
}
if (cursor->cursor_id != 0) {
mongo_cursor_free_le(cursor, MONGO_CURSOR TSRMLS_CC);
cursor->cursor_id = 0;
}
cursor->started_iterating = 0;
cursor->current = 0;
cursor->at = 0;
cursor->num = 0;
cursor->server = 0;
}
/* }}} */
PHP_METHOD(MongoCursor, count) {
zval *db_z, *coll, *query;
mongo_cursor *cursor;
mongo_collection *c;
mongo_db *db;
zend_bool all = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &all) == FAILURE) {
return;
}
PHP_MONGO_GET_CURSOR(getThis());
// fake a MongoDB object
MAKE_STD_ZVAL(db_z);
object_init_ex(db_z, mongo_ce_DB);
db = (mongo_db*)zend_object_store_get_object(db_z TSRMLS_CC);
db->link = cursor->resource;
MAKE_STD_ZVAL(db->name);
ZVAL_STRING(db->name, estrndup(cursor->ns, strchr(cursor->ns, '.') - cursor->ns), 0);
// fake a MongoCollection object
MAKE_STD_ZVAL(coll);
object_init_ex(coll, mongo_ce_Collection);
c = (mongo_collection*)zend_object_store_get_object(coll TSRMLS_CC);
MAKE_STD_ZVAL(c->ns);
ZVAL_STRING(c->ns, estrdup(cursor->ns), 0);
MAKE_STD_ZVAL(c->name);
ZVAL_STRING(c->name, estrdup(cursor->ns + (strchr(cursor->ns, '.') - cursor->ns) + 1), 0);
c->parent = db_z;
if (cursor->query) {
zval **inner_query = 0;
if (!cursor->special) {
query = cursor->query;
zval_add_ref(&query);
}
else if (zend_hash_find(HASH_P(cursor->query), "$query", strlen("$query")+1, (void**)&inner_query) == SUCCESS) {
query = *inner_query;
zval_add_ref(&query);
}
}
else {
MAKE_STD_ZVAL(query);
array_init(query);
}
if (all) {
zval *limit_z, *skip_z;
MAKE_STD_ZVAL(limit_z);
MAKE_STD_ZVAL(skip_z);
ZVAL_LONG(limit_z, cursor->limit);
ZVAL_LONG(skip_z, cursor->skip);
MONGO_METHOD3(MongoCollection, count, return_value, coll, query, limit_z, skip_z);
zval_ptr_dtor(&limit_z);
zval_ptr_dtor(&skip_z);
}
else {
MONGO_METHOD1(MongoCollection, count, return_value, coll, query);
}
zval_ptr_dtor(&query);
c->parent = 0;
zend_objects_store_del_ref(coll TSRMLS_CC);
zval_ptr_dtor(&coll);
db->link = 0;
zend_objects_store_del_ref(db_z TSRMLS_CC);
zval_ptr_dtor(&db_z);
}
ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, ZEND_RETURN_VALUE, 2)
ZEND_ARG_OBJ_INFO(0, connection, Mongo, 0)
ZEND_ARG_INFO(0, database_and_collection_name)
ZEND_ARG_ARRAY_INFO(0, query, 0)
ZEND_ARG_INFO(0, array_of_fields_OR_object)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_no_parameters, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_limit, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, number)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_batchsize, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, number)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_skip, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, number)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_fields, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, fields, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_add_option, 0, ZEND_RETURN_VALUE, 2)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_sort, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, fields, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_hint, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, keyPattern, 0)
ZEND_END_ARG_INFO()
/* {{{ Cursor flags */
ZEND_BEGIN_ARG_INFO_EX(arginfo_set_flag, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, bit)
ZEND_ARG_INFO(0, set)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_tailable, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, tail)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_slave_okay, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, okay)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_immortal, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, liveForever)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_await_data, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, wait)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_partial, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, okay)
ZEND_END_ARG_INFO()
/* }}} */
ZEND_BEGIN_ARG_INFO_EX(arginfo_timeout, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, milliseconds)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_count, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, foundOnly)
ZEND_END_ARG_INFO()
static zend_function_entry MongoCursor_methods[] = {
PHP_ME(MongoCursor, __construct, arginfo___construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, hasNext, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, getNext, arginfo_no_parameters, ZEND_ACC_PUBLIC)
/* options */
PHP_ME(MongoCursor, limit, arginfo_limit, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, batchSize, arginfo_batchsize, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, skip, arginfo_skip, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, fields, arginfo_fields, ZEND_ACC_PUBLIC)
/* meta options */
PHP_ME(MongoCursor, addOption, arginfo_add_option, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, snapshot, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, sort, arginfo_sort, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, hint, arginfo_hint, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, explain, arginfo_no_parameters, ZEND_ACC_PUBLIC)
/* flags */
PHP_ME(MongoCursor, setFlag, arginfo_set_flag, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, slaveOkay, arginfo_slave_okay, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, tailable, arginfo_tailable, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, immortal, arginfo_immortal, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, awaitData, arginfo_await_data, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, partial, arginfo_partial, ZEND_ACC_PUBLIC)
/* query */
PHP_ME(MongoCursor, timeout, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, doQuery, arginfo_no_parameters, ZEND_ACC_PROTECTED)
PHP_ME(MongoCursor, info, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, dead, arginfo_no_parameters, ZEND_ACC_PUBLIC)
/* iterator funcs */
PHP_ME(MongoCursor, current, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, key, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, next, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, rewind, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, valid, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCursor, reset, arginfo_no_parameters, ZEND_ACC_PUBLIC)
/* stand-alones */
PHP_ME(MongoCursor, count, arginfo_count, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
PHP_METHOD(MongoCursorException, getHost) {
zval *h;
h = zend_read_property(mongo_ce_CursorException, getThis(), "host", strlen("host"), NOISY TSRMLS_CC);
RETURN_ZVAL(h, 1, 0);
}
static zend_function_entry cursor_exception_methods[] = {
PHP_ME(MongoCursorException, getHost, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
void mongo_init_CursorExceptions(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MongoCursorException", cursor_exception_methods);
mongo_ce_CursorException = zend_register_internal_class_ex(&ce, mongo_ce_Exception, NULL TSRMLS_CC);
zend_declare_property_null(mongo_ce_CursorException, "host", strlen("host"), ZEND_ACC_PRIVATE TSRMLS_CC);
zend_declare_property_long(mongo_ce_CursorException, "fd", strlen("fd"), 0, ZEND_ACC_PRIVATE TSRMLS_CC);
}
zval* mongo_cursor_throw(mongo_server *server, int code TSRMLS_DC, char *format, ...) {
zval *e;
va_list arg;
if (EG(exception)) {
return EG(exception);
}
va_start(arg, format);
e = zend_throw_exception_ex(mongo_ce_CursorException, code TSRMLS_CC, format, arg);
va_end(arg);
if (server) {
zend_update_property_string(mongo_ce_CursorException, e, "host", strlen("host"), server->label TSRMLS_CC);
zend_update_property_long(mongo_ce_CursorException, e, "fd", strlen("fd"), server->socket TSRMLS_CC);
}
return e;
}
void mongo_cursor_free_le(void *val, int type TSRMLS_DC) {
zend_rsrc_list_entry *le;
LOCK(cursor);
/*
* This should work if le->ptr is null or non-null
*/
if (zend_hash_find(&EG(persistent_list), "cursor_list", strlen("cursor_list") + 1, (void**)&le) == SUCCESS) {
cursor_node *current;
current = le->ptr;
while (current) {
cursor_node *next = current->next;
if (type == MONGO_SERVER) {
mongo_server *server = (mongo_server*)val;
if (server != 0 && current->socket == server->socket) {
if (!server->connected) {
php_mongo_free_cursor_node(current, le);
}
else {
kill_cursor(current, le TSRMLS_CC);
}
// keep going, free all cursor for this connection
}
}
else if (type == MONGO_CURSOR) {
mongo_cursor *cursor = (mongo_cursor*)val;
if (current->cursor_id == cursor->cursor_id &&
cursor->server != 0 &&
current->socket == cursor->server->socket) {
// If the cursor_id is 0, the db is out of results anyway
// If the connection is not connected, just return
if (current->cursor_id == 0 || !cursor->server->connected) {
php_mongo_free_cursor_node(current, le);
}
else {
kill_cursor(current, le TSRMLS_CC);
/*
* if the connection is closed before the cursor is destroyed, the cursor
* might try to fetch more results with disasterous consequences. Thus, the
* cursor_id is set to 0, so no more results will be fetched.
*
* this might not be the most elegant solution, since you could fetch 100
* results, get the first one, close the connection, get 99 more, and suddenly
* not be able to get any more. Not sure if there's a better one, though. I
* guess the user can call dead() on the cursor.
*/
cursor->cursor_id = 0;
}
// only one cursor to be freed
break;
}
}
current = next;
}
}
UNLOCK(cursor);
}
int php_mongo_create_le(mongo_cursor *cursor, char *name TSRMLS_DC) {
zend_rsrc_list_entry *le;
cursor_node *new_node;
LOCK(cursor);
new_node = (cursor_node*)pemalloc(sizeof(cursor_node), 1);
new_node->cursor_id = cursor->cursor_id;
if (cursor->server) {
new_node->socket = cursor->server->socket;
}
else {
new_node->socket = 0;
}
new_node->next = new_node->prev = 0;
/*
* 3 options:
* - le doesn't exist
* - le exists and is null
* - le exists and has elements
* In case 1 & 2, we want to create a new le ptr, otherwise we want to append
* to the existing ptr.
*/
if (zend_hash_find(&EG(persistent_list), name, strlen(name)+1, (void**)&le) == SUCCESS) {
cursor_node *current = le->ptr;
cursor_node *prev = 0;
if (current == 0) {
le->ptr = new_node;
UNLOCK(cursor);
return 0;
}
do {
/*
* if we find the current cursor in the cursor list, we don't need another
* dtor for it so unlock the mutex & return.
*/
if (current->cursor_id == cursor->cursor_id &&
current->socket == cursor->server->socket) {
pefree(new_node, 1);
UNLOCK(cursor);
return 0;
}
prev = current;
current = current->next;
}
while (current);
/*
* we didn't find the cursor. add it to the list. (prev is pointing to the
* tail of the list, current is pointing to null.
*/
prev->next = new_node;
new_node->prev = prev;
}
else {
zend_rsrc_list_entry new_le;
new_le.ptr = new_node;
new_le.type = le_cursor_list;
new_le.refcount = 1;
zend_hash_add(&EG(persistent_list), name, strlen(name)+1, &new_le, sizeof(zend_rsrc_list_entry), NULL);
}
UNLOCK(cursor);
return 0;
}
static int cursor_list_pfree_helper(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
LOCK(cursor);
{
cursor_node *node = (cursor_node*)rsrc->ptr;
if (!node) {
UNLOCK(cursor);
return 0;
}
while (node->next) {
cursor_node *temp = node;
node = node->next;
pefree(temp, 1);
}
pefree(node, 1);
}
UNLOCK(cursor);
return 0;
}
void php_mongo_cursor_list_pfree(zend_rsrc_list_entry *rsrc TSRMLS_DC) {
cursor_list_pfree_helper(rsrc TSRMLS_CC);
}
void php_mongo_free_cursor_node(cursor_node *node, zend_rsrc_list_entry *le) {
/*
* [node1][<->][NODE2][<->][node3]
* [node1][->][node3]
* [node1][<->][node3]
*
* [node1][<->][NODE2]
* [node1]
*/
if (node->prev) {
node->prev->next = node->next;
if (node->next) {
node->next->prev = node->prev;
}
}
/*
* [NODE2][<->][node3]
* le->ptr = node3
* [node3]
*
* [NODE2]
* le->ptr = 0
*/
else {
le->ptr = node->next;
if (node->next) {
node->next->prev = 0;
}
}
pefree(node, 1);
}
// tell db to destroy its cursor
static void kill_cursor(cursor_node *node, zend_rsrc_list_entry *le TSRMLS_DC) {
char quickbuf[128];
buffer buf;
zval temp;
/*
* If the cursor_id is 0, the db is out of results anyway.
*/
if (node->cursor_id == 0) {
php_mongo_free_cursor_node(node, le);
return;
}
buf.pos = quickbuf;
buf.start = buf.pos;
buf.end = buf.start + 128;
php_mongo_write_kill_cursors(&buf, node->cursor_id TSRMLS_CC);
Z_TYPE(temp) = IS_NULL;
_mongo_say(node->socket, &buf, &temp TSRMLS_CC);
if (Z_TYPE(temp) == IS_STRING) {
efree(Z_STRVAL(temp));
Z_TYPE(temp) = IS_NULL;
}
// free this cursor/link pair
php_mongo_free_cursor_node(node, le);
}
static zend_object_value php_mongo_cursor_new(zend_class_entry *class_type TSRMLS_DC) {
php_mongo_obj_new(mongo_cursor);
}
void php_mongo_cursor_free(void *object TSRMLS_DC) {
mongo_cursor *cursor = (mongo_cursor*)object;
if (cursor) {
if (cursor->cursor_id != 0) {
mongo_cursor_free_le(cursor, MONGO_CURSOR TSRMLS_CC);
}
if (cursor->current) zval_ptr_dtor(&cursor->current);
if (cursor->query) zval_ptr_dtor(&cursor->query);
if (cursor->fields) zval_ptr_dtor(&cursor->fields);
if (cursor->buf.start) efree(cursor->buf.start);
if (cursor->ns) efree(cursor->ns);
if (cursor->resource) zval_ptr_dtor(&cursor->resource);
zend_object_std_dtor(&cursor->std TSRMLS_CC);
efree(cursor);
}
}
void mongo_init_MongoCursor(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MongoCursor", MongoCursor_methods);
ce.create_object = php_mongo_cursor_new;
mongo_ce_Cursor = zend_register_internal_class(&ce TSRMLS_CC);
zend_class_implements(mongo_ce_Cursor TSRMLS_CC, 1, zend_ce_iterator);
zend_declare_property_bool(mongo_ce_Cursor, "slaveOkay", strlen("slaveOkay"), 0, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC TSRMLS_CC);
zend_declare_property_long(mongo_ce_Cursor, "timeout", strlen("timeout"), 30000L, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC TSRMLS_CC);
}
Jump to Line
Something went wrong with that request. Please try again.