Skip to content

Commit a0be179

Browse files
committed
PHPC-1691: Provide Iterator implementation for cursors
1 parent df547c6 commit a0be179

9 files changed

+130
-151
lines changed

phongo_compat.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,24 @@
149149
#error Unsupported architecture (integers are neither 32-bit nor 64-bit)
150150
#endif /* SIZEOF_ZEND_LONG */
151151

152+
#if PHP_VERSION_ID < 70300
153+
#define ZVAL_COPY_DEREF(z, v) \
154+
do { \
155+
zval* _z3 = (v); \
156+
if (Z_OPT_REFCOUNTED_P(_z3)) { \
157+
if (UNEXPECTED(Z_OPT_ISREF_P(_z3))) { \
158+
_z3 = Z_REFVAL_P(_z3); \
159+
if (Z_OPT_REFCOUNTED_P(_z3)) { \
160+
Z_ADDREF_P(_z3); \
161+
} \
162+
} else { \
163+
Z_ADDREF_P(_z3); \
164+
} \
165+
} \
166+
ZVAL_COPY_VALUE(z, _z3); \
167+
} while (0)
168+
#endif /* PHP_VERSION_ID < 70300 */
169+
152170
void phongo_add_exception_prop(const char* prop, int prop_len, zval* value);
153171
zend_bool php_phongo_zend_hash_apply_protection_begin(HashTable* ht);
154172
zend_bool php_phongo_zend_hash_apply_protection_end(HashTable* ht);

php_phongo.c

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -313,13 +313,12 @@ static void phongo_cursor_init(zval* return_value, mongoc_client_t* client, mong
313313

314314
object_init_ex(return_value, php_phongo_cursor_ce);
315315

316-
intern = Z_CURSOR_OBJ_P(return_value);
317-
intern->cursor = cursor;
318-
intern->server_id = mongoc_cursor_get_hint(cursor);
319-
intern->client = client;
320-
intern->advanced = false;
321-
intern->got_iterator = false;
322-
intern->current = 0;
316+
intern = Z_CURSOR_OBJ_P(return_value);
317+
intern->cursor = cursor;
318+
intern->server_id = mongoc_cursor_get_hint(cursor);
319+
intern->client = client;
320+
intern->advanced = false;
321+
intern->current = 0;
323322

324323
if (readPreference) {
325324
ZVAL_ZVAL(&intern->read_preference, readPreference, 1, 0);

php_phongo_classes.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,6 @@ static inline php_phongo_commandsucceededevent_t* php_commandsucceededevent_fetc
212212
#define Z_OBJ_COMMANDSTARTEDEVENT(zo) (php_commandstartedevent_fetch_object(zo))
213213
#define Z_OBJ_COMMANDSUCCEEDEDEVENT(zo) (php_commandsucceededevent_fetch_object(zo))
214214

215-
typedef struct {
216-
zend_object_iterator intern;
217-
php_phongo_cursor_t* cursor;
218-
} php_phongo_cursor_iterator;
219-
220215
extern zend_class_entry* php_phongo_clientencryption_ce;
221216
extern zend_class_entry* php_phongo_command_ce;
222217
extern zend_class_entry* php_phongo_cursor_ce;

php_phongo_structs.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ typedef struct {
5252
uint32_t server_id;
5353
bool advanced;
5454
php_phongo_bson_state visitor_data;
55-
bool got_iterator;
5655
long current;
5756
char* database;
5857
char* collection;

src/MongoDB/Cursor.c

Lines changed: 84 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -53,45 +53,28 @@ static void php_phongo_cursor_free_current(php_phongo_cursor_t* cursor) /* {{{ *
5353
} /* }}} */
5454

5555
/* {{{ MongoDB\Driver\Cursor iterator handlers */
56-
static void php_phongo_cursor_iterator_dtor(zend_object_iterator* iter) /* {{{ */
56+
static int php_phongo_cursor_valid(php_phongo_cursor_t* cursor) /* {{{ */
5757
{
58-
php_phongo_cursor_iterator* cursor_it = (php_phongo_cursor_iterator*) iter;
59-
60-
if (!Z_ISUNDEF(cursor_it->intern.data)) {
61-
zval_ptr_dtor(&cursor_it->intern.data);
62-
}
63-
} /* }}} */
64-
65-
static int php_phongo_cursor_iterator_valid(zend_object_iterator* iter) /* {{{ */
66-
{
67-
php_phongo_cursor_t* cursor = ((php_phongo_cursor_iterator*) iter)->cursor;
68-
6958
if (!Z_ISUNDEF(cursor->visitor_data.zchild)) {
7059
return SUCCESS;
7160
}
7261

7362
return FAILURE;
7463
} /* }}} */
7564

76-
static void php_phongo_cursor_iterator_get_current_key(zend_object_iterator* iter, zval* key) /* {{{ */
65+
static void php_phongo_cursor_get_current_key(php_phongo_cursor_t* cursor, zval* key) /* {{{ */
7766
{
78-
php_phongo_cursor_t* cursor = ((php_phongo_cursor_iterator*) iter)->cursor;
79-
8067
ZVAL_LONG(key, cursor->current);
8168
} /* }}} */
8269

83-
static zval* php_phongo_cursor_iterator_get_current_data(zend_object_iterator* iter) /* {{{ */
70+
static zval* php_phongo_cursor_get_current_data(php_phongo_cursor_t* cursor) /* {{{ */
8471
{
85-
php_phongo_cursor_t* cursor = ((php_phongo_cursor_iterator*) iter)->cursor;
86-
8772
return &cursor->visitor_data.zchild;
8873
} /* }}} */
8974

90-
static void php_phongo_cursor_iterator_move_forward(zend_object_iterator* iter) /* {{{ */
75+
static void php_phongo_cursor_move_forward(php_phongo_cursor_t* cursor) /* {{{ */
9176
{
92-
php_phongo_cursor_iterator* cursor_it = (php_phongo_cursor_iterator*) iter;
93-
php_phongo_cursor_t* cursor = cursor_it->cursor;
94-
const bson_t* doc;
77+
const bson_t* doc;
9578

9679
php_phongo_cursor_free_current(cursor);
9780

@@ -124,11 +107,9 @@ static void php_phongo_cursor_iterator_move_forward(zend_object_iterator* iter)
124107
php_phongo_cursor_free_session_if_exhausted(cursor);
125108
} /* }}} */
126109

127-
static void php_phongo_cursor_iterator_rewind(zend_object_iterator* iter) /* {{{ */
110+
static void php_phongo_cursor_rewind(php_phongo_cursor_t* cursor) /* {{{ */
128111
{
129-
php_phongo_cursor_iterator* cursor_it = (php_phongo_cursor_iterator*) iter;
130-
php_phongo_cursor_t* cursor = cursor_it->cursor;
131-
const bson_t* doc;
112+
const bson_t* doc;
132113

133114
/* If the cursor was never advanced (e.g. command cursor), do so now */
134115
if (!cursor->advanced) {
@@ -159,45 +140,6 @@ static void php_phongo_cursor_iterator_rewind(zend_object_iterator* iter) /* {{{
159140

160141
php_phongo_cursor_free_session_if_exhausted(cursor);
161142
} /* }}} */
162-
163-
static zend_object_iterator_funcs php_phongo_cursor_iterator_funcs = {
164-
php_phongo_cursor_iterator_dtor,
165-
php_phongo_cursor_iterator_valid,
166-
php_phongo_cursor_iterator_get_current_data,
167-
php_phongo_cursor_iterator_get_current_key,
168-
php_phongo_cursor_iterator_move_forward,
169-
php_phongo_cursor_iterator_rewind,
170-
NULL /* invalidate_current is not used */
171-
};
172-
173-
static zend_object_iterator* php_phongo_cursor_get_iterator(zend_class_entry* ce, zval* object, int by_ref) /* {{{ */
174-
{
175-
php_phongo_cursor_iterator* cursor_it = NULL;
176-
php_phongo_cursor_t* cursor = Z_CURSOR_OBJ_P(object);
177-
178-
if (by_ref) {
179-
zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
180-
}
181-
182-
if (cursor->got_iterator) {
183-
phongo_throw_exception(PHONGO_ERROR_LOGIC, "Cursors cannot yield multiple iterators");
184-
return NULL;
185-
}
186-
187-
cursor->got_iterator = true;
188-
189-
cursor_it = ecalloc(1, sizeof(php_phongo_cursor_iterator));
190-
zend_iterator_init(&cursor_it->intern);
191-
192-
ZVAL_COPY(&cursor_it->intern.data, object);
193-
cursor_it->intern.funcs = &php_phongo_cursor_iterator_funcs;
194-
cursor_it->cursor = cursor;
195-
/* cursor_it->current should already be allocated to zero */
196-
197-
php_phongo_cursor_free_current(cursor_it->cursor);
198-
199-
return &cursor_it->intern;
200-
} /* }}} */
201143
/* }}} */
202144

203145
/* {{{ proto void MongoDB\Driver\Cursor::setTypeMap(array $typemap)
@@ -354,10 +296,11 @@ static PHP_METHOD(Cursor, isDead)
354296
RETURN_BOOL(!mongoc_cursor_more(intern->cursor));
355297
} /* }}} */
356298

357-
#if PHP_VERSION_ID >= 80000
358-
static PHP_METHOD(Cursor, getIterator)
299+
PHP_METHOD(Cursor, current)
359300
{
360-
zend_error_handling error_handling;
301+
zend_error_handling error_handling;
302+
php_phongo_cursor_t* intern = Z_CURSOR_OBJ_P(getThis());
303+
zval* data;
361304

362305
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
363306
if (zend_parse_parameters_none() == FAILURE) {
@@ -366,9 +309,72 @@ static PHP_METHOD(Cursor, getIterator)
366309
}
367310
zend_restore_error_handling(&error_handling);
368311

369-
zend_create_internal_iterator_zval(return_value, getThis());
312+
data = php_phongo_cursor_get_current_data(intern);
313+
314+
if (data) {
315+
ZVAL_COPY_DEREF(return_value, data);
316+
}
317+
}
318+
319+
PHP_METHOD(Cursor, key)
320+
{
321+
zend_error_handling error_handling;
322+
php_phongo_cursor_t* intern = Z_CURSOR_OBJ_P(getThis());
323+
324+
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
325+
if (zend_parse_parameters_none() == FAILURE) {
326+
zend_restore_error_handling(&error_handling);
327+
return;
328+
}
329+
zend_restore_error_handling(&error_handling);
330+
331+
php_phongo_cursor_get_current_key(intern, return_value);
332+
}
333+
334+
PHP_METHOD(Cursor, next)
335+
{
336+
zend_error_handling error_handling;
337+
php_phongo_cursor_t* intern = Z_CURSOR_OBJ_P(getThis());
338+
339+
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
340+
if (zend_parse_parameters_none() == FAILURE) {
341+
zend_restore_error_handling(&error_handling);
342+
return;
343+
}
344+
zend_restore_error_handling(&error_handling);
345+
346+
php_phongo_cursor_move_forward(intern);
347+
}
348+
349+
PHP_METHOD(Cursor, valid)
350+
{
351+
zend_error_handling error_handling;
352+
php_phongo_cursor_t* intern = Z_CURSOR_OBJ_P(getThis());
353+
354+
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
355+
if (zend_parse_parameters_none() == FAILURE) {
356+
zend_restore_error_handling(&error_handling);
357+
return;
358+
}
359+
zend_restore_error_handling(&error_handling);
360+
361+
RETURN_BOOL(php_phongo_cursor_valid(intern) == SUCCESS);
362+
}
363+
364+
PHP_METHOD(Cursor, rewind)
365+
{
366+
zend_error_handling error_handling;
367+
php_phongo_cursor_t* intern = Z_CURSOR_OBJ_P(getThis());
368+
369+
zend_replace_error_handling(EH_THROW, phongo_exception_from_phongo_domain(PHONGO_ERROR_INVALID_ARGUMENT), &error_handling);
370+
if (zend_parse_parameters_none() == FAILURE) {
371+
zend_restore_error_handling(&error_handling);
372+
return;
373+
}
374+
zend_restore_error_handling(&error_handling);
375+
376+
php_phongo_cursor_rewind(intern);
370377
}
371-
#endif
372378

373379
/* {{{ MongoDB\Driver\Cursor function entries */
374380
ZEND_BEGIN_ARG_INFO_EX(ai_Cursor_setTypeMap, 0, 0, 1)
@@ -385,9 +391,13 @@ static zend_function_entry php_phongo_cursor_me[] = {
385391
PHP_ME(Cursor, getId, ai_Cursor_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
386392
PHP_ME(Cursor, getServer, ai_Cursor_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
387393
PHP_ME(Cursor, isDead, ai_Cursor_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
388-
#if PHP_VERSION_ID >= 80000
389-
PHP_ME(Cursor, getIterator, ai_Cursor_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
390-
#endif
394+
395+
PHP_ME(Cursor, current, ai_Cursor_void, ZEND_ACC_PUBLIC)
396+
PHP_ME(Cursor, key, ai_Cursor_void, ZEND_ACC_PUBLIC)
397+
PHP_ME(Cursor, next, ai_Cursor_void, ZEND_ACC_PUBLIC)
398+
PHP_ME(Cursor, valid, ai_Cursor_void, ZEND_ACC_PUBLIC)
399+
PHP_ME(Cursor, rewind, ai_Cursor_void, ZEND_ACC_PUBLIC)
400+
391401
ZEND_NAMED_ME(__construct, PHP_FN(MongoDB_disabled___construct), ai_Cursor_void, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL)
392402
ZEND_NAMED_ME(__wakeup, PHP_FN(MongoDB_disabled___wakeup), ai_Cursor_void, ZEND_ACC_PUBLIC | ZEND_ACC_FINAL)
393403
PHP_FE_END
@@ -540,11 +550,8 @@ void php_phongo_cursor_init_ce(INIT_FUNC_ARGS) /* {{{ */
540550
php_phongo_cursor_ce->create_object = php_phongo_cursor_create_object;
541551
PHONGO_CE_FINAL(php_phongo_cursor_ce);
542552
PHONGO_CE_DISABLE_SERIALIZATION(php_phongo_cursor_ce);
543-
php_phongo_cursor_ce->get_iterator = php_phongo_cursor_get_iterator;
544553

545-
#if PHP_VERSION_ID >= 80000
546-
zend_class_implements(php_phongo_cursor_ce, 1, zend_ce_aggregate);
547-
#endif
554+
zend_class_implements(php_phongo_cursor_ce, 1, zend_ce_iterator);
548555
zend_class_implements(php_phongo_cursor_ce, 1, php_phongo_cursor_interface_ce);
549556

550557
memcpy(&php_phongo_handler_cursor, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));

tests/cursor/cursor-get_iterator-002.phpt

Lines changed: 0 additions & 48 deletions
This file was deleted.

tests/cursor/cursor-get_iterator-001.phpt renamed to tests/cursor/cursor-iterator-001.phpt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
MongoDB\Driver\Cursor get_iterator handler does not yield multiple iterators (foreach)
2+
MongoDB\Driver\Cursor does not allow iterating multiple times (foreach)
33
--SKIPIF--
44
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
55
<?php skip_if_not_live(); ?>
@@ -29,13 +29,11 @@ foreach ($cursor as $document) {
2929

3030
echo "\nSecond foreach statement:\n";
3131

32-
try {
32+
echo throws(function () use ($cursor) {
3333
foreach ($cursor as $document) {
3434
echo "FAILED: get_iterator should not yield multiple iterators\n";
3535
}
36-
} catch (MongoDB\Driver\Exception\LogicException $e) {
37-
printf("LogicException: %s\n", $e->getMessage());
38-
}
36+
}, MongoDB\Driver\Exception\LogicException::class), "\n";
3937

4038
?>
4139
===DONE===
@@ -58,5 +56,6 @@ object(stdClass)#%d (1) {
5856
}
5957

6058
Second foreach statement:
61-
LogicException: Cursors cannot yield multiple iterators
59+
OK: Got MongoDB\Driver\Exception\LogicException
60+
Cursors cannot rewind after starting iteration
6261
===DONE===

0 commit comments

Comments
 (0)