Skip to content

Commit 4e7df72

Browse files
committed
PHPC-282: Cursor should not yield multiple iterators
Successive get_iterator handler invocations should throw a LogicException. This avoids situations that might allow the user to iterate over the same cursor multiple times.
1 parent 5bf6e2c commit 4e7df72

File tree

4 files changed

+116
-1
lines changed

4 files changed

+116
-1
lines changed

php_phongo.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1696,18 +1696,26 @@ zend_object_iterator_funcs php_phongo_cursor_iterator_funcs = {
16961696

16971697
zend_object_iterator *php_phongo_cursor_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
16981698
{
1699+
php_phongo_cursor_t *cursor = zend_object_store_get_object(object TSRMLS_CC);
16991700
php_phongo_cursor_iterator *cursor_it = NULL;
17001701

17011702
if (by_ref) {
17021703
zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
17031704
}
17041705

1706+
if (cursor->got_iterator) {
1707+
phongo_throw_exception(PHONGO_ERROR_LOGIC TSRMLS_CC, "Cursors cannot yield multiple iterators");
1708+
return NULL;
1709+
}
1710+
1711+
cursor->got_iterator = 1;
1712+
17051713
cursor_it = ecalloc(1, sizeof(php_phongo_cursor_iterator));
17061714

17071715
Z_ADDREF_P(object);
17081716
cursor_it->intern.data = (void*)object;
17091717
cursor_it->intern.funcs = &php_phongo_cursor_iterator_funcs;
1710-
cursor_it->cursor = (php_phongo_cursor_t *)zend_object_store_get_object(object TSRMLS_CC);
1718+
cursor_it->cursor = cursor;
17111719
/* cursor_it->current should already be allocated to zero */
17121720

17131721
php_phongo_cursor_free_current(cursor_it->cursor);

php_phongo_classes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef struct {
4040
mongoc_client_t *client;
4141
int server_id;
4242
php_phongo_bson_state visitor_data;
43+
int got_iterator;
4344
} php_phongo_cursor_t;
4445

4546
typedef struct {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
MongoDB\Driver\Cursor get_iterator handler does not yield multiple iterators (foreach)
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; CLEANUP(STANDALONE) ?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
$manager = new MongoDB\Driver\Manager(STANDALONE);
10+
11+
$bulkWrite = new MongoDB\Driver\BulkWrite;
12+
13+
for ($i = 0; $i < 3; $i++) {
14+
$bulkWrite->insert(array('_id' => $i));
15+
}
16+
17+
$writeResult = $manager->executeBulkWrite(NS, $bulkWrite);
18+
printf("Inserted: %d\n", $writeResult->getInsertedCount());
19+
20+
$cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query(array()));
21+
22+
echo "\nFirst foreach statement:\n";
23+
24+
foreach ($cursor as $document) {
25+
var_dump($document);
26+
}
27+
28+
echo "\nSecond foreach statement:\n";
29+
30+
try {
31+
foreach ($cursor as $document) {
32+
echo "FAILED: get_iterator should not yield multiple iterators\n";
33+
}
34+
} catch (MongoDB\Driver\Exception\LogicException $e) {
35+
printf("LogicException: %s\n", $e->getMessage());
36+
}
37+
38+
?>
39+
===DONE===
40+
<?php exit(0); ?>
41+
--EXPECT--
42+
Inserted: 3
43+
44+
First foreach statement:
45+
array(1) {
46+
["_id"]=>
47+
int(0)
48+
}
49+
array(1) {
50+
["_id"]=>
51+
int(1)
52+
}
53+
array(1) {
54+
["_id"]=>
55+
int(2)
56+
}
57+
58+
Second foreach statement:
59+
LogicException: Cursors cannot yield multiple iterators
60+
===DONE===
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
--TEST--
2+
MongoDB\Driver\Cursor get_iterator handler does not yield multiple iterators (IteratorIterator)
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; CLEANUP(STANDALONE) ?>
5+
--FILE--
6+
<?php
7+
require_once __DIR__ . "/../utils/basic.inc";
8+
9+
$manager = new MongoDB\Driver\Manager(STANDALONE);
10+
11+
$bulkWrite = new MongoDB\Driver\BulkWrite;
12+
13+
for ($i = 0; $i < 3; $i++) {
14+
$bulkWrite->insert(array('_id' => $i));
15+
}
16+
17+
$writeResult = $manager->executeBulkWrite(NS, $bulkWrite);
18+
printf("Inserted: %d\n", $writeResult->getInsertedCount());
19+
20+
$cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query(array()));
21+
22+
echo "\nFirst IteratorIterator wrapping:\n";
23+
24+
var_dump(new IteratorIterator($cursor));
25+
26+
echo "\nSecond IteratorIterator wrapping:\n";
27+
28+
try {
29+
var_dump(new IteratorIterator($cursor));
30+
} catch (MongoDB\Driver\Exception\LogicException $e) {
31+
printf("LogicException: %s\n", $e->getMessage());
32+
}
33+
34+
?>
35+
===DONE===
36+
<?php exit(0); ?>
37+
--EXPECTF--
38+
Inserted: 3
39+
40+
First IteratorIterator wrapping:
41+
object(IteratorIterator)#%d (0) {
42+
}
43+
44+
Second IteratorIterator wrapping:
45+
LogicException: Cursors cannot yield multiple iterators
46+
===DONE===

0 commit comments

Comments
 (0)