1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@php.net> |
14 | Marcus Boerger <helly@php.net> |
15 | Sterling Hughes <sterling@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* The PDO Statement Handle Class */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/php_var.h"
29 #include "php_pdo.h"
30 #include "php_pdo_driver.h"
31 #include "php_pdo_int.h"
32 #include "zend_exceptions.h"
33 #include "zend_interfaces.h"
34 #include "php_memory_streams.h"
35 #include "pdo_stmt_arginfo.h"
36
37 #define PHP_STMT_GET_OBJ \
38 pdo_stmt_t *stmt = Z_PDO_STMT_P(ZEND_THIS); \
39 if (!stmt->dbh) { \
40 zend_throw_error(NULL, "PDO object is uninitialized"); \
41 RETURN_THROWS(); \
42 } \
43
rewrite_name_to_position(pdo_stmt_t * stmt,struct pdo_bound_param_data * param)44 static inline bool rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param) /* {{{ */
45 {
46 if (stmt->bound_param_map) {
47 /* rewriting :name to ? style.
48 * We need to fixup the parameter numbers on the parameters.
49 * If we find that a given named parameter has been used twice,
50 * we will raise an error, as we can't be sure that it is safe
51 * to bind multiple parameters onto the same zval in the underlying
52 * driver */
53 zend_string *name;
54 int position = 0;
55
56 if (stmt->named_rewrite_template) {
57 /* this is not an error here */
58 return 1;
59 }
60 if (!param->name) {
61 /* do the reverse; map the parameter number to the name */
62 if ((name = zend_hash_index_find_ptr(stmt->bound_param_map, param->paramno)) != NULL) {
63 param->name = zend_string_copy(name);
64 return 1;
65 }
66 /* TODO Error? */
67 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
68 return 0;
69 }
70
71 ZEND_HASH_FOREACH_PTR(stmt->bound_param_map, name) {
72 if (!zend_string_equals(name, param->name)) {
73 position++;
74 continue;
75 }
76 if (param->paramno >= 0) {
77 /* TODO Error? */
78 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "PDO refuses to handle repeating the same :named parameter for multiple positions with this driver, as it might be unsafe to do so. Consider using a separate name for each parameter instead");
79 return -1;
80 }
81 param->paramno = position;
82 return 1;
83 } ZEND_HASH_FOREACH_END();
84 /* TODO Error? */
85 pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
86 return 0;
87 }
88 return 1;
89 }
90 /* }}} */
91
92 /* trigger callback hook for parameters */
dispatch_param_event(pdo_stmt_t * stmt,enum pdo_param_event event_type)93 static bool dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type) /* {{{ */
94 {
95 bool ret = 1, is_param = 1;
96 struct pdo_bound_param_data *param;
97 HashTable *ht;
98
99 if (stmt->dbh->skip_param_evt & (1 << event_type)) {
100 return 1;
101 }
102
103 if (!stmt->methods->param_hook) {
104 return 1;
105 }
106
107 ht = stmt->bound_params;
108
109 iterate:
110 if (ht) {
111 ZEND_HASH_FOREACH_PTR(ht, param) {
112 if (!stmt->methods->param_hook(stmt, param, event_type)) {
113 ret = 0;
114 break;
115 }
116 } ZEND_HASH_FOREACH_END();
117 }
118 if (ret && is_param) {
119 ht = stmt->bound_columns;
120 is_param = 0;
121 goto iterate;
122 }
123
124 return ret;
125 }
126 /* }}} */
127
pdo_stmt_describe_columns(pdo_stmt_t * stmt)128 bool pdo_stmt_describe_columns(pdo_stmt_t *stmt) /* {{{ */
129 {
130 int col;
131
132 stmt->columns = ecalloc(stmt->column_count, sizeof(struct pdo_column_data));
133
134 for (col = 0; col < stmt->column_count; col++) {
135 if (!stmt->methods->describer(stmt, col)) {
136 return false;
137 }
138
139 /* if we are applying case conversions on column names, do so now */
140 if (stmt->dbh->native_case != stmt->dbh->desired_case && stmt->dbh->desired_case != PDO_CASE_NATURAL) {
141 zend_string *orig_name = stmt->columns[col].name;
142 switch (stmt->dbh->desired_case) {
143 case PDO_CASE_LOWER:
144 stmt->columns[col].name = zend_string_tolower(orig_name);
145 zend_string_release(orig_name);
146 break;
147 case PDO_CASE_UPPER: {
148 stmt->columns[col].name = zend_string_separate(orig_name, 0);
149 char *s = ZSTR_VAL(stmt->columns[col].name);
150 while (*s != '\0') {
151 *s = toupper(*s);
152 s++;
153 }
154 break;
155 }
156 EMPTY_SWITCH_DEFAULT_CASE()
157 }
158 }
159
160 /* update the column index on named bound parameters */
161 if (stmt->bound_columns) {
162 struct pdo_bound_param_data *param;
163
164 if ((param = zend_hash_find_ptr(stmt->bound_columns,
165 stmt->columns[col].name)) != NULL) {
166 param->paramno = col;
167 }
168 }
169
170 }
171 return true;
172 }
173 /* }}} */
174
pdo_stmt_reset_columns(pdo_stmt_t * stmt)175 static void pdo_stmt_reset_columns(pdo_stmt_t *stmt) {
176 if (stmt->columns) {
177 int i;
178 struct pdo_column_data *cols = stmt->columns;
179
180 for (i = 0; i < stmt->column_count; i++) {
181 if (cols[i].name) {
182 zend_string_release_ex(cols[i].name, 0);
183 }
184 }
185 efree(stmt->columns);
186 }
187 stmt->columns = NULL;
188 stmt->column_count = 0;
189 }
190
191 /**
192 * Change the column count on the statement. If it differs from the previous one,
193 * discard existing columns information.
194 */
php_pdo_stmt_set_column_count(pdo_stmt_t * stmt,int new_count)195 PDO_API void php_pdo_stmt_set_column_count(pdo_stmt_t *stmt, int new_count)
196 {
197 /* Columns not yet "described". */
198 if (!stmt->columns) {
199 stmt->column_count = new_count;
200 return;
201 }
202
203 /* The column count has not changed: No need to reload columns description.
204 * Note: Do not handle attribute name change, without column count change. */
205 if (new_count == stmt->column_count) {
206 return;
207 }
208
209 /* Free previous columns to force reload description. */
210 pdo_stmt_reset_columns(stmt);
211 stmt->column_count = new_count;
212 }
213
get_lazy_object(pdo_stmt_t * stmt,zval * return_value)214 static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value) /* {{{ */
215 {
216 if (Z_ISUNDEF(stmt->lazy_object_ref)) {
217 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
218 row->stmt = stmt;
219 zend_object_std_init(&row->std, pdo_row_ce);
220 ZVAL_OBJ(&stmt->lazy_object_ref, &row->std);
221 row->std.handlers = &pdo_row_object_handlers;
222 GC_ADDREF(&stmt->std);
223 GC_DELREF(&row->std);
224 }
225 ZVAL_COPY(return_value, &stmt->lazy_object_ref);
226 }
227 /* }}} */
228
param_dtor(zval * el)229 static void param_dtor(zval *el) /* {{{ */
230 {
231 struct pdo_bound_param_data *param = (struct pdo_bound_param_data *)Z_PTR_P(el);
232
233 /* tell the driver that it is going away */
234 if (param->stmt->methods->param_hook) {
235 param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE);
236 }
237
238 if (param->name) {
239 zend_string_release_ex(param->name, 0);
240 }
241
242 if (!Z_ISUNDEF(param->parameter)) {
243 zval_ptr_dtor(¶m->parameter);
244 ZVAL_UNDEF(¶m->parameter);
245 }
246 if (!Z_ISUNDEF(param->driver_params)) {
247 zval_ptr_dtor(¶m->driver_params);
248 }
249 efree(param);
250 }
251 /* }}} */
252
really_register_bound_param(struct pdo_bound_param_data * param,pdo_stmt_t * stmt,bool is_param)253 static bool really_register_bound_param(struct pdo_bound_param_data *param, pdo_stmt_t *stmt, bool is_param) /* {{{ */
254 {
255 HashTable *hash;
256 zval *parameter;
257 struct pdo_bound_param_data *pparam = NULL;
258
259 hash = is_param ? stmt->bound_params : stmt->bound_columns;
260
261 if (!hash) {
262 ALLOC_HASHTABLE(hash);
263 zend_hash_init(hash, 13, NULL, param_dtor, 0);
264
265 if (is_param) {
266 stmt->bound_params = hash;
267 } else {
268 stmt->bound_columns = hash;
269 }
270 }
271
272 if (!Z_ISREF(param->parameter)) {
273 parameter = ¶m->parameter;
274 } else {
275 parameter = Z_REFVAL(param->parameter);
276 }
277
278 if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_STR && param->max_value_len <= 0 && !Z_ISNULL_P(parameter)) {
279 if (!try_convert_to_string(parameter)) {
280 return 0;
281 }
282 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_INT && (Z_TYPE_P(parameter) == IS_FALSE || Z_TYPE_P(parameter) == IS_TRUE)) {
283 convert_to_long(parameter);
284 } else if (PDO_PARAM_TYPE(param->param_type) == PDO_PARAM_BOOL && Z_TYPE_P(parameter) == IS_LONG) {
285 convert_to_boolean(parameter);
286 }
287
288 param->stmt = stmt;
289 param->is_param = is_param;
290
291 if (Z_REFCOUNTED(param->driver_params)) {
292 Z_ADDREF(param->driver_params);
293 }
294
295 if (!is_param && param->name && stmt->columns) {
296 /* try to map the name to the column */
297 int i;
298
299 for (i = 0; i < stmt->column_count; i++) {
300 if (zend_string_equals(stmt->columns[i].name, param->name)) {
301 param->paramno = i;
302 break;
303 }
304 }
305
306 /* if you prepare and then execute passing an array of params keyed by names,
307 * then this will trigger, and we don't want that */
308 if (param->paramno == -1) {
309 /* Should this always be an Error? */
310 char *tmp;
311 /* TODO Error? */
312 spprintf(&tmp, 0, "Did not find column name '%s' in the defined columns; it will not be bound", ZSTR_VAL(param->name));
313 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", tmp);
314 efree(tmp);
315 }
316 }
317
318 if (param->name) {
319 if (is_param && ZSTR_VAL(param->name)[0] != ':') {
320 zend_string *temp = zend_string_alloc(ZSTR_LEN(param->name) + 1, 0);
321 ZSTR_VAL(temp)[0] = ':';
322 memmove(ZSTR_VAL(temp) + 1, ZSTR_VAL(param->name), ZSTR_LEN(param->name) + 1);
323 param->name = temp;
324 } else {
325 param->name = zend_string_init(ZSTR_VAL(param->name), ZSTR_LEN(param->name), 0);
326 }
327 }
328
329 if (is_param && !rewrite_name_to_position(stmt, param)) {
330 if (param->name) {
331 zend_string_release_ex(param->name, 0);
332 param->name = NULL;
333 }
334 return 0;
335 }
336
337 /* ask the driver to perform any normalization it needs on the
338 * parameter name. Note that it is illegal for the driver to take
339 * a reference to param, as it resides in transient storage only
340 * at this time. */
341 if (stmt->methods->param_hook) {
342 if (!stmt->methods->param_hook(stmt, param, PDO_PARAM_EVT_NORMALIZE)) {
343 PDO_HANDLE_STMT_ERR();
344 if (param->name) {
345 zend_string_release_ex(param->name, 0);
346 param->name = NULL;
347 }
348 return 0;
349 }
350 }
351
352 /* delete any other parameter registered with this number.
353 * If the parameter is named, it will be removed and correctly
354 * disposed of by the hash_update call that follows */
355 if (param->paramno >= 0) {
356 zend_hash_index_del(hash, param->paramno);
357 }
358
359 /* allocate storage for the parameter, keyed by its "canonical" name */
360 if (param->name) {
361 pparam = zend_hash_update_mem(hash, param->name, param, sizeof(struct pdo_bound_param_data));
362 } else {
363 pparam = zend_hash_index_update_mem(hash, param->paramno, param, sizeof(struct pdo_bound_param_data));
364 }
365
366 /* tell the driver we just created a parameter */
367 if (stmt->methods->param_hook) {
368 if (!stmt->methods->param_hook(stmt, pparam, PDO_PARAM_EVT_ALLOC)) {
369 PDO_HANDLE_STMT_ERR();
370 /* undo storage allocation; the hash will free the parameter
371 * name if required */
372 if (pparam->name) {
373 zend_hash_del(hash, pparam->name);
374 } else {
375 zend_hash_index_del(hash, pparam->paramno);
376 }
377 /* param->parameter is freed by hash dtor */
378 ZVAL_UNDEF(¶m->parameter);
379 return 0;
380 }
381 }
382 return 1;
383 }
384 /* }}} */
385
386 /* {{{ Execute a prepared statement, optionally binding parameters */
PHP_METHOD(PDOStatement,execute)387 PHP_METHOD(PDOStatement, execute)
388 {
389 zval *input_params = NULL;
390 int ret = 1;
391
392 ZEND_PARSE_PARAMETERS_START(0, 1)
393 Z_PARAM_OPTIONAL
394 Z_PARAM_ARRAY_OR_NULL(input_params)
395 ZEND_PARSE_PARAMETERS_END();
396
397 PHP_STMT_GET_OBJ;
398 PDO_STMT_CLEAR_ERR();
399
400 if (input_params) {
401 struct pdo_bound_param_data param;
402 zval *tmp;
403 zend_string *key = NULL;
404 zend_ulong num_index;
405
406 if (stmt->bound_params) {
407 zend_hash_destroy(stmt->bound_params);
408 FREE_HASHTABLE(stmt->bound_params);
409 stmt->bound_params = NULL;
410 }
411
412 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(input_params), num_index, key, tmp) {
413 memset(¶m, 0, sizeof(param));
414
415 if (key) {
416 /* yes this is correct. we don't want to count the null byte. ask wez */
417 param.name = key;
418 param.paramno = -1;
419 } else {
420 /* we're okay to be zero based here */
421 /* num_index is unsignend */
422 param.paramno = num_index;
423 }
424
425 param.param_type = PDO_PARAM_STR;
426 ZVAL_COPY(¶m.parameter, tmp);
427
428 if (!really_register_bound_param(¶m, stmt, 1)) {
429 if (!Z_ISUNDEF(param.parameter)) {
430 zval_ptr_dtor(¶m.parameter);
431 }
432 RETURN_FALSE;
433 }
434 } ZEND_HASH_FOREACH_END();
435 }
436
437 if (PDO_PLACEHOLDER_NONE == stmt->supports_placeholders) {
438 /* handle the emulated parameter binding,
439 * stmt->active_query_string holds the query with binds expanded and
440 * quoted.
441 */
442
443 /* string is leftover from previous calls so PDOStatement::debugDumpParams() can access */
444 if (stmt->active_query_string) {
445 zend_string_release(stmt->active_query_string);
446 stmt->active_query_string = NULL;
447 }
448
449 ret = pdo_parse_params(stmt, stmt->query_string, &stmt->active_query_string);
450
451 if (ret == 0) {
452 /* no changes were made */
453 stmt->active_query_string = zend_string_copy(stmt->query_string);
454 ret = 1;
455 } else if (ret == -1) {
456 /* something broke */
457 RETURN_FALSE;
458 }
459 } else if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE)) {
460 PDO_HANDLE_STMT_ERR();
461 RETURN_FALSE;
462 }
463 if (stmt->methods->executer(stmt)) {
464 if (!stmt->executed) {
465 /* this is the first execute */
466
467 if (stmt->dbh->alloc_own_columns && !stmt->columns) {
468 /* for "big boy" drivers, we need to allocate memory to fetch
469 * the results into, so lets do that now */
470 ret = pdo_stmt_describe_columns(stmt);
471 }
472
473 stmt->executed = 1;
474 }
475
476 if (ret && !dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_POST)) {
477 PDO_HANDLE_STMT_ERR();
478 RETURN_FALSE;
479 }
480
481 RETURN_BOOL(ret);
482 }
483 PDO_HANDLE_STMT_ERR();
484 RETURN_FALSE;
485 }
486 /* }}} */
487
fetch_value(pdo_stmt_t * stmt,zval * dest,int colno,enum pdo_param_type * type_override)488 static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno, enum pdo_param_type *type_override) /* {{{ */
489 {
490 if (colno < 0) {
491 zend_value_error("Column index must be greater than or equal to 0");
492 ZVAL_NULL(dest);
493 return;
494 }
495
496 if (colno >= stmt->column_count) {
497 zend_value_error("Invalid column index");
498 ZVAL_NULL(dest);
499 return;
500 }
501
502 ZVAL_NULL(dest);
503 stmt->methods->get_col(stmt, colno, dest, type_override);
504
505 if (Z_TYPE_P(dest) == IS_STRING && Z_STRLEN_P(dest) == 0
506 && stmt->dbh->oracle_nulls == PDO_NULL_EMPTY_STRING) {
507 zval_ptr_dtor_str(dest);
508 ZVAL_NULL(dest);
509 }
510
511 /* If stringification is requested, override with PDO_PARAM_STR. */
512 enum pdo_param_type pdo_param_str = PDO_PARAM_STR;
513 if (stmt->dbh->stringify) {
514 type_override = &pdo_param_str;
515 }
516
517 if (type_override && Z_TYPE_P(dest) != IS_NULL) {
518 switch (*type_override) {
519 case PDO_PARAM_INT:
520 convert_to_long(dest);
521 break;
522 case PDO_PARAM_BOOL:
523 convert_to_boolean(dest);
524 break;
525 case PDO_PARAM_STR:
526 if (Z_TYPE_P(dest) == IS_FALSE) {
527 /* Return "0" rather than "", because this is what database drivers that
528 * don't have a dedicated boolean type would return. */
529 zval_ptr_dtor_nogc(dest);
530 ZVAL_INTERNED_STR(dest, ZSTR_CHAR('0'));
531 } else if (Z_TYPE_P(dest) == IS_RESOURCE) {
532 /* Convert LOB stream to string */
533 php_stream *stream;
534 php_stream_from_zval_no_verify(stream, dest);
535 zend_string *str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
536 zval_ptr_dtor_nogc(dest);
537 if (str == NULL) {
538 ZVAL_EMPTY_STRING(dest);
539 } else {
540 ZVAL_STR(dest, str);
541 }
542 } else {
543 convert_to_string(dest);
544 }
545 break;
546 case PDO_PARAM_NULL:
547 convert_to_null(dest);
548 break;
549 case PDO_PARAM_LOB:
550 if (Z_TYPE_P(dest) == IS_STRING) {
551 php_stream *stream =
552 php_stream_memory_open(TEMP_STREAM_READONLY, Z_STR_P(dest));
553 zval_ptr_dtor_str(dest);
554 php_stream_to_zval(stream, dest);
555 }
556 break;
557 default:
558 break;
559 }
560 }
561
562 if (Z_TYPE_P(dest) == IS_NULL && stmt->dbh->oracle_nulls == PDO_NULL_TO_STRING) {
563 ZVAL_EMPTY_STRING(dest);
564 }
565 }
566 /* }}} */
567
do_fetch_common(pdo_stmt_t * stmt,enum pdo_fetch_orientation ori,zend_long offset)568 static bool do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori, zend_long offset) /* {{{ */
569 {
570 if (!stmt->executed) {
571 return 0;
572 }
573
574 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE)) {
575 return 0;
576 }
577
578 if (!stmt->methods->fetcher(stmt, ori, offset)) {
579 return 0;
580 }
581
582 /* some drivers might need to describe the columns now */
583 if (!stmt->columns && !pdo_stmt_describe_columns(stmt)) {
584 return 0;
585 }
586
587 if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST)) {
588 return 0;
589 }
590
591 if (stmt->bound_columns) {
592 /* update those bound column variables now */
593 struct pdo_bound_param_data *param;
594
595 ZEND_HASH_FOREACH_PTR(stmt->bound_columns, param) {
596 if (param->paramno >= 0) {
597 if (!Z_ISREF(param->parameter)) {
598 continue;
599 }
600
601 /* delete old value */
602 zval_ptr_dtor(Z_REFVAL(param->parameter));
603
604 /* set new value */
605 fetch_value(stmt, Z_REFVAL(param->parameter), param->paramno, ¶m->param_type);
606
607 /* TODO: some smart thing that avoids duplicating the value in the
608 * general loop below. For now, if you're binding output columns,
609 * it's better to use LAZY or BOUND fetches if you want to shave
610 * off those cycles */
611 }
612 } ZEND_HASH_FOREACH_END();
613 }
614
615 return 1;
616 }
617 /* }}} */
618
do_fetch_class_prepare(pdo_stmt_t * stmt)619 static bool do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */
620 {
621 zend_class_entry *ce = stmt->fetch.cls.ce;
622 zend_fcall_info *fci = &stmt->fetch.cls.fci;
623 zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc;
624
625 fci->size = sizeof(zend_fcall_info);
626
627 if (!ce) {
628 stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
629 ce = ZEND_STANDARD_CLASS_DEF_PTR;
630 }
631
632 if (ce->constructor) {
633 ZVAL_UNDEF(&fci->function_name);
634 fci->retval = &stmt->fetch.cls.retval;
635 fci->param_count = 0;
636 fci->params = NULL;
637
638 zend_fcall_info_args_ex(fci, ce->constructor, &stmt->fetch.cls.ctor_args);
639
640 fcc->function_handler = ce->constructor;
641 fcc->called_scope = ce;
642 return 1;
643 } else if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
644 zend_throw_error(NULL, "User-supplied statement does not accept constructor arguments");
645 return 0;
646 } else {
647 return 1; /* no ctor no args is also ok */
648 }
649 }
650 /* }}} */
651
make_callable_ex(pdo_stmt_t * stmt,zval * callable,zend_fcall_info * fci,zend_fcall_info_cache * fcc,int num_args)652 static bool make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args) /* {{{ */
653 {
654 char *is_callable_error = NULL;
655
656 if (zend_fcall_info_init(callable, 0, fci, fcc, NULL, &is_callable_error) == FAILURE) {
657 if (is_callable_error) {
658 zend_type_error("%s", is_callable_error);
659 efree(is_callable_error);
660 } else {
661 zend_type_error("User-supplied function must be a valid callback");
662 }
663 return false;
664 }
665 if (is_callable_error) {
666 /* Possible error message */
667 efree(is_callable_error);
668 }
669
670 fci->param_count = num_args; /* probably less */
671 fci->params = safe_emalloc(sizeof(zval), num_args, 0);
672
673 return true;
674 }
675 /* }}} */
676
do_fetch_func_prepare(pdo_stmt_t * stmt)677 static bool do_fetch_func_prepare(pdo_stmt_t *stmt) /* {{{ */
678 {
679 zend_fcall_info *fci = &stmt->fetch.cls.fci;
680 zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc;
681
682 if (!make_callable_ex(stmt, &stmt->fetch.func.function, fci, fcc, stmt->column_count)) {
683 return false;
684 } else {
685 stmt->fetch.func.values = safe_emalloc(sizeof(zval), stmt->column_count, 0);
686 return true;
687 }
688 }
689 /* }}} */
690
do_fetch_opt_finish(pdo_stmt_t * stmt,int free_ctor_agrs)691 static void do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs) /* {{{ */
692 {
693 /* fci.size is used to check if it is valid */
694 if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) {
695 if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args)) {
696 /* Added to free constructor arguments */
697 zend_fcall_info_args_clear(&stmt->fetch.cls.fci, 1);
698 } else {
699 efree(stmt->fetch.cls.fci.params);
700 }
701 stmt->fetch.cls.fci.params = NULL;
702 }
703
704 stmt->fetch.cls.fci.size = 0;
705 if (!Z_ISUNDEF(stmt->fetch.cls.ctor_args) && free_ctor_agrs) {
706 zval_ptr_dtor(&stmt->fetch.cls.ctor_args);
707 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
708 stmt->fetch.cls.fci.param_count = 0;
709 }
710 if (stmt->fetch.func.values) {
711 efree(stmt->fetch.func.values);
712 stmt->fetch.func.values = NULL;
713 }
714 }
715 /* }}} */
716
717 /* perform a fetch.
718 * If return_value is not null, store values into it according to HOW. */
do_fetch(pdo_stmt_t * stmt,zval * return_value,enum pdo_fetch_type how,enum pdo_fetch_orientation ori,zend_long offset,zval * return_all)719 static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type how, enum pdo_fetch_orientation ori, zend_long offset, zval *return_all) /* {{{ */
720 {
721 int flags, idx, old_arg_count = 0;
722 zend_class_entry *ce = NULL, *old_ce = NULL;
723 zval grp_val, *pgrp, retval, old_ctor_args = {{0}, {0}, {0}};
724 int colno;
725 int i = 0;
726
727 if (how == PDO_FETCH_USE_DEFAULT) {
728 how = stmt->default_fetch_type;
729 }
730 flags = how & PDO_FETCH_FLAGS;
731 how = how & ~PDO_FETCH_FLAGS;
732
733 if (!do_fetch_common(stmt, ori, offset)) {
734 return 0;
735 }
736
737 if (how == PDO_FETCH_BOUND) {
738 RETVAL_TRUE;
739 return 1;
740 }
741
742 if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
743 colno = 1;
744 } else {
745 colno = stmt->fetch.column;
746 }
747
748 /* If no return value we are done */
749 if (!return_value) {
750 return true;
751 }
752
753 if (how == PDO_FETCH_LAZY) {
754 get_lazy_object(stmt, return_value);
755 return 1;
756 }
757
758 RETVAL_FALSE;
759
760 switch (how) {
761 case PDO_FETCH_USE_DEFAULT:
762 case PDO_FETCH_ASSOC:
763 case PDO_FETCH_BOTH:
764 case PDO_FETCH_NUM:
765 case PDO_FETCH_NAMED:
766 if (!return_all) {
767 array_init_size(return_value, stmt->column_count);
768 } else {
769 array_init(return_value);
770 }
771 break;
772
773 case PDO_FETCH_KEY_PAIR:
774 if (stmt->column_count != 2) {
775 /* TODO: Error? */
776 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain exactly 2 columns.");
777 return 0;
778 }
779 if (!return_all) {
780 array_init(return_value);
781 }
782 break;
783
784 case PDO_FETCH_COLUMN:
785 if (colno < 0 ) {
786 zend_value_error("Column index must be greater than or equal to 0");
787 return false;
788 }
789
790 if (colno >= stmt->column_count) {
791 zend_value_error("Invalid column index");
792 return false;
793 }
794
795 if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
796 fetch_value(stmt, return_value, 1, NULL);
797 } else if (flags == PDO_FETCH_GROUP && colno) {
798 fetch_value(stmt, return_value, 0, NULL);
799 } else {
800 fetch_value(stmt, return_value, colno, NULL);
801 }
802 if (!return_all) {
803 return 1;
804 }
805 break;
806
807 case PDO_FETCH_OBJ:
808 object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
809 break;
810
811 case PDO_FETCH_CLASS:
812 if (flags & PDO_FETCH_CLASSTYPE) {
813 zval val;
814 zend_class_entry *cep;
815
816 old_ce = stmt->fetch.cls.ce;
817 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
818 old_arg_count = stmt->fetch.cls.fci.param_count;
819 do_fetch_opt_finish(stmt, 0);
820
821 fetch_value(stmt, &val, i++, NULL);
822 if (Z_TYPE(val) != IS_NULL) {
823 if (!try_convert_to_string(&val)) {
824 return 0;
825 }
826 if ((cep = zend_lookup_class(Z_STR(val))) == NULL) {
827 stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
828 } else {
829 stmt->fetch.cls.ce = cep;
830 }
831 }
832
833 do_fetch_class_prepare(stmt);
834 zval_ptr_dtor_str(&val);
835 }
836 ce = stmt->fetch.cls.ce;
837 /* TODO: Make this an assertion and ensure this is true higher up? */
838 if (!ce) {
839 /* TODO Error? */
840 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified");
841 return 0;
842 }
843 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
844 if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
845 return 0;
846 }
847 if (!stmt->fetch.cls.fci.size) {
848 if (!do_fetch_class_prepare(stmt)) {
849 zval_ptr_dtor(return_value);
850 return 0;
851 }
852 }
853 if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
854 stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
855 stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
856 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
857 /* TODO Error? */
858 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
859 return 0;
860 } else {
861 if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
862 zval_ptr_dtor(&stmt->fetch.cls.retval);
863 ZVAL_UNDEF(&stmt->fetch.cls.retval);
864 }
865 }
866 }
867 }
868 break;
869
870 case PDO_FETCH_INTO:
871 /* TODO: Make this an assertion and ensure this is true higher up? */
872 if (Z_ISUNDEF(stmt->fetch.into)) {
873 /* TODO ArgumentCountError? */
874 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified.");
875 return 0;
876 break;
877 }
878
879 ZVAL_COPY(return_value, &stmt->fetch.into);
880
881 if (Z_OBJ_P(return_value)->ce == ZEND_STANDARD_CLASS_DEF_PTR) {
882 how = PDO_FETCH_OBJ;
883 }
884 break;
885
886 case PDO_FETCH_FUNC:
887 /* TODO: Make this an assertion and ensure this is true higher up? */
888 if (Z_ISUNDEF(stmt->fetch.func.function)) {
889 /* TODO ArgumentCountError? */
890 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified");
891 return 0;
892 }
893 if (!stmt->fetch.func.fci.size) {
894 if (!do_fetch_func_prepare(stmt))
895 {
896 return 0;
897 }
898 }
899 break;
900 EMPTY_SWITCH_DEFAULT_CASE();
901 }
902
903 if (return_all && how != PDO_FETCH_KEY_PAIR) {
904 if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
905 fetch_value(stmt, &grp_val, colno, NULL);
906 } else {
907 fetch_value(stmt, &grp_val, i, NULL);
908 }
909 convert_to_string(&grp_val);
910 if (how == PDO_FETCH_COLUMN) {
911 i = stmt->column_count; /* no more data to fetch */
912 } else {
913 i++;
914 }
915 }
916
917 for (idx = 0; i < stmt->column_count; i++, idx++) {
918 zval val;
919 fetch_value(stmt, &val, i, NULL);
920
921 switch (how) {
922 case PDO_FETCH_ASSOC:
923 zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
924 break;
925
926 case PDO_FETCH_KEY_PAIR:
927 {
928 zval tmp;
929 fetch_value(stmt, &tmp, ++i, NULL);
930
931 if (Z_TYPE(val) == IS_LONG) {
932 zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL(val), &tmp);
933 } else {
934 convert_to_string(&val);
935 zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STR(val), &tmp);
936 }
937 zval_ptr_dtor(&val);
938 return 1;
939 }
940 break;
941
942 case PDO_FETCH_USE_DEFAULT:
943 case PDO_FETCH_BOTH:
944 zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
945 if (zend_hash_index_add(Z_ARRVAL_P(return_value), i, &val) != NULL) {
946 Z_TRY_ADDREF(val);
947 }
948 break;
949
950 case PDO_FETCH_NAMED:
951 /* already have an item with this name? */
952 {
953 zval *curr_val;
954 if ((curr_val = zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name))) {
955 zval arr;
956 if (Z_TYPE_P(curr_val) != IS_ARRAY) {
957 /* a little bit of black magic here:
958 * we're creating a new array and swapping it for the
959 * zval that's already stored in the hash under the name
960 * we want. We then add that zval to the array.
961 * This is effectively the same thing as:
962 * if (!is_array($hash[$name])) {
963 * $hash[$name] = array($hash[$name]);
964 * }
965 * */
966 zval cur;
967
968 array_init(&arr);
969
970 ZVAL_COPY_VALUE(&cur, curr_val);
971 ZVAL_COPY_VALUE(curr_val, &arr);
972
973 zend_hash_next_index_insert_new(Z_ARRVAL(arr), &cur);
974 } else {
975 ZVAL_COPY_VALUE(&arr, curr_val);
976 }
977 zend_hash_next_index_insert_new(Z_ARRVAL(arr), &val);
978 } else {
979 zend_hash_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
980 }
981 }
982 break;
983
984 case PDO_FETCH_NUM:
985 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &val);
986 break;
987
988 case PDO_FETCH_OBJ:
989 case PDO_FETCH_INTO:
990 zend_update_property_ex(NULL, Z_OBJ_P(return_value),
991 stmt->columns[i].name,
992 &val);
993 zval_ptr_dtor(&val);
994 break;
995
996 case PDO_FETCH_CLASS:
997 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
998 zend_update_property_ex(ce, Z_OBJ_P(return_value),
999 stmt->columns[i].name,
1000 &val);
1001 zval_ptr_dtor(&val);
1002 } else {
1003 if (!ce->unserialize) {
1004 zval_ptr_dtor(&val);
1005 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1006 return 0;
1007 } else if (ce->unserialize(return_value, ce, (unsigned char *)(Z_TYPE(val) == IS_STRING ? Z_STRVAL(val) : ""), Z_TYPE(val) == IS_STRING ? Z_STRLEN(val) : 0, NULL) == FAILURE) {
1008 zval_ptr_dtor(&val);
1009 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1010 zval_ptr_dtor(return_value);
1011 ZVAL_NULL(return_value);
1012 return 0;
1013 } else {
1014 zval_ptr_dtor(&val);
1015 }
1016 }
1017 break;
1018
1019 case PDO_FETCH_FUNC:
1020 ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val);
1021 ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]);
1022 break;
1023
1024 default:
1025 zval_ptr_dtor(&val);
1026 zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants");
1027 return 0;
1028 }
1029 }
1030
1031 switch (how) {
1032 case PDO_FETCH_CLASS:
1033 if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1034 stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
1035 stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
1036 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
1037 /* TODO Error? */
1038 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
1039 return 0;
1040 } else {
1041 if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
1042 zval_ptr_dtor(&stmt->fetch.cls.retval);
1043 }
1044 }
1045 }
1046 if (flags & PDO_FETCH_CLASSTYPE) {
1047 do_fetch_opt_finish(stmt, 0);
1048 stmt->fetch.cls.ce = old_ce;
1049 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1050 stmt->fetch.cls.fci.param_count = old_arg_count;
1051 }
1052 break;
1053
1054 case PDO_FETCH_FUNC:
1055 stmt->fetch.func.fci.param_count = idx;
1056 stmt->fetch.func.fci.retval = &retval;
1057 if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc) == FAILURE) {
1058 /* TODO Error? */
1059 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function");
1060 return 0;
1061 } else {
1062 if (return_all) {
1063 zval_ptr_dtor(return_value); /* we don't need that */
1064 ZVAL_COPY_VALUE(return_value, &retval);
1065 } else if (!Z_ISUNDEF(retval)) {
1066 ZVAL_COPY_VALUE(return_value, &retval);
1067 }
1068 }
1069 while (idx--) {
1070 zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1071 }
1072 break;
1073
1074 default:
1075 break;
1076 }
1077
1078 if (return_all) {
1079 if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1080 zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), return_value);
1081 } else {
1082 zval grp;
1083 if ((pgrp = zend_symtable_find(Z_ARRVAL_P(return_all), Z_STR(grp_val))) == NULL) {
1084 array_init(&grp);
1085 zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), &grp);
1086 } else {
1087 ZVAL_COPY_VALUE(&grp, pgrp);
1088 }
1089 zend_hash_next_index_insert(Z_ARRVAL(grp), return_value);
1090 }
1091 zval_ptr_dtor_str(&grp_val);
1092 }
1093
1094 return 1;
1095 }
1096 /* }}} */
1097
pdo_stmt_verify_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,bool fetch_all)1098 static bool pdo_stmt_verify_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num, bool fetch_all) /* {{{ */
1099 {
1100 int flags = mode & PDO_FETCH_FLAGS;
1101
1102 mode = mode & ~PDO_FETCH_FLAGS;
1103
1104 if (mode < 0 || mode > PDO_FETCH__MAX) {
1105 zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1106 return 0;
1107 }
1108
1109 if (mode == PDO_FETCH_USE_DEFAULT) {
1110 flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1111 mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1112 }
1113
1114 switch(mode) {
1115 case PDO_FETCH_FUNC:
1116 if (!fetch_all) {
1117 zend_value_error("Can only use PDO::FETCH_FUNC in PDOStatement::fetchAll()");
1118 return 0;
1119 }
1120 return 1;
1121
1122 case PDO_FETCH_LAZY:
1123 if (fetch_all) {
1124 zend_argument_value_error(mode_arg_num, "cannot be PDO::FETCH_LAZY in PDOStatement::fetchAll()");
1125 return 0;
1126 }
1127 ZEND_FALLTHROUGH;
1128 default:
1129 if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1130 zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_SERIALIZE with PDO::FETCH_CLASS");
1131 return 0;
1132 }
1133 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1134 zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_CLASSTYPE with PDO::FETCH_CLASS");
1135 return 0;
1136 }
1137 if (mode >= PDO_FETCH__MAX) {
1138 zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1139 return 0;
1140 }
1141 ZEND_FALLTHROUGH;
1142
1143 case PDO_FETCH_CLASS:
1144 if (flags & PDO_FETCH_SERIALIZE) {
1145 php_error_docref(NULL, E_DEPRECATED, "The PDO::FETCH_SERIALIZE mode is deprecated");
1146 }
1147 return 1;
1148 }
1149 }
1150 /* }}} */
1151
1152 /* {{{ Fetches the next row and returns it, or false if there are no more rows */
PHP_METHOD(PDOStatement,fetch)1153 PHP_METHOD(PDOStatement, fetch)
1154 {
1155 zend_long how = PDO_FETCH_USE_DEFAULT;
1156 zend_long ori = PDO_FETCH_ORI_NEXT;
1157 zend_long off = 0;
1158
1159 ZEND_PARSE_PARAMETERS_START(0, 3)
1160 Z_PARAM_OPTIONAL
1161 Z_PARAM_LONG(how)
1162 Z_PARAM_LONG(ori)
1163 Z_PARAM_LONG(off)
1164 ZEND_PARSE_PARAMETERS_END();
1165
1166 PHP_STMT_GET_OBJ;
1167 PDO_STMT_CLEAR_ERR();
1168
1169 if (!pdo_stmt_verify_mode(stmt, how, 1, false)) {
1170 RETURN_THROWS();
1171 }
1172
1173 if (!do_fetch(stmt, return_value, how, ori, off, NULL)) {
1174 PDO_HANDLE_STMT_ERR();
1175 RETURN_FALSE;
1176 }
1177 }
1178 /* }}} */
1179
1180 /* {{{ Fetches the next row and returns it as an object. */
PHP_METHOD(PDOStatement,fetchObject)1181 PHP_METHOD(PDOStatement, fetchObject)
1182 {
1183 zend_class_entry *ce = NULL;
1184 zend_class_entry *old_ce;
1185 zval old_ctor_args, *ctor_args = NULL;
1186 int old_arg_count;
1187
1188 ZEND_PARSE_PARAMETERS_START(0, 2)
1189 Z_PARAM_OPTIONAL
1190 Z_PARAM_CLASS_OR_NULL(ce)
1191 Z_PARAM_ARRAY(ctor_args)
1192 ZEND_PARSE_PARAMETERS_END();
1193
1194 PHP_STMT_GET_OBJ;
1195 PDO_STMT_CLEAR_ERR();
1196
1197 old_ce = stmt->fetch.cls.ce;
1198 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1199 old_arg_count = stmt->fetch.cls.fci.param_count;
1200
1201 do_fetch_opt_finish(stmt, 0);
1202
1203 if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1204 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(Z_ARRVAL_P(ctor_args)));
1205 } else {
1206 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1207 }
1208 if (ce) {
1209 stmt->fetch.cls.ce = ce;
1210 } else {
1211 stmt->fetch.cls.ce = zend_standard_class_def;
1212 }
1213
1214 if (!do_fetch(stmt, return_value, PDO_FETCH_CLASS, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
1215 PDO_HANDLE_STMT_ERR();
1216 RETVAL_FALSE;
1217 }
1218 do_fetch_opt_finish(stmt, 1);
1219
1220 stmt->fetch.cls.ce = old_ce;
1221 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1222 stmt->fetch.cls.fci.param_count = old_arg_count;
1223 }
1224 /* }}} */
1225
1226 /* {{{ Returns a data of the specified column in the result set. */
PHP_METHOD(PDOStatement,fetchColumn)1227 PHP_METHOD(PDOStatement, fetchColumn)
1228 {
1229 zend_long col_n = 0;
1230
1231 ZEND_PARSE_PARAMETERS_START(0, 1)
1232 Z_PARAM_OPTIONAL
1233 Z_PARAM_LONG(col_n)
1234 ZEND_PARSE_PARAMETERS_END();
1235
1236 PHP_STMT_GET_OBJ;
1237 PDO_STMT_CLEAR_ERR();
1238
1239 if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0)) {
1240 PDO_HANDLE_STMT_ERR();
1241 RETURN_FALSE;
1242 }
1243
1244 fetch_value(stmt, return_value, col_n, NULL);
1245 }
1246 /* }}} */
1247
1248 /* {{{ Returns an array of all of the results. */
PHP_METHOD(PDOStatement,fetchAll)1249 PHP_METHOD(PDOStatement, fetchAll)
1250 {
1251 zend_long how = PDO_FETCH_USE_DEFAULT;
1252 zval data, *return_all = NULL;
1253 zval *arg2 = NULL;
1254 zend_class_entry *old_ce;
1255 zval old_ctor_args, *ctor_args = NULL;
1256 bool error = false;
1257 int flags, old_arg_count;
1258
1259 ZEND_PARSE_PARAMETERS_START(0, 3)
1260 Z_PARAM_OPTIONAL
1261 Z_PARAM_LONG(how)
1262 Z_PARAM_ZVAL_OR_NULL(arg2)
1263 Z_PARAM_ARRAY_OR_NULL(ctor_args)
1264 ZEND_PARSE_PARAMETERS_END();
1265
1266 PHP_STMT_GET_OBJ;
1267 if (!pdo_stmt_verify_mode(stmt, how, 1, true)) {
1268 RETURN_THROWS();
1269 }
1270
1271 old_ce = stmt->fetch.cls.ce;
1272 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1273 old_arg_count = stmt->fetch.cls.fci.param_count;
1274
1275 do_fetch_opt_finish(stmt, 0);
1276
1277 /* TODO Would be good to reuse part of pdo_stmt_setup_fetch_mode() in some way */
1278
1279 switch (how & ~PDO_FETCH_FLAGS) {
1280 case PDO_FETCH_CLASS:
1281 /* Figure out correct class */
1282 if (arg2) {
1283 if (Z_TYPE_P(arg2) != IS_STRING) {
1284 zend_argument_type_error(2, "must be of type string, %s given", zend_zval_type_name(arg2));
1285 RETURN_THROWS();
1286 }
1287 stmt->fetch.cls.ce = zend_fetch_class(Z_STR_P(arg2), ZEND_FETCH_CLASS_AUTO);
1288 if (!stmt->fetch.cls.ce) {
1289 zend_argument_type_error(2, "must be a valid class");
1290 RETURN_THROWS();
1291 }
1292 } else {
1293 stmt->fetch.cls.ce = zend_standard_class_def;
1294 }
1295
1296 if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args)) > 0) {
1297 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, ctor_args); /* we're not going to free these */
1298 } else {
1299 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1300 }
1301
1302 do_fetch_class_prepare(stmt);
1303 break;
1304
1305 case PDO_FETCH_FUNC: /* Cannot be a default fetch mode */
1306 if (ZEND_NUM_ARGS() != 2) {
1307 zend_string *func = get_active_function_or_method_name();
1308 zend_argument_count_error("%s() expects exactly 2 argument for PDO::FETCH_FUNC, %d given",
1309 ZSTR_VAL(func), ZEND_NUM_ARGS());
1310 zend_string_release(func);
1311 RETURN_THROWS();
1312 }
1313 if (arg2 == NULL) {
1314 /* TODO use "must be of type callable" format? */
1315 zend_argument_type_error(2, "must be a callable, null given");
1316 RETURN_THROWS();
1317 }
1318 /* TODO Check it is a callable? */
1319 ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2);
1320 if (do_fetch_func_prepare(stmt) == false) {
1321 RETURN_THROWS();
1322 }
1323 break;
1324
1325 case PDO_FETCH_COLUMN:
1326 if (ZEND_NUM_ARGS() > 2) {
1327 zend_string *func = get_active_function_or_method_name();
1328 zend_argument_count_error("%s() expects at most 2 argument for the fetch mode provided, %d given",
1329 ZSTR_VAL(func), ZEND_NUM_ARGS());
1330 zend_string_release(func);
1331 RETURN_THROWS();
1332 }
1333 /* Is column index passed? */
1334 if (arg2) {
1335 // Reuse convert_to_long(arg2); ?
1336 if (Z_TYPE_P(arg2) != IS_LONG) {
1337 zend_argument_type_error(2, "must be of type int, %s given", zend_zval_type_name(arg2));
1338 RETURN_THROWS();
1339 }
1340 if (Z_LVAL_P(arg2) < 0) {
1341 zend_argument_value_error(2, "must be greater than or equal to 0");
1342 RETURN_THROWS();
1343 }
1344 stmt->fetch.column = Z_LVAL_P(arg2);
1345 } else {
1346 stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1347 }
1348 break;
1349
1350 default:
1351 /* No support for PDO_FETCH_INTO which takes 2 args??? */
1352 if (ZEND_NUM_ARGS() > 1) {
1353 zend_string *func = get_active_function_or_method_name();
1354 zend_argument_count_error("%s() expects exactly 1 argument for the fetch mode provided, %d given",
1355 ZSTR_VAL(func), ZEND_NUM_ARGS());
1356 zend_string_release(func);
1357 RETURN_THROWS();
1358 }
1359 }
1360
1361 flags = how & PDO_FETCH_FLAGS;
1362
1363 if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1364 flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1365 how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1366 }
1367
1368 PDO_STMT_CLEAR_ERR();
1369 if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1370 (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1371 ) {
1372 array_init(return_value);
1373 return_all = return_value;
1374 }
1375 if (!do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all)) {
1376 error = true;
1377 }
1378
1379 if (!error) {
1380 if ((how & PDO_FETCH_GROUP)) {
1381 while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all));
1382 } else if (how == PDO_FETCH_KEY_PAIR || (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)) {
1383 while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all));
1384 } else {
1385 array_init(return_value);
1386 do {
1387 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &data);
1388 } while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL));
1389 }
1390 }
1391
1392 do_fetch_opt_finish(stmt, 0);
1393
1394 /* Restore defaults which were changed by PDO_FETCH_CLASS mode */
1395 stmt->fetch.cls.ce = old_ce;
1396 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1397 stmt->fetch.cls.fci.param_count = old_arg_count;
1398
1399 /* on no results, return an empty array */
1400 if (error) {
1401 PDO_HANDLE_STMT_ERR();
1402 if (Z_TYPE_P(return_value) != IS_ARRAY) {
1403 array_init(return_value);
1404 }
1405 }
1406 }
1407 /* }}} */
1408
register_bound_param(INTERNAL_FUNCTION_PARAMETERS,int is_param)1409 static void register_bound_param(INTERNAL_FUNCTION_PARAMETERS, int is_param) /* {{{ */
1410 {
1411 struct pdo_bound_param_data param;
1412 zend_long param_type = PDO_PARAM_STR;
1413 zval *parameter, *driver_params = NULL;
1414
1415 memset(¶m, 0, sizeof(param));
1416
1417 ZEND_PARSE_PARAMETERS_START(2, 5)
1418 Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1419 Z_PARAM_ZVAL(parameter)
1420 Z_PARAM_OPTIONAL
1421 Z_PARAM_LONG(param_type)
1422 Z_PARAM_LONG(param.max_value_len)
1423 Z_PARAM_ZVAL_OR_NULL(driver_params)
1424 ZEND_PARSE_PARAMETERS_END();
1425
1426 PHP_STMT_GET_OBJ;
1427
1428 param.param_type = (int) param_type;
1429
1430 if (param.name) {
1431 if (ZSTR_LEN(param.name) == 0) {
1432 zend_argument_value_error(1, "cannot be empty");
1433 RETURN_THROWS();
1434 }
1435 param.paramno = -1;
1436 } else if (param.paramno > 0) {
1437 --param.paramno; /* make it zero-based internally */
1438 } else {
1439 zend_argument_value_error(1, "must be greater than or equal to 1");
1440 RETURN_THROWS();
1441 }
1442
1443 if (driver_params) {
1444 ZVAL_COPY(¶m.driver_params, driver_params);
1445 }
1446
1447 ZVAL_COPY(¶m.parameter, parameter);
1448 if (!really_register_bound_param(¶m, stmt, is_param)) {
1449 if (!Z_ISUNDEF(param.parameter)) {
1450 zval_ptr_dtor(&(param.parameter));
1451 }
1452
1453 RETURN_FALSE;
1454 }
1455
1456 RETURN_TRUE;
1457 } /* }}} */
1458
1459 /* {{{ bind an input parameter to the value of a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindValue)1460 PHP_METHOD(PDOStatement, bindValue)
1461 {
1462 struct pdo_bound_param_data param;
1463 zend_long param_type = PDO_PARAM_STR;
1464 zval *parameter;
1465
1466 memset(¶m, 0, sizeof(param));
1467
1468 ZEND_PARSE_PARAMETERS_START(2, 3)
1469 Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1470 Z_PARAM_ZVAL(parameter)
1471 Z_PARAM_OPTIONAL
1472 Z_PARAM_LONG(param_type)
1473 ZEND_PARSE_PARAMETERS_END();
1474
1475 PHP_STMT_GET_OBJ;
1476 param.param_type = (int) param_type;
1477
1478 if (param.name) {
1479 if (ZSTR_LEN(param.name) == 0) {
1480 zend_argument_value_error(1, "cannot be empty");
1481 RETURN_THROWS();
1482 }
1483 param.paramno = -1;
1484 } else if (param.paramno > 0) {
1485 --param.paramno; /* make it zero-based internally */
1486 } else {
1487 zend_argument_value_error(1, "must be greater than or equal to 1");
1488 RETURN_THROWS();
1489 }
1490
1491 ZVAL_COPY(¶m.parameter, parameter);
1492 if (!really_register_bound_param(¶m, stmt, TRUE)) {
1493 if (!Z_ISUNDEF(param.parameter)) {
1494 zval_ptr_dtor(&(param.parameter));
1495 ZVAL_UNDEF(¶m.parameter);
1496 }
1497 RETURN_FALSE;
1498 }
1499 RETURN_TRUE;
1500 }
1501 /* }}} */
1502
1503 /* {{{ bind a parameter to a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). This isn't supported by all drivers. It should be called prior to execute(). */
PHP_METHOD(PDOStatement,bindParam)1504 PHP_METHOD(PDOStatement, bindParam)
1505 {
1506 register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1507 }
1508 /* }}} */
1509
1510 /* {{{ bind a column to a PHP variable. On each row fetch $param will contain the value of the corresponding column. $column is the 1-based offset of the column, or the column name. For portability, don't call this before execute(). */
PHP_METHOD(PDOStatement,bindColumn)1511 PHP_METHOD(PDOStatement, bindColumn)
1512 {
1513 register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1514 }
1515 /* }}} */
1516
1517 /* {{{ Returns the number of rows in a result set, or the number of rows affected by the last execute(). It is not always meaningful. */
PHP_METHOD(PDOStatement,rowCount)1518 PHP_METHOD(PDOStatement, rowCount)
1519 {
1520 ZEND_PARSE_PARAMETERS_NONE();
1521
1522 PHP_STMT_GET_OBJ;
1523 RETURN_LONG(stmt->row_count);
1524 }
1525 /* }}} */
1526
1527 /* {{{ Fetch the error code associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorCode)1528 PHP_METHOD(PDOStatement, errorCode)
1529 {
1530 ZEND_PARSE_PARAMETERS_NONE();
1531
1532 PHP_STMT_GET_OBJ;
1533 if (stmt->error_code[0] == '\0') {
1534 RETURN_NULL();
1535 }
1536
1537 RETURN_STRING(stmt->error_code);
1538 }
1539 /* }}} */
1540
1541 /* {{{ Fetch extended error information associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorInfo)1542 PHP_METHOD(PDOStatement, errorInfo)
1543 {
1544 int error_count;
1545 int error_count_diff = 0;
1546 int error_expected_count = 3;
1547
1548 ZEND_PARSE_PARAMETERS_NONE();
1549
1550 PHP_STMT_GET_OBJ;
1551 array_init(return_value);
1552 add_next_index_string(return_value, stmt->error_code);
1553
1554 if (stmt->dbh->methods->fetch_err) {
1555 stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1556 }
1557
1558 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1559
1560 if (error_expected_count > error_count) {
1561 int current_index;
1562
1563 error_count_diff = error_expected_count - error_count;
1564 for (current_index = 0; current_index < error_count_diff; current_index++) {
1565 add_next_index_null(return_value);
1566 }
1567 }
1568 }
1569 /* }}} */
1570
1571 /* {{{ Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1572 PHP_METHOD(PDOStatement, setAttribute)
1573 {
1574 zend_long attr;
1575 zval *value = NULL;
1576
1577 ZEND_PARSE_PARAMETERS_START(2, 2)
1578 Z_PARAM_LONG(attr)
1579 Z_PARAM_ZVAL_OR_NULL(value)
1580 ZEND_PARSE_PARAMETERS_END();
1581
1582 PHP_STMT_GET_OBJ;
1583
1584 /* Driver hasn't registered a function for setting attributes */
1585 if (!stmt->methods->set_attribute) {
1586 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1587 RETURN_FALSE;
1588 }
1589
1590 PDO_STMT_CLEAR_ERR();
1591 if (stmt->methods->set_attribute(stmt, attr, value)) {
1592 RETURN_TRUE;
1593 }
1594
1595 /* Error while setting attribute */
1596 PDO_HANDLE_STMT_ERR();
1597 RETURN_FALSE;
1598 }
1599 /* }}} */
1600
1601 /* {{{ Get an attribute */
1602
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1603 static bool generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1604 {
1605 switch (attr) {
1606 case PDO_ATTR_EMULATE_PREPARES:
1607 RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1608 return 1;
1609 }
1610 return 0;
1611 }
1612
PHP_METHOD(PDOStatement,getAttribute)1613 PHP_METHOD(PDOStatement, getAttribute)
1614 {
1615 zend_long attr;
1616
1617 ZEND_PARSE_PARAMETERS_START(1, 1)
1618 Z_PARAM_LONG(attr)
1619 ZEND_PARSE_PARAMETERS_END();
1620
1621 PHP_STMT_GET_OBJ;
1622 if (!stmt->methods->get_attribute) {
1623 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1624 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1625 "This driver doesn't support getting attributes");
1626 RETURN_FALSE;
1627 }
1628 return;
1629 }
1630
1631 PDO_STMT_CLEAR_ERR();
1632 switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1633 case -1:
1634 PDO_HANDLE_STMT_ERR();
1635 RETURN_FALSE;
1636
1637 case 0:
1638 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1639 /* XXX: should do something better here */
1640 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1641 "driver doesn't support getting that attribute");
1642 RETURN_FALSE;
1643 }
1644 return;
1645
1646 default:
1647 return;
1648 }
1649 }
1650 /* }}} */
1651
1652 /* {{{ Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1653 PHP_METHOD(PDOStatement, columnCount)
1654 {
1655 ZEND_PARSE_PARAMETERS_NONE();
1656
1657 PHP_STMT_GET_OBJ;
1658 RETURN_LONG(stmt->column_count);
1659 }
1660 /* }}} */
1661
1662 /* {{{ Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1663 PHP_METHOD(PDOStatement, getColumnMeta)
1664 {
1665 zend_long colno;
1666 struct pdo_column_data *col;
1667
1668 ZEND_PARSE_PARAMETERS_START(1, 1)
1669 Z_PARAM_LONG(colno)
1670 ZEND_PARSE_PARAMETERS_END();
1671
1672 PHP_STMT_GET_OBJ;
1673 if (colno < 0) {
1674 zend_argument_value_error(1, "must be greater than or equal to 0");
1675 RETURN_THROWS();
1676 }
1677
1678 if (!stmt->methods->get_column_meta) {
1679 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1680 RETURN_FALSE;
1681 }
1682
1683 PDO_STMT_CLEAR_ERR();
1684 if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1685 PDO_HANDLE_STMT_ERR();
1686 RETURN_FALSE;
1687 }
1688
1689 /* add stock items */
1690 col = &stmt->columns[colno];
1691 add_assoc_str(return_value, "name", zend_string_copy(col->name));
1692 add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1693 add_assoc_long(return_value, "precision", col->precision);
1694 }
1695 /* }}} */
1696
1697 /* {{{ Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1698
pdo_stmt_setup_fetch_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,zval * args,uint32_t variadic_num_args)1699 bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num,
1700 zval *args, uint32_t variadic_num_args)
1701 {
1702 int flags = 0;
1703 uint32_t arg1_arg_num = mode_arg_num + 1;
1704 uint32_t constructor_arg_num = mode_arg_num + 2;
1705 uint32_t total_num_args = mode_arg_num + variadic_num_args;
1706
1707 switch (stmt->default_fetch_type) {
1708 case PDO_FETCH_INTO:
1709 if (!Z_ISUNDEF(stmt->fetch.into)) {
1710 zval_ptr_dtor(&stmt->fetch.into);
1711 ZVAL_UNDEF(&stmt->fetch.into);
1712 }
1713 break;
1714 default:
1715 ;
1716 }
1717
1718 stmt->default_fetch_type = PDO_FETCH_BOTH;
1719
1720 flags = mode & PDO_FETCH_FLAGS;
1721
1722 if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {
1723 return false;
1724 }
1725
1726 switch (mode & ~PDO_FETCH_FLAGS) {
1727 case PDO_FETCH_USE_DEFAULT:
1728 case PDO_FETCH_LAZY:
1729 case PDO_FETCH_ASSOC:
1730 case PDO_FETCH_NUM:
1731 case PDO_FETCH_BOTH:
1732 case PDO_FETCH_OBJ:
1733 case PDO_FETCH_BOUND:
1734 case PDO_FETCH_NAMED:
1735 case PDO_FETCH_KEY_PAIR:
1736 if (variadic_num_args != 0) {
1737 zend_string *func = get_active_function_or_method_name();
1738 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1739 ZSTR_VAL(func), mode_arg_num, total_num_args);
1740 zend_string_release(func);
1741 return false;
1742 }
1743 break;
1744
1745 case PDO_FETCH_COLUMN:
1746 if (variadic_num_args != 1) {
1747 zend_string *func = get_active_function_or_method_name();
1748 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1749 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1750 zend_string_release(func);
1751 return false;
1752 }
1753 if (Z_TYPE(args[0]) != IS_LONG) {
1754 zend_argument_type_error(arg1_arg_num, "must be of type int, %s given", zend_zval_type_name(&args[0]));
1755 return false;
1756 }
1757 if (Z_LVAL(args[0]) < 0) {
1758 zend_argument_value_error(arg1_arg_num, "must be greater than or equal to 0");
1759 return false;
1760 }
1761 stmt->fetch.column = Z_LVAL(args[0]);
1762 break;
1763
1764 case PDO_FETCH_CLASS: {
1765 HashTable *constructor_args = NULL;
1766 /* Undef constructor arguments */
1767 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1768 /* Gets its class name from 1st column */
1769 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1770 if (variadic_num_args != 0) {
1771 zend_string *func = get_active_function_or_method_name();
1772 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1773 ZSTR_VAL(func), mode_arg_num, total_num_args);
1774 zend_string_release(func);
1775 return false;
1776 }
1777 stmt->fetch.cls.ce = NULL;
1778 } else {
1779 zend_class_entry *cep;
1780 if (variadic_num_args == 0) {
1781 zend_string *func = get_active_function_or_method_name();
1782 zend_argument_count_error("%s() expects at least %d arguments for the fetch mode provided, %d given",
1783 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1784 zend_string_release(func);
1785 return false;
1786 }
1787 /* constructor_arguments can be null/not passed */
1788 if (variadic_num_args > 2) {
1789 zend_string *func = get_active_function_or_method_name();
1790 zend_argument_count_error("%s() expects at most %d arguments for the fetch mode provided, %d given",
1791 ZSTR_VAL(func), constructor_arg_num, total_num_args);
1792 zend_string_release(func);
1793 return false;
1794 }
1795 if (Z_TYPE(args[0]) != IS_STRING) {
1796 zend_argument_type_error(arg1_arg_num, "must be of type string, %s given", zend_zval_type_name(&args[0]));
1797 return false;
1798 }
1799 cep = zend_lookup_class(Z_STR(args[0]));
1800 if (!cep) {
1801 zend_argument_type_error(arg1_arg_num, "must be a valid class");
1802 return false;
1803 }
1804 /* Verify constructor_args (args[1]) is ?array */
1805 /* TODO: Improve logic? */
1806 if (variadic_num_args == 2) {
1807 if (Z_TYPE(args[1]) != IS_NULL && Z_TYPE(args[1]) != IS_ARRAY) {
1808 zend_argument_type_error(constructor_arg_num, "must be of type ?array, %s given",
1809 zend_zval_type_name(&args[1]));
1810 return false;
1811 }
1812 if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) {
1813 constructor_args = Z_ARRVAL(args[1]);
1814 }
1815 }
1816 stmt->fetch.cls.ce = cep;
1817
1818 /* If constructor arguments are present and not empty */
1819 if (constructor_args) {
1820 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(constructor_args));
1821 }
1822 }
1823
1824 do_fetch_class_prepare(stmt);
1825 break;
1826 }
1827 case PDO_FETCH_INTO:
1828 if (total_num_args != arg1_arg_num) {
1829 zend_string *func = get_active_function_or_method_name();
1830 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1831 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1832 zend_string_release(func);
1833 return false;
1834 }
1835 if (Z_TYPE(args[0]) != IS_OBJECT) {
1836 zend_argument_type_error(arg1_arg_num, "must be of type object, %s given", zend_zval_type_name(&args[0]));
1837 return false;
1838 }
1839
1840 ZVAL_COPY(&stmt->fetch.into, &args[0]);
1841 break;
1842 default:
1843 zend_argument_value_error(mode_arg_num, "must be one of the PDO::FETCH_* constants");
1844 return false;
1845 }
1846
1847 stmt->default_fetch_type = mode;
1848
1849 return true;
1850 }
1851
PHP_METHOD(PDOStatement,setFetchMode)1852 PHP_METHOD(PDOStatement, setFetchMode)
1853 {
1854 zend_long fetch_mode;
1855 zval *args = NULL;
1856 uint32_t num_args = 0;
1857
1858 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l*", &fetch_mode, &args, &num_args) == FAILURE) {
1859 RETURN_THROWS();
1860 }
1861
1862 PHP_STMT_GET_OBJ;
1863
1864 do_fetch_opt_finish(stmt, 1);
1865
1866 if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
1867 RETURN_THROWS();
1868 }
1869
1870 // TODO Void return?
1871 RETURN_TRUE;
1872 }
1873 /* }}} */
1874
1875 /* {{{ Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
1876
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)1877 static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
1878 {
1879 pdo_stmt_reset_columns(stmt);
1880
1881 if (!stmt->methods->next_rowset(stmt)) {
1882 /* Set the executed flag to 0 to reallocate columns on next execute */
1883 stmt->executed = 0;
1884 return 0;
1885 }
1886
1887 pdo_stmt_describe_columns(stmt);
1888
1889 return 1;
1890 }
1891
PHP_METHOD(PDOStatement,nextRowset)1892 PHP_METHOD(PDOStatement, nextRowset)
1893 {
1894 ZEND_PARSE_PARAMETERS_NONE();
1895
1896 PHP_STMT_GET_OBJ;
1897 if (!stmt->methods->next_rowset) {
1898 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
1899 RETURN_FALSE;
1900 }
1901
1902 PDO_STMT_CLEAR_ERR();
1903
1904 if (!pdo_stmt_do_next_rowset(stmt)) {
1905 PDO_HANDLE_STMT_ERR();
1906 RETURN_FALSE;
1907 }
1908
1909 RETURN_TRUE;
1910 }
1911 /* }}} */
1912
1913 /* {{{ Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)1914 PHP_METHOD(PDOStatement, closeCursor)
1915 {
1916 ZEND_PARSE_PARAMETERS_NONE();
1917
1918 PHP_STMT_GET_OBJ;
1919 if (!stmt->methods->cursor_closer) {
1920 /* emulate it by fetching and discarding rows */
1921 do {
1922 while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
1923 ;
1924 if (!stmt->methods->next_rowset) {
1925 break;
1926 }
1927
1928 if (!pdo_stmt_do_next_rowset(stmt)) {
1929 break;
1930 }
1931
1932 } while (1);
1933 stmt->executed = 0;
1934 RETURN_TRUE;
1935 }
1936
1937 PDO_STMT_CLEAR_ERR();
1938
1939 if (!stmt->methods->cursor_closer(stmt)) {
1940 PDO_HANDLE_STMT_ERR();
1941 RETURN_FALSE;
1942 }
1943 stmt->executed = 0;
1944 RETURN_TRUE;
1945 }
1946 /* }}} */
1947
1948 /* {{{ A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)1949 PHP_METHOD(PDOStatement, debugDumpParams)
1950 {
1951 ZEND_PARSE_PARAMETERS_NONE();
1952
1953 php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
1954 struct pdo_bound_param_data *param;
1955
1956 ZEND_PARSE_PARAMETERS_NONE();
1957
1958 PHP_STMT_GET_OBJ;
1959
1960 if (out == NULL) {
1961 RETURN_FALSE;
1962 }
1963
1964 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1965 php_stream_printf(out, "SQL: [%zd] ", ZSTR_LEN(stmt->query_string));
1966 php_stream_write(out, ZSTR_VAL(stmt->query_string), ZSTR_LEN(stmt->query_string));
1967 php_stream_write(out, "\n", 1);
1968
1969 /* show parsed SQL if emulated prepares enabled */
1970 /* pointers will be equal if PDO::query() was invoked */
1971 if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
1972 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1973 php_stream_printf(out, "Sent SQL: [%zd] ", ZSTR_LEN(stmt->active_query_string));
1974 php_stream_write(out, ZSTR_VAL(stmt->active_query_string), ZSTR_LEN(stmt->active_query_string));
1975 php_stream_write(out, "\n", 1);
1976 }
1977
1978 php_stream_printf(out, "Params: %d\n",
1979 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
1980
1981 if (stmt->bound_params) {
1982 zend_ulong num;
1983 zend_string *key = NULL;
1984 ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
1985 if (key) {
1986 php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
1987 ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
1988 } else {
1989 php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
1990 }
1991
1992 php_stream_printf(out,
1993 "paramno=" ZEND_LONG_FMT "\n"
1994 "name=[%zd] \"%.*s\"\n"
1995 "is_param=%d\n"
1996 "param_type=%d\n",
1997 param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
1998 param->name ? ZSTR_VAL(param->name) : "",
1999 param->is_param,
2000 param->param_type);
2001
2002 } ZEND_HASH_FOREACH_END();
2003 }
2004
2005 php_stream_close(out);
2006 }
2007 /* }}} */
2008
PHP_METHOD(PDOStatement,getIterator)2009 PHP_METHOD(PDOStatement, getIterator)
2010 {
2011 if (zend_parse_parameters_none() == FAILURE) {
2012 return;
2013 }
2014
2015 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
2016 }
2017
2018 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2019 static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2020 {
2021 if (zend_string_equals_literal(name, "queryString")) {
2022 zval *query_string = OBJ_PROP_NUM(object, 0);
2023 if (!Z_ISUNDEF_P(query_string)) {
2024 zend_throw_error(NULL, "Property queryString is read only");
2025 return value;
2026 }
2027 }
2028 return zend_std_write_property(object, name, value, cache_slot);
2029 }
2030
dbstmt_prop_delete(zend_object * object,zend_string * name,void ** cache_slot)2031 static void dbstmt_prop_delete(zend_object *object, zend_string *name, void **cache_slot)
2032 {
2033 if (zend_string_equals_literal(name, "queryString")) {
2034 zend_throw_error(NULL, "Property queryString is read only");
2035 } else {
2036 zend_std_unset_property(object, name, cache_slot);
2037 }
2038 }
2039
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2040 static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2041 {
2042 zend_function *fbc = NULL;
2043 zend_string *lc_method_name;
2044 zend_object *object = *object_pp;
2045
2046 lc_method_name = zend_string_tolower(method_name);
2047
2048 if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2049 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2050 /* instance not created by PDO object */
2051 if (!stmt->dbh) {
2052 goto out;
2053 }
2054 /* not a pre-defined method, nor a user-defined method; check
2055 * the driver specific methods */
2056 if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2057 if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2058 PDO_DBH_DRIVER_METHOD_KIND_STMT)
2059 || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2060 goto out;
2061 }
2062 }
2063
2064 if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2065 goto out;
2066 }
2067 /* got it */
2068 }
2069
2070 out:
2071 zend_string_release_ex(lc_method_name, 0);
2072 if (!fbc) {
2073 fbc = zend_std_get_method(object_pp, method_name, key);
2074 }
2075 return fbc;
2076 }
2077
2078 zend_object_handlers pdo_dbstmt_object_handlers;
2079 zend_object_handlers pdo_row_object_handlers;
2080
php_pdo_free_statement(pdo_stmt_t * stmt)2081 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2082 {
2083 if (stmt->bound_params) {
2084 zend_hash_destroy(stmt->bound_params);
2085 FREE_HASHTABLE(stmt->bound_params);
2086 stmt->bound_params = NULL;
2087 }
2088 if (stmt->bound_param_map) {
2089 zend_hash_destroy(stmt->bound_param_map);
2090 FREE_HASHTABLE(stmt->bound_param_map);
2091 stmt->bound_param_map = NULL;
2092 }
2093 if (stmt->bound_columns) {
2094 zend_hash_destroy(stmt->bound_columns);
2095 FREE_HASHTABLE(stmt->bound_columns);
2096 stmt->bound_columns = NULL;
2097 }
2098
2099 if (stmt->methods && stmt->methods->dtor) {
2100 stmt->methods->dtor(stmt);
2101 }
2102 if (stmt->active_query_string) {
2103 zend_string_release(stmt->active_query_string);
2104 }
2105 if (stmt->query_string) {
2106 zend_string_release(stmt->query_string);
2107 }
2108
2109 pdo_stmt_reset_columns(stmt);
2110
2111 if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2112 zval_ptr_dtor(&stmt->fetch.into);
2113 ZVAL_UNDEF(&stmt->fetch.into);
2114 }
2115
2116 do_fetch_opt_finish(stmt, 1);
2117
2118 if (!Z_ISUNDEF(stmt->database_object_handle)) {
2119 zval_ptr_dtor(&stmt->database_object_handle);
2120 }
2121 zend_object_std_dtor(&stmt->std);
2122 }
2123
pdo_dbstmt_free_storage(zend_object * std)2124 void pdo_dbstmt_free_storage(zend_object *std)
2125 {
2126 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2127 php_pdo_free_statement(stmt);
2128 }
2129
pdo_dbstmt_new(zend_class_entry * ce)2130 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2131 {
2132 pdo_stmt_t *stmt;
2133
2134 stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2135 zend_object_std_init(&stmt->std, ce);
2136 object_properties_init(&stmt->std, ce);
2137
2138 stmt->std.handlers = &pdo_dbstmt_object_handlers;
2139
2140 return &stmt->std;
2141 }
2142 /* }}} */
2143
2144 /* {{{ statement iterator */
2145
2146 struct php_pdo_iterator {
2147 zend_object_iterator iter;
2148 zend_ulong key;
2149 zval fetch_ahead;
2150 };
2151
pdo_stmt_iter_dtor(zend_object_iterator * iter)2152 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2153 {
2154 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2155
2156 zval_ptr_dtor(&I->iter.data);
2157
2158 if (!Z_ISUNDEF(I->fetch_ahead)) {
2159 zval_ptr_dtor(&I->fetch_ahead);
2160 }
2161 }
2162
pdo_stmt_iter_valid(zend_object_iterator * iter)2163 static int pdo_stmt_iter_valid(zend_object_iterator *iter)
2164 {
2165 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2166
2167 return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2168 }
2169
pdo_stmt_iter_get_data(zend_object_iterator * iter)2170 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2171 {
2172 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2173
2174 /* sanity */
2175 if (Z_ISUNDEF(I->fetch_ahead)) {
2176 return NULL;
2177 }
2178
2179 return &I->fetch_ahead;
2180 }
2181
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2182 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2183 {
2184 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2185
2186 if (I->key == (zend_ulong)-1) {
2187 ZVAL_NULL(key);
2188 } else {
2189 ZVAL_LONG(key, I->key);
2190 }
2191 }
2192
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2193 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2194 {
2195 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2196 pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2197
2198 if (!Z_ISUNDEF(I->fetch_ahead)) {
2199 zval_ptr_dtor(&I->fetch_ahead);
2200 }
2201
2202 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2203 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2204
2205 PDO_HANDLE_STMT_ERR();
2206 I->key = (zend_ulong)-1;
2207 ZVAL_UNDEF(&I->fetch_ahead);
2208
2209 return;
2210 }
2211
2212 I->key++;
2213 }
2214
2215 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2216 pdo_stmt_iter_dtor,
2217 pdo_stmt_iter_valid,
2218 pdo_stmt_iter_get_data,
2219 pdo_stmt_iter_get_key,
2220 pdo_stmt_iter_move_forwards,
2221 NULL,
2222 NULL,
2223 NULL, /* get_gc */
2224 };
2225
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2226 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2227 {
2228 if (by_ref) {
2229 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2230 return NULL;
2231 }
2232
2233 pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2234 if (!stmt->dbh) {
2235 zend_throw_error(NULL, "PDO object is uninitialized");
2236 return NULL;
2237 }
2238
2239 struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2240 zend_iterator_init(&I->iter);
2241 I->iter.funcs = &pdo_stmt_iter_funcs;
2242 Z_ADDREF_P(object);
2243 ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2244
2245 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2246 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2247 PDO_HANDLE_STMT_ERR();
2248 I->key = (zend_ulong)-1;
2249 ZVAL_UNDEF(&I->fetch_ahead);
2250 }
2251
2252 return &I->iter;
2253 }
2254
2255 /* }}} */
2256
2257 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2258
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2259 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2260 {
2261 pdo_row_t *row = (pdo_row_t *)object;
2262 pdo_stmt_t *stmt = row->stmt;
2263 int colno = -1;
2264 zend_long lval;
2265 ZEND_ASSERT(stmt);
2266
2267 ZVAL_NULL(rv);
2268 if (zend_string_equals_literal(name, "queryString")) {
2269 return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2270 } else if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2271 if (lval >= 0 && lval < stmt->column_count) {
2272 fetch_value(stmt, rv, lval, NULL);
2273 }
2274 } else {
2275 /* TODO: replace this with a hash of available column names to column
2276 * numbers */
2277 for (colno = 0; colno < stmt->column_count; colno++) {
2278 if (zend_string_equals(stmt->columns[colno].name, name)) {
2279 fetch_value(stmt, rv, colno, NULL);
2280 return rv;
2281 }
2282 }
2283 }
2284
2285 return rv;
2286 }
2287
row_dim_read(zend_object * object,zval * member,int type,zval * rv)2288 static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv)
2289 {
2290 pdo_row_t *row = (pdo_row_t *)object;
2291 pdo_stmt_t *stmt = row->stmt;
2292 int colno = -1;
2293 zend_long lval;
2294 ZEND_ASSERT(stmt);
2295
2296 ZVAL_NULL(rv);
2297 if (Z_TYPE_P(member) == IS_LONG) {
2298 if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2299 fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2300 }
2301 } else if (Z_TYPE_P(member) == IS_STRING
2302 && is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2303 if (lval >= 0 && lval < stmt->column_count) {
2304 fetch_value(stmt, rv, lval, NULL);
2305 }
2306 } else {
2307 if (!try_convert_to_string(member)) {
2308 return &EG(uninitialized_zval);
2309 }
2310
2311 if (zend_string_equals_literal(Z_STR_P(member), "queryString")) {
2312 return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv);
2313 }
2314
2315 /* TODO: replace this with a hash of available column names to column
2316 * numbers */
2317 for (colno = 0; colno < stmt->column_count; colno++) {
2318 if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2319 fetch_value(stmt, rv, colno, NULL);
2320 return rv;
2321 }
2322 }
2323 }
2324
2325 return rv;
2326 }
2327
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2328 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2329 {
2330 zend_throw_error(NULL, "Cannot write to PDORow property");
2331 return value;
2332 }
2333
row_dim_write(zend_object * object,zval * member,zval * value)2334 static void row_dim_write(zend_object *object, zval *member, zval *value)
2335 {
2336 zend_throw_error(NULL, "Cannot write to PDORow offset");
2337 }
2338
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2339 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2340 {
2341 pdo_row_t *row = (pdo_row_t *)object;
2342 pdo_stmt_t *stmt = row->stmt;
2343 int colno = -1;
2344 zend_long lval;
2345 ZEND_ASSERT(stmt);
2346
2347 if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2348 return lval >=0 && lval < stmt->column_count;
2349 }
2350
2351 /* TODO: replace this with a hash of available column names to column
2352 * numbers */
2353 for (colno = 0; colno < stmt->column_count; colno++) {
2354 if (zend_string_equals(stmt->columns[colno].name, name)) {
2355 int res;
2356 zval val;
2357
2358 fetch_value(stmt, &val, colno, NULL);
2359 res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2360 zval_ptr_dtor_nogc(&val);
2361
2362 return res;
2363 }
2364 }
2365
2366 return 0;
2367 }
2368
row_dim_exists(zend_object * object,zval * member,int check_empty)2369 static int row_dim_exists(zend_object *object, zval *member, int check_empty)
2370 {
2371 pdo_row_t *row = (pdo_row_t *)object;
2372 pdo_stmt_t *stmt = row->stmt;
2373 int colno = -1;
2374 zend_long lval;
2375 ZEND_ASSERT(stmt);
2376
2377 if (Z_TYPE_P(member) == IS_LONG) {
2378 return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2379 } else if (Z_TYPE_P(member) == IS_STRING) {
2380 if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2381 return lval >=0 && lval < stmt->column_count;
2382 }
2383 } else {
2384 if (!try_convert_to_string(member)) {
2385 return 0;
2386 }
2387 }
2388
2389 /* TODO: replace this with a hash of available column names to column
2390 * numbers */
2391 for (colno = 0; colno < stmt->column_count; colno++) {
2392 if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2393 int res;
2394 zval val;
2395
2396 fetch_value(stmt, &val, colno, NULL);
2397 res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2398 zval_ptr_dtor_nogc(&val);
2399
2400 return res;
2401 }
2402 }
2403
2404 return 0;
2405 }
2406
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2407 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2408 {
2409 zend_throw_error(NULL, "Cannot unset PDORow property");
2410 }
2411
row_dim_delete(zend_object * object,zval * offset)2412 static void row_dim_delete(zend_object *object, zval *offset)
2413 {
2414 zend_throw_error(NULL, "Cannot unset PDORow offset");
2415 }
2416
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2417 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2418 {
2419 pdo_row_t *row = (pdo_row_t *)object;
2420 pdo_stmt_t *stmt = row->stmt;
2421 HashTable *props;
2422 int i;
2423 ZEND_ASSERT(stmt);
2424
2425 if (purpose != ZEND_PROP_PURPOSE_DEBUG) {
2426 return zend_std_get_properties_for(object, purpose);
2427 }
2428
2429 if (!stmt->std.properties) {
2430 rebuild_object_properties(&stmt->std);
2431 }
2432 props = zend_array_dup(stmt->std.properties);
2433 for (i = 0; i < stmt->column_count; i++) {
2434 if (zend_string_equals_literal(stmt->columns[i].name, "queryString")) {
2435 continue;
2436 }
2437
2438 zval val;
2439 fetch_value(stmt, &val, i, NULL);
2440
2441 zend_hash_update(props, stmt->columns[i].name, &val);
2442 }
2443 return props;
2444 }
2445
row_get_ctor(zend_object * object)2446 static zend_function *row_get_ctor(zend_object *object)
2447 {
2448 zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2449 return NULL;
2450 }
2451
pdo_row_free_storage(zend_object * std)2452 void pdo_row_free_storage(zend_object *std)
2453 {
2454 pdo_row_t *row = (pdo_row_t *)std;
2455 if (row->stmt) {
2456 ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2457 OBJ_RELEASE(&row->stmt->std);
2458 }
2459 }
2460
pdo_row_new(zend_class_entry * ce)2461 zend_object *pdo_row_new(zend_class_entry *ce)
2462 {
2463 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2464 zend_object_std_init(&row->std, ce);
2465 row->std.handlers = &pdo_row_object_handlers;
2466
2467 return &row->std;
2468 }
2469
pdo_stmt_init(void)2470 void pdo_stmt_init(void)
2471 {
2472 pdo_dbstmt_ce = register_class_PDOStatement(zend_ce_aggregate);
2473 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2474 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2475
2476 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2477 pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2478 pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2479 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2480 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2481 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2482 pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable;
2483 pdo_dbstmt_object_handlers.clone_obj = NULL;
2484
2485 pdo_row_ce = register_class_PDORow();
2486 pdo_row_ce->create_object = pdo_row_new;
2487
2488 memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2489 pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2490 pdo_row_object_handlers.clone_obj = NULL;
2491 pdo_row_object_handlers.get_property_ptr_ptr = NULL;
2492 pdo_row_object_handlers.read_property = row_prop_read;
2493 pdo_row_object_handlers.write_property = row_prop_write;
2494 pdo_row_object_handlers.has_property = row_prop_exists;
2495 pdo_row_object_handlers.unset_property = row_prop_delete;
2496 pdo_row_object_handlers.read_dimension = row_dim_read;
2497 pdo_row_object_handlers.write_dimension = row_dim_write;
2498 pdo_row_object_handlers.has_dimension = row_dim_exists;
2499 pdo_row_object_handlers.unset_dimension = row_dim_delete;
2500 pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2501 pdo_row_object_handlers.get_constructor = row_get_ctor;
2502 pdo_row_object_handlers.compare = zend_objects_not_comparable;
2503 }
2504