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