Skip to content

Commit cc31f3a

Browse files
authored
PHPC-2269: Add internal iterator logic for MongoDB\BSON\Iterator (#1454)
* PHPC-2269: Add internal iterator logic for MongoDB\BSON\Iterator * Add compat macros for PHP 7.4 * Apply code review suggestions
1 parent ce9227e commit cc31f3a

File tree

2 files changed

+193
-37
lines changed

2 files changed

+193
-37
lines changed

src/BSON/Iterator.c

Lines changed: 155 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,67 @@ static void php_phongo_iterator_build_current(php_phongo_iterator_t* intern)
9292
phongo_bson_value_to_zval(bson_iter_value(&intern->iter), &intern->current);
9393
}
9494

95+
static zval* php_phongo_iterator_get_current(php_phongo_iterator_t* intern)
96+
{
97+
if (!intern->valid) {
98+
phongo_throw_exception(PHONGO_ERROR_LOGIC, "Cannot call current() on an exhausted iterator");
99+
return NULL;
100+
}
101+
102+
if (Z_ISUNDEF(intern->current)) {
103+
php_phongo_iterator_build_current(intern);
104+
}
105+
106+
return &intern->current;
107+
}
108+
109+
static void php_phongo_iterator_next(php_phongo_iterator_t* intern)
110+
{
111+
intern->valid = bson_iter_next(&intern->iter);
112+
intern->key++;
113+
php_phongo_iterator_free_current(intern);
114+
}
115+
116+
static bool php_phongo_iterator_key(php_phongo_iterator_t* intern, zval* key)
117+
{
118+
const char* ckey;
119+
120+
if (!intern->valid) {
121+
phongo_throw_exception(PHONGO_ERROR_LOGIC, "Cannot call key() on an exhausted iterator");
122+
return false;
123+
}
124+
125+
if (intern->is_array) {
126+
ZVAL_LONG(key, intern->key);
127+
128+
return true;
129+
}
130+
131+
ckey = bson_iter_key(&intern->iter);
132+
if (!bson_utf8_validate(ckey, strlen(ckey), false)) {
133+
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected corrupt BSON data at offset %d", intern->iter.off);
134+
return false;
135+
}
136+
137+
ZVAL_STRING(key, ckey);
138+
139+
return true;
140+
}
141+
142+
static void php_phongo_iterator_rewind(php_phongo_iterator_t* intern)
143+
{
144+
/* Don't re-initialise iterator if we're still on the first item */
145+
if (intern->key == 0) {
146+
return;
147+
}
148+
149+
php_phongo_iterator_free_current(intern);
150+
151+
bson_iter_init(&intern->iter, php_phongo_iterator_get_bson_from_zval(&intern->bson));
152+
intern->key = 0;
153+
intern->valid = bson_iter_next(&intern->iter);
154+
}
155+
95156
static HashTable* php_phongo_iterator_get_properties_hash(phongo_compat_object_handler_type* object, bool is_temp)
96157
{
97158
php_phongo_iterator_t* intern;
@@ -113,48 +174,33 @@ PHONGO_DISABLED_WAKEUP(MongoDB_BSON_Iterator)
113174
static PHP_METHOD(MongoDB_BSON_Iterator, current)
114175
{
115176
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(getThis());
177+
zval* data;
116178

117179
PHONGO_PARSE_PARAMETERS_NONE();
118180

119-
if (!intern->valid) {
120-
phongo_throw_exception(PHONGO_ERROR_LOGIC, "Cannot call current() on an exhausted iterator");
121-
return;
122-
}
123-
124-
if (Z_ISUNDEF(intern->current)) {
125-
php_phongo_iterator_build_current(intern);
181+
data = php_phongo_iterator_get_current(intern);
182+
if (!data) {
183+
// Exception already thrown
184+
RETURN_NULL();
126185
}
127186

128-
if (Z_ISUNDEF(intern->current)) {
187+
if (Z_ISUNDEF_P(data)) {
129188
RETURN_NULL();
130189
} else {
131-
ZVAL_COPY_DEREF(return_value, &intern->current);
190+
ZVAL_COPY_DEREF(return_value, data);
132191
}
133192
}
134193

135194
static PHP_METHOD(MongoDB_BSON_Iterator, key)
136195
{
137-
const char* key;
138196
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(getThis());
139197

140198
PHONGO_PARSE_PARAMETERS_NONE();
141199

142-
if (!intern->valid) {
143-
phongo_throw_exception(PHONGO_ERROR_LOGIC, "Cannot call key() on an exhausted iterator");
200+
if (!php_phongo_iterator_key(intern, return_value)) {
201+
// Exception already thrown
144202
return;
145203
}
146-
147-
if (intern->is_array) {
148-
RETURN_LONG(intern->key);
149-
}
150-
151-
key = bson_iter_key(&intern->iter);
152-
if (!bson_utf8_validate(key, strlen(key), false)) {
153-
phongo_throw_exception(PHONGO_ERROR_UNEXPECTED_VALUE, "Detected corrupt BSON data at offset %d", intern->iter.off);
154-
return;
155-
}
156-
157-
RETURN_STRING(key);
158204
}
159205

160206
static PHP_METHOD(MongoDB_BSON_Iterator, next)
@@ -163,9 +209,7 @@ static PHP_METHOD(MongoDB_BSON_Iterator, next)
163209

164210
PHONGO_PARSE_PARAMETERS_NONE();
165211

166-
intern->valid = bson_iter_next(&intern->iter);
167-
intern->key++;
168-
php_phongo_iterator_free_current(intern);
212+
php_phongo_iterator_next(intern);
169213
}
170214

171215
static PHP_METHOD(MongoDB_BSON_Iterator, valid)
@@ -183,16 +227,7 @@ static PHP_METHOD(MongoDB_BSON_Iterator, rewind)
183227

184228
PHONGO_PARSE_PARAMETERS_NONE();
185229

186-
/* Don't re-initialise iterator if we're still on the first item */
187-
if (intern->key == 0) {
188-
return;
189-
}
190-
191-
php_phongo_iterator_free_current(intern);
192-
193-
bson_iter_init(&intern->iter, php_phongo_iterator_get_bson_from_zval(&intern->bson));
194-
intern->key = 0;
195-
intern->valid = bson_iter_next(&intern->iter);
230+
php_phongo_iterator_rewind(intern);
196231
}
197232

198233
void phongo_iterator_init(zval* return_value, zval* document_or_packedarray)
@@ -263,10 +298,93 @@ static HashTable* php_phongo_iterator_get_properties(phongo_compat_object_handle
263298
return php_phongo_iterator_get_properties_hash(object, false);
264299
}
265300

301+
/* Iterator handlers */
302+
static void php_phongo_iterator_it_dtor(zend_object_iterator* iter)
303+
{
304+
zval_ptr_dtor(&iter->data);
305+
}
306+
307+
static int php_phongo_iterator_it_valid(zend_object_iterator* iter)
308+
{
309+
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(&iter->data);
310+
311+
return intern->valid ? SUCCESS : FAILURE;
312+
}
313+
314+
static zval* php_phongo_iterator_it_get_current_data(zend_object_iterator* iter)
315+
{
316+
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(&iter->data);
317+
318+
return php_phongo_iterator_get_current(intern);
319+
}
320+
321+
static void php_phongo_iterator_it_get_current_key(zend_object_iterator* iter, zval* key)
322+
{
323+
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(&iter->data);
324+
325+
if (!php_phongo_iterator_key(intern, key)) {
326+
// Exception already thrown
327+
return;
328+
}
329+
}
330+
331+
static void php_phongo_iterator_it_move_forward(zend_object_iterator* iter)
332+
{
333+
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(&iter->data);
334+
335+
php_phongo_iterator_next(intern);
336+
}
337+
338+
static void php_phongo_iterator_it_rewind(zend_object_iterator* iter)
339+
{
340+
php_phongo_iterator_t* intern = Z_ITERATOR_OBJ_P(&iter->data);
341+
342+
php_phongo_iterator_rewind(intern);
343+
}
344+
345+
#if PHP_VERSION_ID >= 80000
346+
static HashTable* php_phongo_iterator_it_get_gc(zend_object_iterator* iter, zval** table, int* n)
347+
{
348+
*n = 1;
349+
*table = &iter->data;
350+
return NULL;
351+
}
352+
#endif
353+
354+
static const zend_object_iterator_funcs php_phongo_iterator_it_funcs = PHONGO_ITERATOR_FUNCS(
355+
php_phongo_iterator_it_dtor,
356+
php_phongo_iterator_it_valid,
357+
php_phongo_iterator_it_get_current_data,
358+
php_phongo_iterator_it_get_current_key,
359+
php_phongo_iterator_it_move_forward,
360+
php_phongo_iterator_it_rewind,
361+
NULL, /* invalidate_current */
362+
php_phongo_iterator_it_get_gc);
363+
364+
static zend_object_iterator* php_phongo_iterator_get_iterator(zend_class_entry* ce, zval* object, int by_ref)
365+
{
366+
zend_object_iterator* iterator;
367+
368+
if (by_ref) {
369+
phongo_throw_exception(PHONGO_ERROR_LOGIC, "An iterator cannot be used with foreach by reference");
370+
return NULL;
371+
}
372+
373+
iterator = emalloc(sizeof(zend_object_iterator));
374+
zend_iterator_init(iterator);
375+
376+
ZVAL_OBJ_COPY(&iterator->data, Z_OBJ_P(object));
377+
378+
iterator->funcs = &php_phongo_iterator_it_funcs;
379+
380+
return iterator;
381+
}
382+
266383
void php_phongo_iterator_init_ce(INIT_FUNC_ARGS)
267384
{
268385
php_phongo_iterator_ce = register_class_MongoDB_BSON_Iterator(zend_ce_iterator);
269386
php_phongo_iterator_ce->create_object = php_phongo_iterator_create_object;
387+
php_phongo_iterator_ce->get_iterator = php_phongo_iterator_get_iterator;
270388
PHONGO_CE_DISABLE_SERIALIZATION(php_phongo_iterator_ce);
271389

272390
memcpy(&php_phongo_handler_iterator, phongo_get_std_object_handlers(), sizeof(zend_object_handlers));

src/phongo_compat.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,42 @@ zend_bool zend_array_is_list(zend_array* array);
303303
typedef ZEND_RESULT_CODE zend_result;
304304
#endif
305305

306+
/* get_gc iterator handler was added in PHP 8.0 */
307+
#if PHP_VERSION_ID >= 80000
308+
#define PHONGO_ITERATOR_FUNCS(dtor, valid, get_current_data, get_current_key, move_forward, rewind, invalidate_current, get_gc) \
309+
{ \
310+
(dtor), \
311+
(valid), \
312+
(get_current_data), \
313+
(get_current_key), \
314+
(move_forward), \
315+
(rewind), \
316+
(invalidate_current), \
317+
(get_gc), \
318+
}
319+
#else /* PHP_VERSION_ID < 80000 */
320+
#define PHONGO_ITERATOR_FUNCS(dtor, valid, get_current_data, get_current_key, move_forward, rewind, invalidate_current, get_gc) \
321+
{ \
322+
(dtor), \
323+
(valid), \
324+
(get_current_data), \
325+
(get_current_key), \
326+
(move_forward), \
327+
(rewind), \
328+
(invalidate_current), \
329+
}
330+
#endif /* PHP_VERSION_ID >= 80000 */
331+
332+
/* ZVAL_OBJ_COPY was added in PHP 8.0 */
333+
#ifndef ZVAL_OBJ_COPY
334+
#define ZVAL_OBJ_COPY(z, o) \
335+
do { \
336+
zval* __z = (z); \
337+
zend_object* __o = (o); \
338+
GC_ADDREF(__o); \
339+
Z_OBJ_P(__z) = __o; \
340+
Z_TYPE_INFO_P(__z) = IS_OBJECT_EX; \
341+
} while (0)
342+
#endif
343+
306344
#endif /* PHONGO_COMPAT_H */

0 commit comments

Comments
 (0)