Skip to content

Commit 7dd13d5

Browse files
committed
Add iterator to mysqli_result. Works both for :
- USE_RESULT, can be iterated only once, kind of forward iterator - STORE_RESULT, can be iterated multiple times
1 parent 50cda40 commit 7dd13d5

File tree

7 files changed

+482
-62
lines changed

7 files changed

+482
-62
lines changed

ext/mysqli/config.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ dnl Build extension
7777
if test "$PHP_MYSQLI" != "no"; then
7878
mysqli_sources="mysqli.c mysqli_api.c mysqli_prop.c mysqli_nonapi.c \
7979
mysqli_fe.c mysqli_report.c mysqli_driver.c mysqli_warning.c \
80-
mysqli_exception.c $mysqli_extra_sources"
80+
mysqli_exception.c mysqli_result_iterator.c $mysqli_extra_sources"
8181
PHP_NEW_EXTENSION(mysqli, $mysqli_sources, $ext_shared)
8282
PHP_SUBST(MYSQLI_SHARED_LIBADD)
8383

ext/mysqli/config.w32

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ if (PHP_MYSQLI != "no") {
1818
"mysqli_fe.c " +
1919
"mysqli_nonapi.c " +
2020
"mysqli_prop.c " +
21+
"mysqli_result_iterator.c " +
2122
"mysqli_report.c " +
2223
"mysqli_warning.c";
2324

ext/mysqli/mysqli.c

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "ext/standard/php_string.h"
3333
#include "php_mysqli_structs.h"
3434
#include "zend_exceptions.h"
35+
#include "zend_interfaces.h"
3536

3637
ZEND_DECLARE_MODULE_GLOBALS(mysqli)
3738
static PHP_GINIT_FUNCTION(mysqli);
@@ -685,6 +686,9 @@ PHP_MINIT_FUNCTION(mysqli)
685686
zend_hash_init(&mysqli_result_properties, 0, NULL, NULL, 1);
686687
MYSQLI_ADD_PROPERTIES(&mysqli_result_properties, mysqli_result_property_entries);
687688
MYSQLI_ADD_PROPERTIES_INFO(ce, mysqli_result_property_info_entries);
689+
mysqli_result_class_entry->get_iterator = php_mysqli_result_get_iterator;
690+
mysqli_result_class_entry->iterator_funcs.funcs = &php_mysqli_result_iterator_funcs;
691+
zend_class_implements(mysqli_result_class_entry TSRMLS_CC, 1, zend_ce_traversable);
688692
zend_hash_add(&classes, ce->name, ce->name_length+1, &mysqli_result_properties, sizeof(mysqli_result_properties), NULL);
689693

690694
REGISTER_MYSQLI_CLASS_ENTRY("mysqli_stmt", mysqli_stmt_class_entry, mysqli_stmt_methods);
@@ -1072,58 +1076,15 @@ PHP_FUNCTION(mysqli_result_construct)
10721076
}
10731077
/* }}} */
10741078

1075-
/* {{{ php_mysqli_fetch_into_hash
1079+
1080+
/* {{{ php_mysqli_fetch_into_hash_aux
10761081
*/
1077-
void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags, int into_object)
1082+
void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, long fetchtype TSRMLS_DC)
10781083
{
1079-
MYSQL_RES *result;
1080-
zval *mysql_result;
1081-
long fetchtype;
1082-
zval *ctor_params = NULL;
1083-
zend_class_entry *ce = NULL;
1084-
#if !defined(MYSQLI_USE_MYSQLND)
1084+
MYSQL_ROW row;
10851085
unsigned int i;
10861086
MYSQL_FIELD *fields;
1087-
MYSQL_ROW row;
10881087
unsigned long *field_len;
1089-
#endif
1090-
1091-
if (into_object) {
1092-
char *class_name;
1093-
int class_name_len;
1094-
1095-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sz", &mysql_result, mysqli_result_class_entry, &class_name, &class_name_len, &ctor_params) == FAILURE) {
1096-
return;
1097-
}
1098-
if (ZEND_NUM_ARGS() < (getThis() ? 1 : 2)) {
1099-
ce = zend_standard_class_def;
1100-
} else {
1101-
ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1102-
}
1103-
if (!ce) {
1104-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find class '%s'", class_name);
1105-
return;
1106-
}
1107-
fetchtype = MYSQLI_ASSOC;
1108-
} else {
1109-
if (override_flags) {
1110-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
1111-
return;
1112-
}
1113-
fetchtype = override_flags;
1114-
} else {
1115-
fetchtype = MYSQLI_BOTH;
1116-
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &fetchtype) == FAILURE) {
1117-
return;
1118-
}
1119-
}
1120-
}
1121-
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
1122-
1123-
if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) {
1124-
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH");
1125-
RETURN_FALSE;
1126-
}
11271088

11281089
#if !defined(MYSQLI_USE_MYSQLND)
11291090
if (!(row = mysql_fetch_row(result))) {
@@ -1195,8 +1156,60 @@ void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags
11951156
}
11961157
}
11971158
#else
1198-
mysqlnd_fetch_into(result, MYSQLND_FETCH_ASSOC, return_value, MYSQLND_MYSQLI);
1159+
mysqlnd_fetch_into(result, ((fetchtype & MYSQLI_NUM)? MYSQLND_FETCH_NUM:0) | ((fetchtype & MYSQLI_ASSOC)? MYSQLND_FETCH_ASSOC:0), return_value, MYSQLND_MYSQLI);
11991160
#endif
1161+
}
1162+
/* }}} */
1163+
1164+
1165+
/* {{{ php_mysqli_fetch_into_hash
1166+
*/
1167+
void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags, int into_object)
1168+
{
1169+
MYSQL_RES *result;
1170+
zval *mysql_result;
1171+
long fetchtype;
1172+
zval *ctor_params = NULL;
1173+
zend_class_entry *ce = NULL;
1174+
1175+
if (into_object) {
1176+
char *class_name;
1177+
int class_name_len;
1178+
1179+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|sz", &mysql_result, mysqli_result_class_entry, &class_name, &class_name_len, &ctor_params) == FAILURE) {
1180+
return;
1181+
}
1182+
if (ZEND_NUM_ARGS() < (getThis() ? 1 : 2)) {
1183+
ce = zend_standard_class_def;
1184+
} else {
1185+
ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
1186+
}
1187+
if (!ce) {
1188+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not find class '%s'", class_name);
1189+
return;
1190+
}
1191+
fetchtype = MYSQLI_ASSOC;
1192+
} else {
1193+
if (override_flags) {
1194+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
1195+
return;
1196+
}
1197+
fetchtype = override_flags;
1198+
} else {
1199+
fetchtype = MYSQLI_BOTH;
1200+
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &fetchtype) == FAILURE) {
1201+
return;
1202+
}
1203+
}
1204+
}
1205+
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, &mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
1206+
1207+
if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) {
1208+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The result type should be either MYSQLI_NUM, MYSQLI_ASSOC or MYSQLI_BOTH");
1209+
RETURN_FALSE;
1210+
}
1211+
1212+
php_mysqli_fetch_into_hash_aux(return_value, result, fetchtype TSRMLS_CC);
12001213

12011214
if (into_object && Z_TYPE_P(return_value) != IS_NULL) {
12021215
zval dataset = *return_value;

ext/mysqli/mysqli_libmysql.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
/*
2-
----------------------------------------------------------------------
3-
| PHP Version 6 |
4-
----------------------------------------------------------------------
5-
| Copyright (c) 2007 The PHP Group |
6-
----------------------------------------------------------------------
2+
+----------------------------------------------------------------------+
3+
| PHP Version 5 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 1997-2010 The PHP Group |
6+
+----------------------------------------------------------------------+
77
| This source file is subject to version 3.01 of the PHP license, |
88
| that is bundled with this package in the file LICENSE, and is |
99
| available through the world-wide-web at the following url: |
1010
| http://www.php.net/license/3_01.txt |
1111
| If you did not receive a copy of the PHP license and are unable to |
1212
| obtain it through the world-wide-web, please send a note to |
1313
| license@php.net so we can mail you a copy immediately. |
14-
----------------------------------------------------------------------
15-
| Authors: Georg Richter <georg@mysql.com> |
16-
| Andrey Hristov <andrey@mysql.com> |
17-
| Ulf Wendel <uwendel@mysql.com> |
18-
----------------------------------------------------------------------
19-
14+
+----------------------------------------------------------------------+
15+
| Authors: Georg Richter <georg@php.net> |
16+
| Andrey Hristov <andrey@php.net> |
17+
| Ulf Wendel <uw@php.net> |
18+
+----------------------------------------------------------------------+
2019
*/
2120

2221
#ifndef MYSQLI_LIBMYSQL_H
@@ -29,7 +28,8 @@
2928
#define MYSQLND_OPT_NUMERIC_AND_DATETIME_AS_UNICODE 200
3029
#define MYSQLND_OPT_INT_AND_YEAR_AS_INT 201
3130

32-
#define mysqli_result_is_unbuffered(r) ((r)->handle && (r)->handle->status == MYSQL_STATUS_USE_RESULT)
31+
/* r->data should be always NULL, at least in recent libmysql versions, the status changes once data is read*/
32+
#define mysqli_result_is_unbuffered(r) (((r)->handle && (r)->handle->status == MYSQL_STATUS_USE_RESULT) || ((r)->data == NULL))
3333
#define mysqli_server_status(c) (c)->server_status
3434
#define mysqli_stmt_get_id(s) ((s)->stmt_id)
3535
#define mysqli_stmt_warning_count(s) mysql_warning_count((s)->mysql)

ext/mysqli/mysqli_result_iterator.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| PHP Version 5 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 1997-2010 The PHP Group |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 3.01 of the PHP license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.php.net/license/3_01.txt |
11+
| If you did not receive a copy of the PHP license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| license@php.net so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Georg Richter <georg@php.net> |
16+
| Andrey Hristov <andrey@php.net> |
17+
| Ulf Wendel <uw@php.net> |
18+
+----------------------------------------------------------------------+
19+
20+
$Id: mysqli.c 299335 2010-05-13 11:05:09Z andrey $
21+
*/
22+
23+
#ifdef HAVE_CONFIG_H
24+
#include "config.h"
25+
#endif
26+
27+
#include <signal.h>
28+
29+
#include "php.h"
30+
#include "php_ini.h"
31+
#include "php_mysqli_structs.h"
32+
#include "zend_interfaces.h"
33+
34+
35+
extern zend_object_iterator_funcs php_mysqli_result_iterator_funcs;
36+
37+
typedef struct {
38+
zend_object_iterator intern;
39+
mysqli_object *result;
40+
zval *current_row;
41+
my_longlong row_num;
42+
} php_mysqli_result_iterator;
43+
44+
45+
/* {{{ */
46+
zend_object_iterator *php_mysqli_result_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC)
47+
{
48+
php_mysqli_result_iterator *iterator;
49+
50+
if (by_ref) {
51+
zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
52+
}
53+
iterator = ecalloc(1, sizeof(php_mysqli_result_iterator));
54+
55+
Z_ADDREF_P(object);
56+
iterator->intern.data = (void*)object;
57+
iterator->intern.funcs = &php_mysqli_result_iterator_funcs;
58+
iterator->result = (mysqli_object *) zend_object_store_get_object(object TSRMLS_CC);
59+
iterator->row_num = -1;
60+
61+
return (zend_object_iterator*)iterator;
62+
}
63+
/* }}} */
64+
65+
66+
/* {{{ */
67+
static void php_mysqli_result_iterator_dtor(zend_object_iterator *iter TSRMLS_DC)
68+
{
69+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
70+
71+
/* cleanup handled in sxe_object_dtor as we dont always have an iterator wrapper */
72+
if (iterator->intern.data) {
73+
zval_ptr_dtor((zval**)&iterator->intern.data);
74+
}
75+
if (iterator->current_row) {
76+
zval_ptr_dtor(&iterator->current_row);
77+
}
78+
efree(iterator);
79+
}
80+
/* }}} */
81+
82+
83+
/* {{{ */
84+
static int php_mysqli_result_iterator_valid(zend_object_iterator *iter TSRMLS_DC)
85+
{
86+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
87+
88+
return iterator->current_row && Z_TYPE_P(iterator->current_row) == IS_ARRAY ? SUCCESS : FAILURE;
89+
}
90+
/* }}} */
91+
92+
93+
/* {{{ */
94+
static void php_mysqli_result_iterator_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC)
95+
{
96+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
97+
98+
*data = &iterator->current_row;
99+
}
100+
/* }}} */
101+
102+
103+
/* {{{ */
104+
static void php_mysqli_result_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC)
105+
{
106+
107+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
108+
mysqli_object *intern = iterator->result;
109+
MYSQL_RES *result;
110+
111+
MYSQLI_FETCH_RESOURCE_BY_OBJ(result, MYSQL_RES *, intern, "mysqli_result", MYSQLI_STATUS_VALID);
112+
if (iterator->current_row) {
113+
zval_ptr_dtor(&iterator->current_row);
114+
}
115+
MAKE_STD_ZVAL(iterator->current_row);
116+
php_mysqli_fetch_into_hash_aux(iterator->current_row, result, MYSQLI_ASSOC TSRMLS_CC);
117+
if (Z_TYPE_P(iterator->current_row) == IS_ARRAY) {
118+
iterator->row_num++;
119+
}
120+
}
121+
/* }}} */
122+
123+
124+
/* {{{ */
125+
static void php_mysqli_result_iterator_rewind(zend_object_iterator *iter TSRMLS_DC)
126+
{
127+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
128+
mysqli_object *intern = iterator->result;
129+
MYSQL_RES *result;
130+
131+
MYSQLI_FETCH_RESOURCE_BY_OBJ(result, MYSQL_RES *, intern, "mysqli_result", MYSQLI_STATUS_VALID);
132+
133+
if (mysqli_result_is_unbuffered(result)) {
134+
#if MYSQLI_USE_MYSQLND
135+
if (result->unbuf && result->unbuf->eof_reached) {
136+
#else
137+
if (result->eof) {
138+
#endif
139+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Data fetched with MYSQLI_USE_RESULT can be iterated only once");
140+
return;
141+
}
142+
} else {
143+
mysql_data_seek(result, 0);
144+
}
145+
iterator->row_num = -1;
146+
php_mysqli_result_iterator_move_forward(iter TSRMLS_CC);
147+
}
148+
/* }}} */
149+
150+
151+
/* {{{ */
152+
/* PHP6 has the following declaration
153+
static int php_mysqli_result_iterator_current_key(zend_object_iterator *iter, zstr *str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
154+
*/
155+
static int php_mysqli_result_iterator_current_key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC)
156+
{
157+
php_mysqli_result_iterator *iterator = (php_mysqli_result_iterator*) iter;
158+
159+
*int_key = iterator->row_num;
160+
return HASH_KEY_IS_LONG;
161+
}
162+
/* }}} */
163+
164+
165+
/* {{{ php_mysqli_result_iterator_funcs */
166+
zend_object_iterator_funcs php_mysqli_result_iterator_funcs = {
167+
php_mysqli_result_iterator_dtor,
168+
php_mysqli_result_iterator_valid,
169+
php_mysqli_result_iterator_current_data,
170+
php_mysqli_result_iterator_current_key,
171+
php_mysqli_result_iterator_move_forward,
172+
php_mysqli_result_iterator_rewind,
173+
};
174+
/* }}} */
175+
176+
177+
/*
178+
* Local variables:
179+
* tab-width: 4
180+
* c-basic-offset: 4
181+
* End:
182+
* vim600: noet sw=4 ts=4 fdm=marker
183+
* vim<600: noet sw=4 ts=4
184+
*/

0 commit comments

Comments
 (0)