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) || how == PDO_FETCH_KEY_PAIR ||
1381 (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1382 ) {
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 (strncmp(stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) {
1555 if (stmt->dbh->methods->fetch_err) {
1556 stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1557 }
1558 }
1559
1560 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1561
1562 if (error_expected_count > error_count) {
1563 int current_index;
1564
1565 error_count_diff = error_expected_count - error_count;
1566 for (current_index = 0; current_index < error_count_diff; current_index++) {
1567 add_next_index_null(return_value);
1568 }
1569 }
1570 }
1571 /* }}} */
1572
1573 /* {{{ Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1574 PHP_METHOD(PDOStatement, setAttribute)
1575 {
1576 zend_long attr;
1577 zval *value = NULL;
1578
1579 ZEND_PARSE_PARAMETERS_START(2, 2)
1580 Z_PARAM_LONG(attr)
1581 Z_PARAM_ZVAL_OR_NULL(value)
1582 ZEND_PARSE_PARAMETERS_END();
1583
1584 PHP_STMT_GET_OBJ;
1585
1586 /* Driver hasn't registered a function for setting attributes */
1587 if (!stmt->methods->set_attribute) {
1588 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1589 RETURN_FALSE;
1590 }
1591
1592 PDO_STMT_CLEAR_ERR();
1593 if (stmt->methods->set_attribute(stmt, attr, value)) {
1594 RETURN_TRUE;
1595 }
1596
1597 /* Error while setting attribute */
1598 PDO_HANDLE_STMT_ERR();
1599 RETURN_FALSE;
1600 }
1601 /* }}} */
1602
1603 /* {{{ Get an attribute */
1604
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1605 static bool generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1606 {
1607 switch (attr) {
1608 case PDO_ATTR_EMULATE_PREPARES:
1609 RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1610 return 1;
1611 }
1612 return 0;
1613 }
1614
PHP_METHOD(PDOStatement,getAttribute)1615 PHP_METHOD(PDOStatement, getAttribute)
1616 {
1617 zend_long attr;
1618
1619 ZEND_PARSE_PARAMETERS_START(1, 1)
1620 Z_PARAM_LONG(attr)
1621 ZEND_PARSE_PARAMETERS_END();
1622
1623 PHP_STMT_GET_OBJ;
1624 if (!stmt->methods->get_attribute) {
1625 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1626 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1627 "This driver doesn't support getting attributes");
1628 RETURN_FALSE;
1629 }
1630 return;
1631 }
1632
1633 PDO_STMT_CLEAR_ERR();
1634 switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1635 case -1:
1636 PDO_HANDLE_STMT_ERR();
1637 RETURN_FALSE;
1638
1639 case 0:
1640 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1641 /* XXX: should do something better here */
1642 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1643 "driver doesn't support getting that attribute");
1644 RETURN_FALSE;
1645 }
1646 return;
1647
1648 default:
1649 return;
1650 }
1651 }
1652 /* }}} */
1653
1654 /* {{{ Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1655 PHP_METHOD(PDOStatement, columnCount)
1656 {
1657 ZEND_PARSE_PARAMETERS_NONE();
1658
1659 PHP_STMT_GET_OBJ;
1660 RETURN_LONG(stmt->column_count);
1661 }
1662 /* }}} */
1663
1664 /* {{{ Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1665 PHP_METHOD(PDOStatement, getColumnMeta)
1666 {
1667 zend_long colno;
1668 struct pdo_column_data *col;
1669
1670 ZEND_PARSE_PARAMETERS_START(1, 1)
1671 Z_PARAM_LONG(colno)
1672 ZEND_PARSE_PARAMETERS_END();
1673
1674 PHP_STMT_GET_OBJ;
1675 if (colno < 0) {
1676 zend_argument_value_error(1, "must be greater than or equal to 0");
1677 RETURN_THROWS();
1678 }
1679
1680 if (!stmt->methods->get_column_meta) {
1681 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1682 RETURN_FALSE;
1683 }
1684
1685 PDO_STMT_CLEAR_ERR();
1686 if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1687 PDO_HANDLE_STMT_ERR();
1688 RETURN_FALSE;
1689 }
1690
1691 /* add stock items */
1692 col = &stmt->columns[colno];
1693 add_assoc_str(return_value, "name", zend_string_copy(col->name));
1694 add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1695 add_assoc_long(return_value, "precision", col->precision);
1696 }
1697 /* }}} */
1698
1699 /* {{{ Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1700
pdo_stmt_setup_fetch_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,zval * args,uint32_t variadic_num_args)1701 bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num,
1702 zval *args, uint32_t variadic_num_args)
1703 {
1704 int flags = 0;
1705 uint32_t arg1_arg_num = mode_arg_num + 1;
1706 uint32_t constructor_arg_num = mode_arg_num + 2;
1707 uint32_t total_num_args = mode_arg_num + variadic_num_args;
1708
1709 switch (stmt->default_fetch_type) {
1710 case PDO_FETCH_INTO:
1711 if (!Z_ISUNDEF(stmt->fetch.into)) {
1712 zval_ptr_dtor(&stmt->fetch.into);
1713 ZVAL_UNDEF(&stmt->fetch.into);
1714 }
1715 break;
1716 default:
1717 ;
1718 }
1719
1720 stmt->default_fetch_type = PDO_FETCH_BOTH;
1721
1722 flags = mode & PDO_FETCH_FLAGS;
1723
1724 if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {
1725 return false;
1726 }
1727
1728 switch (mode & ~PDO_FETCH_FLAGS) {
1729 case PDO_FETCH_USE_DEFAULT:
1730 case PDO_FETCH_LAZY:
1731 case PDO_FETCH_ASSOC:
1732 case PDO_FETCH_NUM:
1733 case PDO_FETCH_BOTH:
1734 case PDO_FETCH_OBJ:
1735 case PDO_FETCH_BOUND:
1736 case PDO_FETCH_NAMED:
1737 case PDO_FETCH_KEY_PAIR:
1738 if (variadic_num_args != 0) {
1739 zend_string *func = get_active_function_or_method_name();
1740 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1741 ZSTR_VAL(func), mode_arg_num, total_num_args);
1742 zend_string_release(func);
1743 return false;
1744 }
1745 break;
1746
1747 case PDO_FETCH_COLUMN:
1748 if (variadic_num_args != 1) {
1749 zend_string *func = get_active_function_or_method_name();
1750 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1751 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1752 zend_string_release(func);
1753 return false;
1754 }
1755 if (Z_TYPE(args[0]) != IS_LONG) {
1756 zend_argument_type_error(arg1_arg_num, "must be of type int, %s given", zend_zval_type_name(&args[0]));
1757 return false;
1758 }
1759 if (Z_LVAL(args[0]) < 0) {
1760 zend_argument_value_error(arg1_arg_num, "must be greater than or equal to 0");
1761 return false;
1762 }
1763 stmt->fetch.column = Z_LVAL(args[0]);
1764 break;
1765
1766 case PDO_FETCH_CLASS: {
1767 HashTable *constructor_args = NULL;
1768 /* Undef constructor arguments */
1769 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1770 /* Gets its class name from 1st column */
1771 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1772 if (variadic_num_args != 0) {
1773 zend_string *func = get_active_function_or_method_name();
1774 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1775 ZSTR_VAL(func), mode_arg_num, total_num_args);
1776 zend_string_release(func);
1777 return false;
1778 }
1779 stmt->fetch.cls.ce = NULL;
1780 } else {
1781 zend_class_entry *cep;
1782 if (variadic_num_args == 0) {
1783 zend_string *func = get_active_function_or_method_name();
1784 zend_argument_count_error("%s() expects at least %d arguments for the fetch mode provided, %d given",
1785 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1786 zend_string_release(func);
1787 return false;
1788 }
1789 /* constructor_arguments can be null/not passed */
1790 if (variadic_num_args > 2) {
1791 zend_string *func = get_active_function_or_method_name();
1792 zend_argument_count_error("%s() expects at most %d arguments for the fetch mode provided, %d given",
1793 ZSTR_VAL(func), constructor_arg_num, total_num_args);
1794 zend_string_release(func);
1795 return false;
1796 }
1797 if (Z_TYPE(args[0]) != IS_STRING) {
1798 zend_argument_type_error(arg1_arg_num, "must be of type string, %s given", zend_zval_type_name(&args[0]));
1799 return false;
1800 }
1801 cep = zend_lookup_class(Z_STR(args[0]));
1802 if (!cep) {
1803 zend_argument_type_error(arg1_arg_num, "must be a valid class");
1804 return false;
1805 }
1806 /* Verify constructor_args (args[1]) is ?array */
1807 /* TODO: Improve logic? */
1808 if (variadic_num_args == 2) {
1809 if (Z_TYPE(args[1]) != IS_NULL && Z_TYPE(args[1]) != IS_ARRAY) {
1810 zend_argument_type_error(constructor_arg_num, "must be of type ?array, %s given",
1811 zend_zval_type_name(&args[1]));
1812 return false;
1813 }
1814 if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) {
1815 constructor_args = Z_ARRVAL(args[1]);
1816 }
1817 }
1818 stmt->fetch.cls.ce = cep;
1819
1820 /* If constructor arguments are present and not empty */
1821 if (constructor_args) {
1822 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(constructor_args));
1823 }
1824 }
1825
1826 do_fetch_class_prepare(stmt);
1827 break;
1828 }
1829 case PDO_FETCH_INTO:
1830 if (total_num_args != arg1_arg_num) {
1831 zend_string *func = get_active_function_or_method_name();
1832 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1833 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1834 zend_string_release(func);
1835 return false;
1836 }
1837 if (Z_TYPE(args[0]) != IS_OBJECT) {
1838 zend_argument_type_error(arg1_arg_num, "must be of type object, %s given", zend_zval_type_name(&args[0]));
1839 return false;
1840 }
1841
1842 ZVAL_COPY(&stmt->fetch.into, &args[0]);
1843 break;
1844 default:
1845 zend_argument_value_error(mode_arg_num, "must be one of the PDO::FETCH_* constants");
1846 return false;
1847 }
1848
1849 stmt->default_fetch_type = mode;
1850
1851 return true;
1852 }
1853
PHP_METHOD(PDOStatement,setFetchMode)1854 PHP_METHOD(PDOStatement, setFetchMode)
1855 {
1856 zend_long fetch_mode;
1857 zval *args = NULL;
1858 uint32_t num_args = 0;
1859
1860 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l*", &fetch_mode, &args, &num_args) == FAILURE) {
1861 RETURN_THROWS();
1862 }
1863
1864 PHP_STMT_GET_OBJ;
1865
1866 do_fetch_opt_finish(stmt, 1);
1867
1868 if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
1869 RETURN_THROWS();
1870 }
1871
1872 // TODO Void return?
1873 RETURN_TRUE;
1874 }
1875 /* }}} */
1876
1877 /* {{{ Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
1878
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)1879 static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
1880 {
1881 pdo_stmt_reset_columns(stmt);
1882
1883 if (!stmt->methods->next_rowset(stmt)) {
1884 /* Set the executed flag to 0 to reallocate columns on next execute */
1885 stmt->executed = 0;
1886 return 0;
1887 }
1888
1889 pdo_stmt_describe_columns(stmt);
1890
1891 return 1;
1892 }
1893
PHP_METHOD(PDOStatement,nextRowset)1894 PHP_METHOD(PDOStatement, nextRowset)
1895 {
1896 ZEND_PARSE_PARAMETERS_NONE();
1897
1898 PHP_STMT_GET_OBJ;
1899 if (!stmt->methods->next_rowset) {
1900 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
1901 RETURN_FALSE;
1902 }
1903
1904 PDO_STMT_CLEAR_ERR();
1905
1906 if (!pdo_stmt_do_next_rowset(stmt)) {
1907 PDO_HANDLE_STMT_ERR();
1908 RETURN_FALSE;
1909 }
1910
1911 RETURN_TRUE;
1912 }
1913 /* }}} */
1914
1915 /* {{{ Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)1916 PHP_METHOD(PDOStatement, closeCursor)
1917 {
1918 ZEND_PARSE_PARAMETERS_NONE();
1919
1920 PHP_STMT_GET_OBJ;
1921 if (!stmt->methods->cursor_closer) {
1922 /* emulate it by fetching and discarding rows */
1923 do {
1924 while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
1925 ;
1926 if (!stmt->methods->next_rowset) {
1927 break;
1928 }
1929
1930 if (!pdo_stmt_do_next_rowset(stmt)) {
1931 break;
1932 }
1933
1934 } while (1);
1935 stmt->executed = 0;
1936 RETURN_TRUE;
1937 }
1938
1939 PDO_STMT_CLEAR_ERR();
1940
1941 if (!stmt->methods->cursor_closer(stmt)) {
1942 PDO_HANDLE_STMT_ERR();
1943 RETURN_FALSE;
1944 }
1945 stmt->executed = 0;
1946 RETURN_TRUE;
1947 }
1948 /* }}} */
1949
1950 /* {{{ A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)1951 PHP_METHOD(PDOStatement, debugDumpParams)
1952 {
1953 ZEND_PARSE_PARAMETERS_NONE();
1954
1955 php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
1956 struct pdo_bound_param_data *param;
1957
1958 ZEND_PARSE_PARAMETERS_NONE();
1959
1960 PHP_STMT_GET_OBJ;
1961
1962 if (out == NULL) {
1963 RETURN_FALSE;
1964 }
1965
1966 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1967 php_stream_printf(out, "SQL: [%zd] ", ZSTR_LEN(stmt->query_string));
1968 php_stream_write(out, ZSTR_VAL(stmt->query_string), ZSTR_LEN(stmt->query_string));
1969 php_stream_write(out, "\n", 1);
1970
1971 /* show parsed SQL if emulated prepares enabled */
1972 /* pointers will be equal if PDO::query() was invoked */
1973 if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
1974 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1975 php_stream_printf(out, "Sent SQL: [%zd] ", ZSTR_LEN(stmt->active_query_string));
1976 php_stream_write(out, ZSTR_VAL(stmt->active_query_string), ZSTR_LEN(stmt->active_query_string));
1977 php_stream_write(out, "\n", 1);
1978 }
1979
1980 php_stream_printf(out, "Params: %d\n",
1981 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
1982
1983 if (stmt->bound_params) {
1984 zend_ulong num;
1985 zend_string *key = NULL;
1986 ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
1987 if (key) {
1988 php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
1989 ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
1990 } else {
1991 php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
1992 }
1993
1994 php_stream_printf(out,
1995 "paramno=" ZEND_LONG_FMT "\n"
1996 "name=[%zd] \"%.*s\"\n"
1997 "is_param=%d\n"
1998 "param_type=%d\n",
1999 param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
2000 param->name ? ZSTR_VAL(param->name) : "",
2001 param->is_param,
2002 param->param_type);
2003
2004 } ZEND_HASH_FOREACH_END();
2005 }
2006
2007 php_stream_close(out);
2008 }
2009 /* }}} */
2010
PHP_METHOD(PDOStatement,getIterator)2011 PHP_METHOD(PDOStatement, getIterator)
2012 {
2013 if (zend_parse_parameters_none() == FAILURE) {
2014 return;
2015 }
2016
2017 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
2018 }
2019
2020 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2021 static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2022 {
2023 if (zend_string_equals_literal(name, "queryString")) {
2024 zval *query_string = OBJ_PROP_NUM(object, 0);
2025 if (!Z_ISUNDEF_P(query_string)) {
2026 zend_throw_error(NULL, "Property queryString is read only");
2027 return value;
2028 }
2029 }
2030 return zend_std_write_property(object, name, value, cache_slot);
2031 }
2032
dbstmt_prop_delete(zend_object * object,zend_string * name,void ** cache_slot)2033 static void dbstmt_prop_delete(zend_object *object, zend_string *name, void **cache_slot)
2034 {
2035 if (zend_string_equals_literal(name, "queryString")) {
2036 zend_throw_error(NULL, "Property queryString is read only");
2037 } else {
2038 zend_std_unset_property(object, name, cache_slot);
2039 }
2040 }
2041
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2042 static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2043 {
2044 zend_function *fbc = NULL;
2045 zend_string *lc_method_name;
2046 zend_object *object = *object_pp;
2047
2048 lc_method_name = zend_string_tolower(method_name);
2049
2050 if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2051 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2052 /* instance not created by PDO object */
2053 if (!stmt->dbh) {
2054 goto out;
2055 }
2056 /* not a pre-defined method, nor a user-defined method; check
2057 * the driver specific methods */
2058 if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2059 if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2060 PDO_DBH_DRIVER_METHOD_KIND_STMT)
2061 || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2062 goto out;
2063 }
2064 }
2065
2066 if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2067 goto out;
2068 }
2069 /* got it */
2070 }
2071
2072 out:
2073 zend_string_release_ex(lc_method_name, 0);
2074 if (!fbc) {
2075 fbc = zend_std_get_method(object_pp, method_name, key);
2076 }
2077 return fbc;
2078 }
2079
dbstmt_get_gc(zend_object * object,zval ** gc_data,int * gc_count)2080 static HashTable *dbstmt_get_gc(zend_object *object, zval **gc_data, int *gc_count)
2081 {
2082 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2083 *gc_data = &stmt->fetch.into;
2084 *gc_count = 1;
2085
2086 /**
2087 * If there are no dynamic properties and the default property is 1 (that is, there is only one property
2088 * of string that does not participate in GC), there is no need to call zend_std_get_properties().
2089 */
2090 if (object->properties == NULL && object->ce->default_properties_count <= 1) {
2091 return NULL;
2092 } else {
2093 return zend_std_get_properties(object);
2094 }
2095 }
2096
2097 zend_object_handlers pdo_dbstmt_object_handlers;
2098 zend_object_handlers pdo_row_object_handlers;
2099
php_pdo_free_statement(pdo_stmt_t * stmt)2100 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2101 {
2102 if (stmt->bound_params) {
2103 zend_hash_destroy(stmt->bound_params);
2104 FREE_HASHTABLE(stmt->bound_params);
2105 stmt->bound_params = NULL;
2106 }
2107 if (stmt->bound_param_map) {
2108 zend_hash_destroy(stmt->bound_param_map);
2109 FREE_HASHTABLE(stmt->bound_param_map);
2110 stmt->bound_param_map = NULL;
2111 }
2112 if (stmt->bound_columns) {
2113 zend_hash_destroy(stmt->bound_columns);
2114 FREE_HASHTABLE(stmt->bound_columns);
2115 stmt->bound_columns = NULL;
2116 }
2117
2118 if (stmt->methods && stmt->methods->dtor) {
2119 stmt->methods->dtor(stmt);
2120 }
2121 if (stmt->active_query_string) {
2122 zend_string_release(stmt->active_query_string);
2123 }
2124 if (stmt->query_string) {
2125 zend_string_release(stmt->query_string);
2126 }
2127
2128 pdo_stmt_reset_columns(stmt);
2129
2130 if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2131 zval_ptr_dtor(&stmt->fetch.into);
2132 ZVAL_UNDEF(&stmt->fetch.into);
2133 }
2134
2135 do_fetch_opt_finish(stmt, 1);
2136
2137 if (!Z_ISUNDEF(stmt->database_object_handle)) {
2138 zval_ptr_dtor(&stmt->database_object_handle);
2139 }
2140 zend_object_std_dtor(&stmt->std);
2141 }
2142
pdo_dbstmt_free_storage(zend_object * std)2143 void pdo_dbstmt_free_storage(zend_object *std)
2144 {
2145 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2146 php_pdo_free_statement(stmt);
2147 }
2148
pdo_dbstmt_new(zend_class_entry * ce)2149 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2150 {
2151 pdo_stmt_t *stmt;
2152
2153 stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2154 zend_object_std_init(&stmt->std, ce);
2155 object_properties_init(&stmt->std, ce);
2156
2157 stmt->std.handlers = &pdo_dbstmt_object_handlers;
2158
2159 return &stmt->std;
2160 }
2161 /* }}} */
2162
2163 /* {{{ statement iterator */
2164
2165 struct php_pdo_iterator {
2166 zend_object_iterator iter;
2167 zend_ulong key;
2168 zval fetch_ahead;
2169 };
2170
pdo_stmt_iter_dtor(zend_object_iterator * iter)2171 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2172 {
2173 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2174
2175 zval_ptr_dtor(&I->iter.data);
2176
2177 if (!Z_ISUNDEF(I->fetch_ahead)) {
2178 zval_ptr_dtor(&I->fetch_ahead);
2179 }
2180 }
2181
pdo_stmt_iter_valid(zend_object_iterator * iter)2182 static int pdo_stmt_iter_valid(zend_object_iterator *iter)
2183 {
2184 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2185
2186 return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2187 }
2188
pdo_stmt_iter_get_data(zend_object_iterator * iter)2189 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2190 {
2191 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2192
2193 /* sanity */
2194 if (Z_ISUNDEF(I->fetch_ahead)) {
2195 return NULL;
2196 }
2197
2198 return &I->fetch_ahead;
2199 }
2200
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2201 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2202 {
2203 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2204
2205 if (I->key == (zend_ulong)-1) {
2206 ZVAL_NULL(key);
2207 } else {
2208 ZVAL_LONG(key, I->key);
2209 }
2210 }
2211
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2212 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2213 {
2214 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2215 pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2216
2217 if (!Z_ISUNDEF(I->fetch_ahead)) {
2218 zval_ptr_dtor(&I->fetch_ahead);
2219 }
2220
2221 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2222 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2223
2224 PDO_HANDLE_STMT_ERR();
2225 I->key = (zend_ulong)-1;
2226 ZVAL_UNDEF(&I->fetch_ahead);
2227
2228 return;
2229 }
2230
2231 I->key++;
2232 }
2233
2234 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2235 pdo_stmt_iter_dtor,
2236 pdo_stmt_iter_valid,
2237 pdo_stmt_iter_get_data,
2238 pdo_stmt_iter_get_key,
2239 pdo_stmt_iter_move_forwards,
2240 NULL,
2241 NULL,
2242 NULL, /* get_gc */
2243 };
2244
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2245 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2246 {
2247 if (by_ref) {
2248 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2249 return NULL;
2250 }
2251
2252 pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2253 if (!stmt->dbh) {
2254 zend_throw_error(NULL, "PDO object is uninitialized");
2255 return NULL;
2256 }
2257
2258 struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2259 zend_iterator_init(&I->iter);
2260 I->iter.funcs = &pdo_stmt_iter_funcs;
2261 Z_ADDREF_P(object);
2262 ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2263
2264 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2265 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2266 PDO_HANDLE_STMT_ERR();
2267 I->key = (zend_ulong)-1;
2268 ZVAL_UNDEF(&I->fetch_ahead);
2269 }
2270
2271 return &I->iter;
2272 }
2273
2274 /* }}} */
2275
2276 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
row_read_column_name(pdo_stmt_t * stmt,zend_string * name,zval * rv)2277 static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv)
2278 {
2279 /* TODO: replace this with a hash of available column names to column numbers */
2280 for (int colno = 0; colno < stmt->column_count; colno++) {
2281 if (zend_string_equals(stmt->columns[colno].name, name)) {
2282 fetch_value(stmt, rv, colno, NULL);
2283 return rv;
2284 }
2285 }
2286 return NULL;
2287 }
2288
row_read_column_number(pdo_stmt_t * stmt,zend_long column,zval * rv)2289 static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv)
2290 {
2291 if (column >= 0 && column < stmt->column_count) {
2292 fetch_value(stmt, rv, column, NULL);
2293 return rv;
2294 }
2295 return NULL;
2296 }
2297
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2298 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2299 {
2300 pdo_row_t *row = (pdo_row_t *)object;
2301 pdo_stmt_t *stmt = row->stmt;
2302 zend_long lval;
2303 zval *retval;
2304 ZEND_ASSERT(stmt);
2305
2306 ZVAL_NULL(rv);
2307 if (zend_string_equals_literal(name, "queryString")) {
2308 return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2309 } else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2310 retval = row_read_column_number(stmt, lval, rv);
2311 } else {
2312 retval = row_read_column_name(stmt, name, rv);
2313 }
2314 if (UNEXPECTED(!retval)) {
2315 // TODO throw an error on master
2316 //if (type != BP_VAR_IS) {
2317 // if (is_numeric) {
2318 // zend_value_error("Invalid column index");
2319 // } else {
2320 // zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name));
2321 // }
2322 //}
2323 //return &EG(uninitialized_zval);
2324 ZVAL_NULL(rv);
2325 return rv;
2326 }
2327 return retval;
2328 }
2329
row_dim_read(zend_object * object,zval * offset,int type,zval * rv)2330 static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv)
2331 {
2332 if (UNEXPECTED(!offset)) {
2333 zend_throw_error(NULL, "Cannot append to PDORow offset");
2334 return NULL;
2335 }
2336 if (Z_TYPE_P(offset) == IS_LONG) {
2337 pdo_row_t *row = (pdo_row_t *)object;
2338 pdo_stmt_t *stmt = row->stmt;
2339 ZEND_ASSERT(stmt);
2340
2341 ZVAL_NULL(rv);
2342 if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) {
2343 fetch_value(stmt, rv, Z_LVAL_P(offset), NULL);
2344 }
2345 return rv;
2346 } else {
2347 zend_string *member = zval_try_get_string(offset);
2348 if (!member) {
2349 return NULL;
2350 }
2351 zval *result = row_prop_read(object, member, type, NULL, rv);
2352 zend_string_release_ex(member, false);
2353 return result;
2354 }
2355 }
2356
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2357 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2358 {
2359 zend_throw_error(NULL, "Cannot write to PDORow property");
2360 return value;
2361 }
2362
row_dim_write(zend_object * object,zval * member,zval * value)2363 static void row_dim_write(zend_object *object, zval *member, zval *value)
2364 {
2365 if (!member) {
2366 zend_throw_error(NULL, "Cannot append to PDORow offset");
2367 } else {
2368 zend_throw_error(NULL, "Cannot write to PDORow offset");
2369 }
2370 }
2371
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2372 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2373 {
2374 pdo_row_t *row = (pdo_row_t *)object;
2375 pdo_stmt_t *stmt = row->stmt;
2376 zend_long lval;
2377 zval tmp_val;
2378 zval *retval = NULL;
2379 ZEND_ASSERT(stmt);
2380
2381 if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2382 retval = row_read_column_number(stmt, lval, &tmp_val);
2383 } else {
2384 retval = row_read_column_name(stmt, name, &tmp_val);
2385 }
2386
2387 if (!retval) {
2388 return false;
2389 }
2390 ZEND_ASSERT(retval == &tmp_val);
2391 int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2392 zval_ptr_dtor_nogc(retval);
2393 return res;
2394 }
2395
row_dim_exists(zend_object * object,zval * offset,int check_empty)2396 static int row_dim_exists(zend_object *object, zval *offset, int check_empty)
2397 {
2398 if (Z_TYPE_P(offset) == IS_LONG) {
2399 pdo_row_t *row = (pdo_row_t *)object;
2400 pdo_stmt_t *stmt = row->stmt;
2401 ZEND_ASSERT(stmt);
2402 zend_long column = Z_LVAL_P(offset);
2403
2404 if (!check_empty) {
2405 return column >= 0 && column < stmt->column_count;
2406 }
2407
2408 zval tmp_val;
2409 zval *retval = row_read_column_number(stmt, column, &tmp_val);
2410 if (!retval) {
2411 return false;
2412 }
2413 ZEND_ASSERT(retval == &tmp_val);
2414 int res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2415 zval_ptr_dtor_nogc(retval);
2416 return res;
2417 } else {
2418 zend_string *member = zval_try_get_string(offset);
2419 if (!member) {
2420 return 0;
2421 }
2422 int result = row_prop_exists(object, member, check_empty, NULL);
2423 zend_string_release_ex(member, false);
2424 return result;
2425 }
2426 }
2427
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2428 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2429 {
2430 zend_throw_error(NULL, "Cannot unset PDORow property");
2431 }
2432
row_dim_delete(zend_object * object,zval * offset)2433 static void row_dim_delete(zend_object *object, zval *offset)
2434 {
2435 zend_throw_error(NULL, "Cannot unset PDORow offset");
2436 }
2437
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2438 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2439 {
2440 pdo_row_t *row = (pdo_row_t *)object;
2441 pdo_stmt_t *stmt = row->stmt;
2442 HashTable *props;
2443 int i;
2444 ZEND_ASSERT(stmt);
2445
2446 if (purpose != ZEND_PROP_PURPOSE_DEBUG) {
2447 return zend_std_get_properties_for(object, purpose);
2448 }
2449
2450 if (!stmt->std.properties) {
2451 rebuild_object_properties(&stmt->std);
2452 }
2453 props = zend_array_dup(stmt->std.properties);
2454 for (i = 0; i < stmt->column_count; i++) {
2455 if (zend_string_equals_literal(stmt->columns[i].name, "queryString")) {
2456 continue;
2457 }
2458
2459 zval val;
2460 fetch_value(stmt, &val, i, NULL);
2461
2462 zend_hash_update(props, stmt->columns[i].name, &val);
2463 }
2464 return props;
2465 }
2466
row_get_ctor(zend_object * object)2467 static zend_function *row_get_ctor(zend_object *object)
2468 {
2469 zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2470 return NULL;
2471 }
2472
pdo_row_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)2473 static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
2474 {
2475 ZEND_IGNORE_VALUE(object);
2476 ZEND_IGNORE_VALUE(name);
2477 ZEND_IGNORE_VALUE(type);
2478 ZEND_IGNORE_VALUE(cache_slot);
2479
2480 return NULL;
2481 }
2482
pdo_row_free_storage(zend_object * std)2483 void pdo_row_free_storage(zend_object *std)
2484 {
2485 pdo_row_t *row = (pdo_row_t *)std;
2486 if (row->stmt) {
2487 ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2488 OBJ_RELEASE(&row->stmt->std);
2489 }
2490 }
2491
pdo_row_new(zend_class_entry * ce)2492 zend_object *pdo_row_new(zend_class_entry *ce)
2493 {
2494 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2495 zend_object_std_init(&row->std, ce);
2496 row->std.handlers = &pdo_row_object_handlers;
2497
2498 return &row->std;
2499 }
2500
pdo_stmt_init(void)2501 void pdo_stmt_init(void)
2502 {
2503 pdo_dbstmt_ce = register_class_PDOStatement(zend_ce_aggregate);
2504 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2505 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2506
2507 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2508 pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2509 pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2510 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2511 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2512 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2513 pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable;
2514 pdo_dbstmt_object_handlers.clone_obj = NULL;
2515 pdo_dbstmt_object_handlers.get_gc = dbstmt_get_gc;
2516
2517 pdo_row_ce = register_class_PDORow();
2518 pdo_row_ce->create_object = pdo_row_new;
2519
2520 memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2521 pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2522 pdo_row_object_handlers.clone_obj = NULL;
2523 pdo_row_object_handlers.get_property_ptr_ptr = pdo_row_get_property_ptr_ptr;
2524 pdo_row_object_handlers.read_property = row_prop_read;
2525 pdo_row_object_handlers.write_property = row_prop_write;
2526 pdo_row_object_handlers.has_property = row_prop_exists;
2527 pdo_row_object_handlers.unset_property = row_prop_delete;
2528 pdo_row_object_handlers.read_dimension = row_dim_read;
2529 pdo_row_object_handlers.write_dimension = row_dim_write;
2530 pdo_row_object_handlers.has_dimension = row_dim_exists;
2531 pdo_row_object_handlers.unset_dimension = row_dim_delete;
2532 pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2533 pdo_row_object_handlers.get_constructor = row_get_ctor;
2534 pdo_row_object_handlers.compare = zend_objects_not_comparable;
2535 }
2536