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