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_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 * 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)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 (how == PDO_FETCH_LAZY) {
748 get_lazy_object(stmt, return_value);
749 return 1;
750 }
751
752 RETVAL_FALSE;
753
754 switch (how) {
755 case PDO_FETCH_USE_DEFAULT:
756 case PDO_FETCH_ASSOC:
757 case PDO_FETCH_BOTH:
758 case PDO_FETCH_NUM:
759 case PDO_FETCH_NAMED:
760 if (!return_all) {
761 array_init_size(return_value, stmt->column_count);
762 } else {
763 array_init(return_value);
764 }
765 break;
766
767 case PDO_FETCH_KEY_PAIR:
768 if (stmt->column_count != 2) {
769 /* TODO: Error? */
770 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain exactly 2 columns.");
771 return 0;
772 }
773 if (!return_all) {
774 array_init(return_value);
775 }
776 break;
777
778 case PDO_FETCH_COLUMN:
779 if (colno < 0 ) {
780 zend_value_error("Column index must be greater than or equal to 0");
781 return false;
782 }
783
784 if (colno >= stmt->column_count) {
785 zend_value_error("Invalid column index");
786 return false;
787 }
788
789 if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
790 fetch_value(stmt, return_value, 1, NULL);
791 } else if (flags == PDO_FETCH_GROUP && colno) {
792 fetch_value(stmt, return_value, 0, NULL);
793 } else {
794 fetch_value(stmt, return_value, colno, NULL);
795 }
796 if (!return_all) {
797 return 1;
798 }
799 break;
800
801 case PDO_FETCH_OBJ:
802 object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
803 break;
804
805 case PDO_FETCH_CLASS:
806 if (flags & PDO_FETCH_CLASSTYPE) {
807 zval val;
808 zend_class_entry *cep;
809
810 old_ce = stmt->fetch.cls.ce;
811 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
812 old_arg_count = stmt->fetch.cls.fci.param_count;
813 do_fetch_opt_finish(stmt, 0);
814
815 fetch_value(stmt, &val, i++, NULL);
816 if (Z_TYPE(val) != IS_NULL) {
817 if (!try_convert_to_string(&val)) {
818 return 0;
819 }
820 if ((cep = zend_lookup_class(Z_STR(val))) == NULL) {
821 stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
822 } else {
823 stmt->fetch.cls.ce = cep;
824 }
825 }
826
827 do_fetch_class_prepare(stmt);
828 zval_ptr_dtor_str(&val);
829 }
830 ce = stmt->fetch.cls.ce;
831 /* TODO: Make this an assertion and ensure this is true higher up? */
832 if (!ce) {
833 /* TODO Error? */
834 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified");
835 return 0;
836 }
837 if ((flags & PDO_FETCH_SERIALIZE) == 0) {
838 if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
839 return 0;
840 }
841 if (!stmt->fetch.cls.fci.size) {
842 if (!do_fetch_class_prepare(stmt)) {
843 zval_ptr_dtor(return_value);
844 return 0;
845 }
846 }
847 if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
848 stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
849 stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
850 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
851 /* TODO Error? */
852 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
853 return 0;
854 } else {
855 if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
856 zval_ptr_dtor(&stmt->fetch.cls.retval);
857 ZVAL_UNDEF(&stmt->fetch.cls.retval);
858 }
859 }
860 }
861 }
862 break;
863
864 case PDO_FETCH_INTO:
865 /* TODO: Make this an assertion and ensure this is true higher up? */
866 if (Z_ISUNDEF(stmt->fetch.into)) {
867 /* TODO ArgumentCountError? */
868 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified.");
869 return 0;
870 break;
871 }
872
873 ZVAL_COPY(return_value, &stmt->fetch.into);
874
875 if (Z_OBJ_P(return_value)->ce == ZEND_STANDARD_CLASS_DEF_PTR) {
876 how = PDO_FETCH_OBJ;
877 }
878 break;
879
880 case PDO_FETCH_FUNC:
881 /* TODO: Make this an assertion and ensure this is true higher up? */
882 if (Z_ISUNDEF(stmt->fetch.func.function)) {
883 /* TODO ArgumentCountError? */
884 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified");
885 return 0;
886 }
887 if (!stmt->fetch.func.fci.size) {
888 if (!do_fetch_func_prepare(stmt))
889 {
890 return 0;
891 }
892 }
893 break;
894 EMPTY_SWITCH_DEFAULT_CASE();
895 }
896
897 if (return_all && how != PDO_FETCH_KEY_PAIR) {
898 if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
899 fetch_value(stmt, &grp_val, colno, NULL);
900 } else {
901 fetch_value(stmt, &grp_val, i, NULL);
902 }
903 convert_to_string(&grp_val);
904 if (how == PDO_FETCH_COLUMN) {
905 i = stmt->column_count; /* no more data to fetch */
906 } else {
907 i++;
908 }
909 }
910
911 for (idx = 0; i < stmt->column_count; i++, idx++) {
912 zval val;
913 fetch_value(stmt, &val, i, NULL);
914
915 switch (how) {
916 case PDO_FETCH_ASSOC:
917 zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
918 break;
919
920 case PDO_FETCH_KEY_PAIR:
921 {
922 zval tmp;
923 fetch_value(stmt, &tmp, ++i, NULL);
924
925 if (Z_TYPE(val) == IS_LONG) {
926 zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL(val), &tmp);
927 } else {
928 convert_to_string(&val);
929 zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STR(val), &tmp);
930 }
931 zval_ptr_dtor(&val);
932 return 1;
933 }
934 break;
935
936 case PDO_FETCH_USE_DEFAULT:
937 case PDO_FETCH_BOTH:
938 zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
939 if (zend_hash_index_add(Z_ARRVAL_P(return_value), i, &val) != NULL) {
940 Z_TRY_ADDREF(val);
941 }
942 break;
943
944 case PDO_FETCH_NAMED:
945 /* already have an item with this name? */
946 {
947 zval *curr_val;
948 if ((curr_val = zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name))) {
949 zval arr;
950 if (Z_TYPE_P(curr_val) != IS_ARRAY) {
951 /* a little bit of black magic here:
952 * we're creating a new array and swapping it for the
953 * zval that's already stored in the hash under the name
954 * we want. We then add that zval to the array.
955 * This is effectively the same thing as:
956 * if (!is_array($hash[$name])) {
957 * $hash[$name] = array($hash[$name]);
958 * }
959 * */
960 zval cur;
961
962 array_init(&arr);
963
964 ZVAL_COPY_VALUE(&cur, curr_val);
965 ZVAL_COPY_VALUE(curr_val, &arr);
966
967 zend_hash_next_index_insert_new(Z_ARRVAL(arr), &cur);
968 } else {
969 ZVAL_COPY_VALUE(&arr, curr_val);
970 }
971 zend_hash_next_index_insert_new(Z_ARRVAL(arr), &val);
972 } else {
973 zend_hash_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
974 }
975 }
976 break;
977
978 case PDO_FETCH_NUM:
979 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &val);
980 break;
981
982 case PDO_FETCH_OBJ:
983 case PDO_FETCH_INTO:
984 zend_update_property_ex(NULL, Z_OBJ_P(return_value),
985 stmt->columns[i].name,
986 &val);
987 zval_ptr_dtor(&val);
988 break;
989
990 case PDO_FETCH_CLASS:
991 if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
992 zend_update_property_ex(ce, Z_OBJ_P(return_value),
993 stmt->columns[i].name,
994 &val);
995 zval_ptr_dtor(&val);
996 } else {
997 if (!ce->unserialize) {
998 zval_ptr_dtor(&val);
999 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1000 return 0;
1001 } 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) {
1002 zval_ptr_dtor(&val);
1003 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1004 zval_ptr_dtor(return_value);
1005 ZVAL_NULL(return_value);
1006 return 0;
1007 } else {
1008 zval_ptr_dtor(&val);
1009 }
1010 }
1011 break;
1012
1013 case PDO_FETCH_FUNC:
1014 ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val);
1015 ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]);
1016 break;
1017
1018 default:
1019 zval_ptr_dtor(&val);
1020 zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants");
1021 return 0;
1022 }
1023 }
1024
1025 switch (how) {
1026 case PDO_FETCH_CLASS:
1027 if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1028 stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
1029 stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
1030 if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
1031 /* TODO Error? */
1032 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
1033 return 0;
1034 } else {
1035 if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
1036 zval_ptr_dtor(&stmt->fetch.cls.retval);
1037 }
1038 }
1039 }
1040 if (flags & PDO_FETCH_CLASSTYPE) {
1041 do_fetch_opt_finish(stmt, 0);
1042 stmt->fetch.cls.ce = old_ce;
1043 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1044 stmt->fetch.cls.fci.param_count = old_arg_count;
1045 }
1046 break;
1047
1048 case PDO_FETCH_FUNC:
1049 stmt->fetch.func.fci.param_count = idx;
1050 stmt->fetch.func.fci.retval = &retval;
1051 if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc) == FAILURE) {
1052 /* TODO Error? */
1053 pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function");
1054 return 0;
1055 } else {
1056 if (return_all) {
1057 zval_ptr_dtor(return_value); /* we don't need that */
1058 ZVAL_COPY_VALUE(return_value, &retval);
1059 } else if (!Z_ISUNDEF(retval)) {
1060 ZVAL_COPY_VALUE(return_value, &retval);
1061 }
1062 }
1063 while (idx--) {
1064 zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1065 }
1066 break;
1067
1068 default:
1069 break;
1070 }
1071
1072 if (return_all) {
1073 if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1074 zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), return_value);
1075 } else {
1076 zval grp;
1077 if ((pgrp = zend_symtable_find(Z_ARRVAL_P(return_all), Z_STR(grp_val))) == NULL) {
1078 array_init(&grp);
1079 zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), &grp);
1080 } else {
1081 ZVAL_COPY_VALUE(&grp, pgrp);
1082 }
1083 zend_hash_next_index_insert(Z_ARRVAL(grp), return_value);
1084 }
1085 zval_ptr_dtor_str(&grp_val);
1086 }
1087
1088 return 1;
1089 }
1090 /* }}} */
1091
pdo_stmt_verify_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,bool fetch_all)1092 static bool pdo_stmt_verify_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num, bool fetch_all) /* {{{ */
1093 {
1094 int flags = mode & PDO_FETCH_FLAGS;
1095
1096 mode = mode & ~PDO_FETCH_FLAGS;
1097
1098 if (mode < 0 || mode > PDO_FETCH__MAX) {
1099 zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1100 return 0;
1101 }
1102
1103 if (mode == PDO_FETCH_USE_DEFAULT) {
1104 flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1105 mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1106 }
1107
1108 switch(mode) {
1109 case PDO_FETCH_FUNC:
1110 if (!fetch_all) {
1111 zend_value_error("Can only use PDO::FETCH_FUNC in PDOStatement::fetchAll()");
1112 return 0;
1113 }
1114 return 1;
1115
1116 case PDO_FETCH_LAZY:
1117 if (fetch_all) {
1118 zend_argument_value_error(mode_arg_num, "cannot be PDO::FETCH_LAZY in PDOStatement::fetchAll()");
1119 return 0;
1120 }
1121 ZEND_FALLTHROUGH;
1122 default:
1123 if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1124 zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_SERIALIZE with PDO::FETCH_CLASS");
1125 return 0;
1126 }
1127 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1128 zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_CLASSTYPE with PDO::FETCH_CLASS");
1129 return 0;
1130 }
1131 if (mode >= PDO_FETCH__MAX) {
1132 zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1133 return 0;
1134 }
1135 ZEND_FALLTHROUGH;
1136
1137 case PDO_FETCH_CLASS:
1138 if (flags & PDO_FETCH_SERIALIZE) {
1139 php_error_docref(NULL, E_DEPRECATED, "The PDO::FETCH_SERIALIZE mode is deprecated");
1140 }
1141 return 1;
1142 }
1143 }
1144 /* }}} */
1145
1146 /* {{{ Fetches the next row and returns it, or false if there are no more rows */
PHP_METHOD(PDOStatement,fetch)1147 PHP_METHOD(PDOStatement, fetch)
1148 {
1149 zend_long how = PDO_FETCH_USE_DEFAULT;
1150 zend_long ori = PDO_FETCH_ORI_NEXT;
1151 zend_long off = 0;
1152
1153 ZEND_PARSE_PARAMETERS_START(0, 3)
1154 Z_PARAM_OPTIONAL
1155 Z_PARAM_LONG(how)
1156 Z_PARAM_LONG(ori)
1157 Z_PARAM_LONG(off)
1158 ZEND_PARSE_PARAMETERS_END();
1159
1160 PHP_STMT_GET_OBJ;
1161 PDO_STMT_CLEAR_ERR();
1162
1163 if (!pdo_stmt_verify_mode(stmt, how, 1, false)) {
1164 RETURN_THROWS();
1165 }
1166
1167 if (!do_fetch(stmt, return_value, how, ori, off, NULL)) {
1168 PDO_HANDLE_STMT_ERR();
1169 RETURN_FALSE;
1170 }
1171 }
1172 /* }}} */
1173
1174 /* {{{ Fetches the next row and returns it as an object. */
PHP_METHOD(PDOStatement,fetchObject)1175 PHP_METHOD(PDOStatement, fetchObject)
1176 {
1177 zend_class_entry *ce = NULL;
1178 zend_class_entry *old_ce;
1179 zval old_ctor_args, *ctor_args = NULL;
1180 int old_arg_count;
1181
1182 ZEND_PARSE_PARAMETERS_START(0, 2)
1183 Z_PARAM_OPTIONAL
1184 Z_PARAM_CLASS_OR_NULL(ce)
1185 Z_PARAM_ARRAY(ctor_args)
1186 ZEND_PARSE_PARAMETERS_END();
1187
1188 PHP_STMT_GET_OBJ;
1189 PDO_STMT_CLEAR_ERR();
1190
1191 old_ce = stmt->fetch.cls.ce;
1192 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1193 old_arg_count = stmt->fetch.cls.fci.param_count;
1194
1195 do_fetch_opt_finish(stmt, 0);
1196
1197 if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1198 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(Z_ARRVAL_P(ctor_args)));
1199 } else {
1200 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1201 }
1202 if (ce) {
1203 stmt->fetch.cls.ce = ce;
1204 } else {
1205 stmt->fetch.cls.ce = zend_standard_class_def;
1206 }
1207
1208 if (!do_fetch(stmt, return_value, PDO_FETCH_CLASS, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
1209 PDO_HANDLE_STMT_ERR();
1210 RETVAL_FALSE;
1211 }
1212 do_fetch_opt_finish(stmt, 1);
1213
1214 stmt->fetch.cls.ce = old_ce;
1215 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1216 stmt->fetch.cls.fci.param_count = old_arg_count;
1217 }
1218 /* }}} */
1219
1220 /* {{{ Returns a data of the specified column in the result set. */
PHP_METHOD(PDOStatement,fetchColumn)1221 PHP_METHOD(PDOStatement, fetchColumn)
1222 {
1223 zend_long col_n = 0;
1224
1225 ZEND_PARSE_PARAMETERS_START(0, 1)
1226 Z_PARAM_OPTIONAL
1227 Z_PARAM_LONG(col_n)
1228 ZEND_PARSE_PARAMETERS_END();
1229
1230 PHP_STMT_GET_OBJ;
1231 PDO_STMT_CLEAR_ERR();
1232
1233 if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0)) {
1234 PDO_HANDLE_STMT_ERR();
1235 RETURN_FALSE;
1236 }
1237
1238 fetch_value(stmt, return_value, col_n, NULL);
1239 }
1240 /* }}} */
1241
1242 /* {{{ Returns an array of all of the results. */
PHP_METHOD(PDOStatement,fetchAll)1243 PHP_METHOD(PDOStatement, fetchAll)
1244 {
1245 zend_long how = PDO_FETCH_USE_DEFAULT;
1246 zval data, *return_all = NULL;
1247 zval *arg2 = NULL;
1248 zend_class_entry *old_ce;
1249 zval old_ctor_args, *ctor_args = NULL;
1250 bool error = false;
1251 int flags, old_arg_count;
1252
1253 ZEND_PARSE_PARAMETERS_START(0, 3)
1254 Z_PARAM_OPTIONAL
1255 Z_PARAM_LONG(how)
1256 Z_PARAM_ZVAL_OR_NULL(arg2)
1257 Z_PARAM_ARRAY_OR_NULL(ctor_args)
1258 ZEND_PARSE_PARAMETERS_END();
1259
1260 PHP_STMT_GET_OBJ;
1261 if (!pdo_stmt_verify_mode(stmt, how, 1, true)) {
1262 RETURN_THROWS();
1263 }
1264
1265 old_ce = stmt->fetch.cls.ce;
1266 ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1267 old_arg_count = stmt->fetch.cls.fci.param_count;
1268
1269 do_fetch_opt_finish(stmt, 0);
1270
1271 /* TODO Would be good to reuse part of pdo_stmt_setup_fetch_mode() in some way */
1272
1273 switch (how & ~PDO_FETCH_FLAGS) {
1274 case PDO_FETCH_CLASS:
1275 /* Figure out correct class */
1276 if (arg2) {
1277 if (Z_TYPE_P(arg2) != IS_STRING) {
1278 zend_argument_type_error(2, "must be of type string, %s given", zend_zval_value_name(arg2));
1279 RETURN_THROWS();
1280 }
1281 stmt->fetch.cls.ce = zend_fetch_class(Z_STR_P(arg2), ZEND_FETCH_CLASS_AUTO);
1282 if (!stmt->fetch.cls.ce) {
1283 zend_argument_type_error(2, "must be a valid class");
1284 RETURN_THROWS();
1285 }
1286 } else {
1287 stmt->fetch.cls.ce = zend_standard_class_def;
1288 }
1289
1290 if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args)) > 0) {
1291 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, ctor_args); /* we're not going to free these */
1292 } else {
1293 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1294 }
1295
1296 do_fetch_class_prepare(stmt);
1297 break;
1298
1299 case PDO_FETCH_FUNC: /* Cannot be a default fetch mode */
1300 if (ZEND_NUM_ARGS() != 2) {
1301 zend_string *func = get_active_function_or_method_name();
1302 zend_argument_count_error("%s() expects exactly 2 argument for PDO::FETCH_FUNC, %d given",
1303 ZSTR_VAL(func), ZEND_NUM_ARGS());
1304 zend_string_release(func);
1305 RETURN_THROWS();
1306 }
1307 if (arg2 == NULL) {
1308 /* TODO use "must be of type callable" format? */
1309 zend_argument_type_error(2, "must be a callable, null given");
1310 RETURN_THROWS();
1311 }
1312 /* TODO Check it is a callable? */
1313 ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2);
1314 if (do_fetch_func_prepare(stmt) == false) {
1315 RETURN_THROWS();
1316 }
1317 break;
1318
1319 case PDO_FETCH_COLUMN:
1320 if (ZEND_NUM_ARGS() > 2) {
1321 zend_string *func = get_active_function_or_method_name();
1322 zend_argument_count_error("%s() expects at most 2 argument for the fetch mode provided, %d given",
1323 ZSTR_VAL(func), ZEND_NUM_ARGS());
1324 zend_string_release(func);
1325 RETURN_THROWS();
1326 }
1327 /* Is column index passed? */
1328 if (arg2) {
1329 // Reuse convert_to_long(arg2); ?
1330 if (Z_TYPE_P(arg2) != IS_LONG) {
1331 zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(arg2));
1332 RETURN_THROWS();
1333 }
1334 if (Z_LVAL_P(arg2) < 0) {
1335 zend_argument_value_error(2, "must be greater than or equal to 0");
1336 RETURN_THROWS();
1337 }
1338 stmt->fetch.column = Z_LVAL_P(arg2);
1339 } else {
1340 stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1341 }
1342 break;
1343
1344 default:
1345 /* No support for PDO_FETCH_INTO which takes 2 args??? */
1346 if (ZEND_NUM_ARGS() > 1) {
1347 zend_string *func = get_active_function_or_method_name();
1348 zend_argument_count_error("%s() expects exactly 1 argument for the fetch mode provided, %d given",
1349 ZSTR_VAL(func), ZEND_NUM_ARGS());
1350 zend_string_release(func);
1351 RETURN_THROWS();
1352 }
1353 }
1354
1355 flags = how & PDO_FETCH_FLAGS;
1356
1357 if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1358 flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1359 how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1360 }
1361
1362 PDO_STMT_CLEAR_ERR();
1363 if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1364 (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1365 ) {
1366 array_init(return_value);
1367 return_all = return_value;
1368 }
1369 if (!do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all)) {
1370 error = true;
1371 }
1372
1373 if (!error) {
1374 if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1375 (how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1376 ) {
1377 while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all));
1378 } else {
1379 array_init(return_value);
1380 do {
1381 zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &data);
1382 } while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL));
1383 }
1384 }
1385
1386 do_fetch_opt_finish(stmt, 0);
1387
1388 /* Restore defaults which were changed by PDO_FETCH_CLASS mode */
1389 stmt->fetch.cls.ce = old_ce;
1390 ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1391 stmt->fetch.cls.fci.param_count = old_arg_count;
1392
1393 /* on no results, return an empty array */
1394 if (error) {
1395 PDO_HANDLE_STMT_ERR();
1396 if (Z_TYPE_P(return_value) != IS_ARRAY) {
1397 array_init(return_value);
1398 }
1399 }
1400 }
1401 /* }}} */
1402
register_bound_param(INTERNAL_FUNCTION_PARAMETERS,int is_param)1403 static void register_bound_param(INTERNAL_FUNCTION_PARAMETERS, int is_param) /* {{{ */
1404 {
1405 struct pdo_bound_param_data param;
1406 zend_long param_type = PDO_PARAM_STR;
1407 zval *parameter, *driver_params = NULL;
1408
1409 memset(¶m, 0, sizeof(param));
1410
1411 ZEND_PARSE_PARAMETERS_START(2, 5)
1412 Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1413 Z_PARAM_ZVAL(parameter)
1414 Z_PARAM_OPTIONAL
1415 Z_PARAM_LONG(param_type)
1416 Z_PARAM_LONG(param.max_value_len)
1417 Z_PARAM_ZVAL_OR_NULL(driver_params)
1418 ZEND_PARSE_PARAMETERS_END();
1419
1420 PHP_STMT_GET_OBJ;
1421
1422 param.param_type = (int) param_type;
1423
1424 if (param.name) {
1425 if (ZSTR_LEN(param.name) == 0) {
1426 zend_argument_must_not_be_empty_error(1);
1427 RETURN_THROWS();
1428 }
1429 param.paramno = -1;
1430 } else if (param.paramno > 0) {
1431 --param.paramno; /* make it zero-based internally */
1432 } else {
1433 zend_argument_value_error(1, "must be greater than or equal to 1");
1434 RETURN_THROWS();
1435 }
1436
1437 if (driver_params) {
1438 ZVAL_COPY(¶m.driver_params, driver_params);
1439 }
1440
1441 ZVAL_COPY(¶m.parameter, parameter);
1442 if (!really_register_bound_param(¶m, stmt, is_param)) {
1443 if (!Z_ISUNDEF(param.parameter)) {
1444 zval_ptr_dtor(&(param.parameter));
1445 }
1446
1447 RETURN_FALSE;
1448 }
1449
1450 RETURN_TRUE;
1451 } /* }}} */
1452
1453 /* {{{ 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)1454 PHP_METHOD(PDOStatement, bindValue)
1455 {
1456 struct pdo_bound_param_data param;
1457 zend_long param_type = PDO_PARAM_STR;
1458 zval *parameter;
1459
1460 memset(¶m, 0, sizeof(param));
1461
1462 ZEND_PARSE_PARAMETERS_START(2, 3)
1463 Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1464 Z_PARAM_ZVAL(parameter)
1465 Z_PARAM_OPTIONAL
1466 Z_PARAM_LONG(param_type)
1467 ZEND_PARSE_PARAMETERS_END();
1468
1469 PHP_STMT_GET_OBJ;
1470 param.param_type = (int) param_type;
1471
1472 if (param.name) {
1473 if (ZSTR_LEN(param.name) == 0) {
1474 zend_argument_must_not_be_empty_error(1);
1475 RETURN_THROWS();
1476 }
1477 param.paramno = -1;
1478 } else if (param.paramno > 0) {
1479 --param.paramno; /* make it zero-based internally */
1480 } else {
1481 zend_argument_value_error(1, "must be greater than or equal to 1");
1482 RETURN_THROWS();
1483 }
1484
1485 ZVAL_COPY(¶m.parameter, parameter);
1486 if (!really_register_bound_param(¶m, stmt, TRUE)) {
1487 if (!Z_ISUNDEF(param.parameter)) {
1488 zval_ptr_dtor(&(param.parameter));
1489 ZVAL_UNDEF(¶m.parameter);
1490 }
1491 RETURN_FALSE;
1492 }
1493 RETURN_TRUE;
1494 }
1495 /* }}} */
1496
1497 /* {{{ 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)1498 PHP_METHOD(PDOStatement, bindParam)
1499 {
1500 register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1501 }
1502 /* }}} */
1503
1504 /* {{{ 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)1505 PHP_METHOD(PDOStatement, bindColumn)
1506 {
1507 register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1508 }
1509 /* }}} */
1510
1511 /* {{{ 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)1512 PHP_METHOD(PDOStatement, rowCount)
1513 {
1514 ZEND_PARSE_PARAMETERS_NONE();
1515
1516 PHP_STMT_GET_OBJ;
1517 RETURN_LONG(stmt->row_count);
1518 }
1519 /* }}} */
1520
1521 /* {{{ Fetch the error code associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorCode)1522 PHP_METHOD(PDOStatement, errorCode)
1523 {
1524 ZEND_PARSE_PARAMETERS_NONE();
1525
1526 PHP_STMT_GET_OBJ;
1527 if (stmt->error_code[0] == '\0') {
1528 RETURN_NULL();
1529 }
1530
1531 RETURN_STRING(stmt->error_code);
1532 }
1533 /* }}} */
1534
1535 /* {{{ Fetch extended error information associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorInfo)1536 PHP_METHOD(PDOStatement, errorInfo)
1537 {
1538 int error_count;
1539 int error_count_diff = 0;
1540 int error_expected_count = 3;
1541
1542 ZEND_PARSE_PARAMETERS_NONE();
1543
1544 PHP_STMT_GET_OBJ;
1545 array_init(return_value);
1546 add_next_index_string(return_value, stmt->error_code);
1547
1548 if (strncmp(stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) {
1549 if (stmt->dbh->methods->fetch_err) {
1550 stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1551 }
1552 }
1553
1554 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1555
1556 if (error_expected_count > error_count) {
1557 int current_index;
1558
1559 error_count_diff = error_expected_count - error_count;
1560 for (current_index = 0; current_index < error_count_diff; current_index++) {
1561 add_next_index_null(return_value);
1562 }
1563 }
1564 }
1565 /* }}} */
1566
1567 /* {{{ Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1568 PHP_METHOD(PDOStatement, setAttribute)
1569 {
1570 zend_long attr;
1571 zval *value = NULL;
1572
1573 ZEND_PARSE_PARAMETERS_START(2, 2)
1574 Z_PARAM_LONG(attr)
1575 Z_PARAM_ZVAL_OR_NULL(value)
1576 ZEND_PARSE_PARAMETERS_END();
1577
1578 PHP_STMT_GET_OBJ;
1579
1580 /* Driver hasn't registered a function for setting attributes */
1581 if (!stmt->methods->set_attribute) {
1582 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1583 RETURN_FALSE;
1584 }
1585
1586 PDO_STMT_CLEAR_ERR();
1587 if (stmt->methods->set_attribute(stmt, attr, value)) {
1588 RETURN_TRUE;
1589 }
1590
1591 /* Error while setting attribute */
1592 PDO_HANDLE_STMT_ERR();
1593 RETURN_FALSE;
1594 }
1595 /* }}} */
1596
1597 /* {{{ Get an attribute */
1598
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1599 static bool generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1600 {
1601 switch (attr) {
1602 case PDO_ATTR_EMULATE_PREPARES:
1603 RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1604 return 1;
1605 }
1606 return 0;
1607 }
1608
PHP_METHOD(PDOStatement,getAttribute)1609 PHP_METHOD(PDOStatement, getAttribute)
1610 {
1611 zend_long attr;
1612
1613 ZEND_PARSE_PARAMETERS_START(1, 1)
1614 Z_PARAM_LONG(attr)
1615 ZEND_PARSE_PARAMETERS_END();
1616
1617 PHP_STMT_GET_OBJ;
1618 if (!stmt->methods->get_attribute) {
1619 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1620 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1621 "This driver doesn't support getting attributes");
1622 RETURN_FALSE;
1623 }
1624 return;
1625 }
1626
1627 PDO_STMT_CLEAR_ERR();
1628 switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1629 case -1:
1630 PDO_HANDLE_STMT_ERR();
1631 RETURN_FALSE;
1632
1633 case 0:
1634 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1635 /* XXX: should do something better here */
1636 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1637 "driver doesn't support getting that attribute");
1638 RETURN_FALSE;
1639 }
1640 return;
1641
1642 default:
1643 return;
1644 }
1645 }
1646 /* }}} */
1647
1648 /* {{{ Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1649 PHP_METHOD(PDOStatement, columnCount)
1650 {
1651 ZEND_PARSE_PARAMETERS_NONE();
1652
1653 PHP_STMT_GET_OBJ;
1654 RETURN_LONG(stmt->column_count);
1655 }
1656 /* }}} */
1657
1658 /* {{{ Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1659 PHP_METHOD(PDOStatement, getColumnMeta)
1660 {
1661 zend_long colno;
1662 struct pdo_column_data *col;
1663
1664 ZEND_PARSE_PARAMETERS_START(1, 1)
1665 Z_PARAM_LONG(colno)
1666 ZEND_PARSE_PARAMETERS_END();
1667
1668 PHP_STMT_GET_OBJ;
1669 if (colno < 0) {
1670 zend_argument_value_error(1, "must be greater than or equal to 0");
1671 RETURN_THROWS();
1672 }
1673
1674 if (!stmt->methods->get_column_meta) {
1675 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1676 RETURN_FALSE;
1677 }
1678
1679 PDO_STMT_CLEAR_ERR();
1680 if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1681 PDO_HANDLE_STMT_ERR();
1682 RETURN_FALSE;
1683 }
1684
1685 /* add stock items */
1686 col = &stmt->columns[colno];
1687 add_assoc_str(return_value, "name", zend_string_copy(col->name));
1688 add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1689 add_assoc_long(return_value, "precision", col->precision);
1690 }
1691 /* }}} */
1692
1693 /* {{{ Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1694
pdo_stmt_setup_fetch_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,zval * args,uint32_t variadic_num_args)1695 bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num,
1696 zval *args, uint32_t variadic_num_args)
1697 {
1698 int flags = 0;
1699 uint32_t arg1_arg_num = mode_arg_num + 1;
1700 uint32_t constructor_arg_num = mode_arg_num + 2;
1701 uint32_t total_num_args = mode_arg_num + variadic_num_args;
1702
1703 switch (stmt->default_fetch_type) {
1704 case PDO_FETCH_INTO:
1705 if (!Z_ISUNDEF(stmt->fetch.into)) {
1706 zval_ptr_dtor(&stmt->fetch.into);
1707 ZVAL_UNDEF(&stmt->fetch.into);
1708 }
1709 break;
1710 default:
1711 ;
1712 }
1713
1714 stmt->default_fetch_type = PDO_FETCH_BOTH;
1715
1716 flags = mode & PDO_FETCH_FLAGS;
1717
1718 if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {
1719 return false;
1720 }
1721
1722 switch (mode & ~PDO_FETCH_FLAGS) {
1723 case PDO_FETCH_USE_DEFAULT:
1724 case PDO_FETCH_LAZY:
1725 case PDO_FETCH_ASSOC:
1726 case PDO_FETCH_NUM:
1727 case PDO_FETCH_BOTH:
1728 case PDO_FETCH_OBJ:
1729 case PDO_FETCH_BOUND:
1730 case PDO_FETCH_NAMED:
1731 case PDO_FETCH_KEY_PAIR:
1732 if (variadic_num_args != 0) {
1733 zend_string *func = get_active_function_or_method_name();
1734 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1735 ZSTR_VAL(func), mode_arg_num, total_num_args);
1736 zend_string_release(func);
1737 return false;
1738 }
1739 break;
1740
1741 case PDO_FETCH_COLUMN:
1742 if (variadic_num_args != 1) {
1743 zend_string *func = get_active_function_or_method_name();
1744 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1745 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1746 zend_string_release(func);
1747 return false;
1748 }
1749 if (Z_TYPE(args[0]) != IS_LONG) {
1750 zend_argument_type_error(arg1_arg_num, "must be of type int, %s given", zend_zval_value_name(&args[0]));
1751 return false;
1752 }
1753 if (Z_LVAL(args[0]) < 0) {
1754 zend_argument_value_error(arg1_arg_num, "must be greater than or equal to 0");
1755 return false;
1756 }
1757 stmt->fetch.column = Z_LVAL(args[0]);
1758 break;
1759
1760 case PDO_FETCH_CLASS: {
1761 HashTable *constructor_args = NULL;
1762 /* Undef constructor arguments */
1763 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1764 /* Gets its class name from 1st column */
1765 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1766 if (variadic_num_args != 0) {
1767 zend_string *func = get_active_function_or_method_name();
1768 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1769 ZSTR_VAL(func), mode_arg_num, total_num_args);
1770 zend_string_release(func);
1771 return false;
1772 }
1773 stmt->fetch.cls.ce = NULL;
1774 } else {
1775 zend_class_entry *cep;
1776 if (variadic_num_args == 0) {
1777 zend_string *func = get_active_function_or_method_name();
1778 zend_argument_count_error("%s() expects at least %d arguments for the fetch mode provided, %d given",
1779 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1780 zend_string_release(func);
1781 return false;
1782 }
1783 /* constructor_arguments can be null/not passed */
1784 if (variadic_num_args > 2) {
1785 zend_string *func = get_active_function_or_method_name();
1786 zend_argument_count_error("%s() expects at most %d arguments for the fetch mode provided, %d given",
1787 ZSTR_VAL(func), constructor_arg_num, total_num_args);
1788 zend_string_release(func);
1789 return false;
1790 }
1791 if (Z_TYPE(args[0]) != IS_STRING) {
1792 zend_argument_type_error(arg1_arg_num, "must be of type string, %s given", zend_zval_value_name(&args[0]));
1793 return false;
1794 }
1795 cep = zend_lookup_class(Z_STR(args[0]));
1796 if (!cep) {
1797 zend_argument_type_error(arg1_arg_num, "must be a valid class");
1798 return false;
1799 }
1800 /* Verify constructor_args (args[1]) is ?array */
1801 /* TODO: Improve logic? */
1802 if (variadic_num_args == 2) {
1803 if (Z_TYPE(args[1]) != IS_NULL && Z_TYPE(args[1]) != IS_ARRAY) {
1804 zend_argument_type_error(constructor_arg_num, "must be of type ?array, %s given",
1805 zend_zval_value_name(&args[1]));
1806 return false;
1807 }
1808 if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) {
1809 constructor_args = Z_ARRVAL(args[1]);
1810 }
1811 }
1812 stmt->fetch.cls.ce = cep;
1813
1814 /* If constructor arguments are present and not empty */
1815 if (constructor_args) {
1816 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(constructor_args));
1817 }
1818 }
1819
1820 do_fetch_class_prepare(stmt);
1821 break;
1822 }
1823 case PDO_FETCH_INTO:
1824 if (total_num_args != arg1_arg_num) {
1825 zend_string *func = get_active_function_or_method_name();
1826 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1827 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1828 zend_string_release(func);
1829 return false;
1830 }
1831 if (Z_TYPE(args[0]) != IS_OBJECT) {
1832 zend_argument_type_error(arg1_arg_num, "must be of type object, %s given", zend_zval_value_name(&args[0]));
1833 return false;
1834 }
1835
1836 ZVAL_COPY(&stmt->fetch.into, &args[0]);
1837 break;
1838 default:
1839 zend_argument_value_error(mode_arg_num, "must be one of the PDO::FETCH_* constants");
1840 return false;
1841 }
1842
1843 stmt->default_fetch_type = mode;
1844
1845 return true;
1846 }
1847
PHP_METHOD(PDOStatement,setFetchMode)1848 PHP_METHOD(PDOStatement, setFetchMode)
1849 {
1850 zend_long fetch_mode;
1851 zval *args = NULL;
1852 uint32_t num_args = 0;
1853
1854 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l*", &fetch_mode, &args, &num_args) == FAILURE) {
1855 RETURN_THROWS();
1856 }
1857
1858 PHP_STMT_GET_OBJ;
1859
1860 do_fetch_opt_finish(stmt, 1);
1861
1862 if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
1863 RETURN_THROWS();
1864 }
1865
1866 // TODO Void return?
1867 RETURN_TRUE;
1868 }
1869 /* }}} */
1870
1871 /* {{{ Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
1872
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)1873 static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
1874 {
1875 pdo_stmt_reset_columns(stmt);
1876
1877 if (!stmt->methods->next_rowset(stmt)) {
1878 /* Set the executed flag to 0 to reallocate columns on next execute */
1879 stmt->executed = 0;
1880 return 0;
1881 }
1882
1883 pdo_stmt_describe_columns(stmt);
1884
1885 return 1;
1886 }
1887
PHP_METHOD(PDOStatement,nextRowset)1888 PHP_METHOD(PDOStatement, nextRowset)
1889 {
1890 ZEND_PARSE_PARAMETERS_NONE();
1891
1892 PHP_STMT_GET_OBJ;
1893 if (!stmt->methods->next_rowset) {
1894 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
1895 RETURN_FALSE;
1896 }
1897
1898 PDO_STMT_CLEAR_ERR();
1899
1900 if (!pdo_stmt_do_next_rowset(stmt)) {
1901 PDO_HANDLE_STMT_ERR();
1902 RETURN_FALSE;
1903 }
1904
1905 RETURN_TRUE;
1906 }
1907 /* }}} */
1908
1909 /* {{{ Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)1910 PHP_METHOD(PDOStatement, closeCursor)
1911 {
1912 ZEND_PARSE_PARAMETERS_NONE();
1913
1914 PHP_STMT_GET_OBJ;
1915 if (!stmt->methods->cursor_closer) {
1916 /* emulate it by fetching and discarding rows */
1917 do {
1918 while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
1919 ;
1920 if (!stmt->methods->next_rowset) {
1921 break;
1922 }
1923
1924 if (!pdo_stmt_do_next_rowset(stmt)) {
1925 break;
1926 }
1927
1928 } while (1);
1929 stmt->executed = 0;
1930 RETURN_TRUE;
1931 }
1932
1933 PDO_STMT_CLEAR_ERR();
1934
1935 if (!stmt->methods->cursor_closer(stmt)) {
1936 PDO_HANDLE_STMT_ERR();
1937 RETURN_FALSE;
1938 }
1939 stmt->executed = 0;
1940 RETURN_TRUE;
1941 }
1942 /* }}} */
1943
1944 /* {{{ A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)1945 PHP_METHOD(PDOStatement, debugDumpParams)
1946 {
1947 ZEND_PARSE_PARAMETERS_NONE();
1948
1949 php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
1950 struct pdo_bound_param_data *param;
1951
1952 ZEND_PARSE_PARAMETERS_NONE();
1953
1954 PHP_STMT_GET_OBJ;
1955
1956 if (out == NULL) {
1957 RETURN_FALSE;
1958 }
1959
1960 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1961 php_stream_printf(out, "SQL: [%zd] ", ZSTR_LEN(stmt->query_string));
1962 php_stream_write(out, ZSTR_VAL(stmt->query_string), ZSTR_LEN(stmt->query_string));
1963 php_stream_write(out, "\n", 1);
1964
1965 /* show parsed SQL if emulated prepares enabled */
1966 /* pointers will be equal if PDO::query() was invoked */
1967 if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
1968 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1969 php_stream_printf(out, "Sent SQL: [%zd] ", ZSTR_LEN(stmt->active_query_string));
1970 php_stream_write(out, ZSTR_VAL(stmt->active_query_string), ZSTR_LEN(stmt->active_query_string));
1971 php_stream_write(out, "\n", 1);
1972 }
1973
1974 php_stream_printf(out, "Params: %d\n",
1975 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
1976
1977 if (stmt->bound_params) {
1978 zend_ulong num;
1979 zend_string *key = NULL;
1980 ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
1981 if (key) {
1982 php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
1983 ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
1984 } else {
1985 php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
1986 }
1987
1988 php_stream_printf(out,
1989 "paramno=" ZEND_LONG_FMT "\n"
1990 "name=[%zd] \"%.*s\"\n"
1991 "is_param=%d\n"
1992 "param_type=%d\n",
1993 param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
1994 param->name ? ZSTR_VAL(param->name) : "",
1995 param->is_param,
1996 param->param_type);
1997
1998 } ZEND_HASH_FOREACH_END();
1999 }
2000
2001 php_stream_close(out);
2002 }
2003 /* }}} */
2004
PHP_METHOD(PDOStatement,getIterator)2005 PHP_METHOD(PDOStatement, getIterator)
2006 {
2007 if (zend_parse_parameters_none() == FAILURE) {
2008 return;
2009 }
2010
2011 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
2012 }
2013
2014 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2015 static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2016 {
2017 if (zend_string_equals_literal(name, "queryString")) {
2018 zval *query_string = OBJ_PROP_NUM(object, 0);
2019 if (!Z_ISUNDEF_P(query_string)) {
2020 zend_throw_error(NULL, "Property queryString is read only");
2021 return value;
2022 }
2023 }
2024 return zend_std_write_property(object, name, value, cache_slot);
2025 }
2026
dbstmt_prop_delete(zend_object * object,zend_string * name,void ** cache_slot)2027 static void dbstmt_prop_delete(zend_object *object, zend_string *name, void **cache_slot)
2028 {
2029 if (zend_string_equals_literal(name, "queryString")) {
2030 zend_throw_error(NULL, "Property queryString is read only");
2031 } else {
2032 zend_std_unset_property(object, name, cache_slot);
2033 }
2034 }
2035
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2036 static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2037 {
2038 zend_function *fbc = NULL;
2039 zend_string *lc_method_name;
2040 zend_object *object = *object_pp;
2041
2042 lc_method_name = zend_string_tolower(method_name);
2043
2044 if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2045 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2046 /* instance not created by PDO object */
2047 if (!stmt->dbh) {
2048 goto out;
2049 }
2050 /* not a pre-defined method, nor a user-defined method; check
2051 * the driver specific methods */
2052 if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2053 if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2054 PDO_DBH_DRIVER_METHOD_KIND_STMT)
2055 || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2056 goto out;
2057 }
2058 }
2059
2060 if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2061 goto out;
2062 }
2063 /* got it */
2064 }
2065
2066 out:
2067 zend_string_release_ex(lc_method_name, 0);
2068 if (!fbc) {
2069 fbc = zend_std_get_method(object_pp, method_name, key);
2070 }
2071 return fbc;
2072 }
2073
2074 zend_object_handlers pdo_dbstmt_object_handlers;
2075 zend_object_handlers pdo_row_object_handlers;
2076
php_pdo_free_statement(pdo_stmt_t * stmt)2077 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2078 {
2079 if (stmt->bound_params) {
2080 zend_hash_destroy(stmt->bound_params);
2081 FREE_HASHTABLE(stmt->bound_params);
2082 stmt->bound_params = NULL;
2083 }
2084 if (stmt->bound_param_map) {
2085 zend_hash_destroy(stmt->bound_param_map);
2086 FREE_HASHTABLE(stmt->bound_param_map);
2087 stmt->bound_param_map = NULL;
2088 }
2089 if (stmt->bound_columns) {
2090 zend_hash_destroy(stmt->bound_columns);
2091 FREE_HASHTABLE(stmt->bound_columns);
2092 stmt->bound_columns = NULL;
2093 }
2094
2095 if (stmt->methods && stmt->methods->dtor) {
2096 stmt->methods->dtor(stmt);
2097 }
2098 if (stmt->active_query_string) {
2099 zend_string_release(stmt->active_query_string);
2100 }
2101 if (stmt->query_string) {
2102 zend_string_release(stmt->query_string);
2103 }
2104
2105 pdo_stmt_reset_columns(stmt);
2106
2107 if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2108 zval_ptr_dtor(&stmt->fetch.into);
2109 ZVAL_UNDEF(&stmt->fetch.into);
2110 }
2111
2112 do_fetch_opt_finish(stmt, 1);
2113
2114 if (!Z_ISUNDEF(stmt->database_object_handle)) {
2115 zval_ptr_dtor(&stmt->database_object_handle);
2116 }
2117 zend_object_std_dtor(&stmt->std);
2118 }
2119
pdo_dbstmt_free_storage(zend_object * std)2120 void pdo_dbstmt_free_storage(zend_object *std)
2121 {
2122 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2123 php_pdo_free_statement(stmt);
2124 }
2125
pdo_dbstmt_new(zend_class_entry * ce)2126 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2127 {
2128 pdo_stmt_t *stmt;
2129
2130 stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2131 zend_object_std_init(&stmt->std, ce);
2132 object_properties_init(&stmt->std, ce);
2133
2134 return &stmt->std;
2135 }
2136 /* }}} */
2137
2138 /* {{{ statement iterator */
2139
2140 struct php_pdo_iterator {
2141 zend_object_iterator iter;
2142 zend_ulong key;
2143 zval fetch_ahead;
2144 };
2145
pdo_stmt_iter_dtor(zend_object_iterator * iter)2146 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2147 {
2148 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2149
2150 zval_ptr_dtor(&I->iter.data);
2151
2152 if (!Z_ISUNDEF(I->fetch_ahead)) {
2153 zval_ptr_dtor(&I->fetch_ahead);
2154 }
2155 }
2156
pdo_stmt_iter_valid(zend_object_iterator * iter)2157 static zend_result pdo_stmt_iter_valid(zend_object_iterator *iter)
2158 {
2159 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2160
2161 return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2162 }
2163
pdo_stmt_iter_get_data(zend_object_iterator * iter)2164 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2165 {
2166 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2167
2168 /* sanity */
2169 if (Z_ISUNDEF(I->fetch_ahead)) {
2170 return NULL;
2171 }
2172
2173 return &I->fetch_ahead;
2174 }
2175
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2176 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2177 {
2178 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2179
2180 if (I->key == (zend_ulong)-1) {
2181 ZVAL_NULL(key);
2182 } else {
2183 ZVAL_LONG(key, I->key);
2184 }
2185 }
2186
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2187 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2188 {
2189 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2190 pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2191
2192 if (!Z_ISUNDEF(I->fetch_ahead)) {
2193 zval_ptr_dtor(&I->fetch_ahead);
2194 }
2195
2196 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2197 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2198
2199 PDO_HANDLE_STMT_ERR();
2200 I->key = (zend_ulong)-1;
2201 ZVAL_UNDEF(&I->fetch_ahead);
2202
2203 return;
2204 }
2205
2206 I->key++;
2207 }
2208
2209 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2210 pdo_stmt_iter_dtor,
2211 pdo_stmt_iter_valid,
2212 pdo_stmt_iter_get_data,
2213 pdo_stmt_iter_get_key,
2214 pdo_stmt_iter_move_forwards,
2215 NULL,
2216 NULL,
2217 NULL, /* get_gc */
2218 };
2219
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2220 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2221 {
2222 if (by_ref) {
2223 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2224 return NULL;
2225 }
2226
2227 pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2228 if (!stmt->dbh) {
2229 zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(ce->name));
2230 return NULL;
2231 }
2232
2233 struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2234 zend_iterator_init(&I->iter);
2235 I->iter.funcs = &pdo_stmt_iter_funcs;
2236 Z_ADDREF_P(object);
2237 ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2238
2239 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2240 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2241 PDO_HANDLE_STMT_ERR();
2242 I->key = (zend_ulong)-1;
2243 ZVAL_UNDEF(&I->fetch_ahead);
2244 }
2245
2246 return &I->iter;
2247 }
2248
2249 /* }}} */
2250
2251 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
row_read_column_name(pdo_stmt_t * stmt,zend_string * name,zval * rv)2252 static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv)
2253 {
2254 /* TODO: replace this with a hash of available column names to column numbers */
2255 for (int colno = 0; colno < stmt->column_count; colno++) {
2256 if (zend_string_equals(stmt->columns[colno].name, name)) {
2257 fetch_value(stmt, rv, colno, NULL);
2258 return rv;
2259 }
2260 }
2261 return NULL;
2262 }
2263
row_read_column_number(pdo_stmt_t * stmt,zend_long column,zval * rv)2264 static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv)
2265 {
2266 if (column >= 0 && column < stmt->column_count) {
2267 fetch_value(stmt, rv, column, NULL);
2268 return rv;
2269 }
2270 return NULL;
2271 }
2272
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2273 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2274 {
2275 pdo_row_t *row = (pdo_row_t *)object;
2276 pdo_stmt_t *stmt = row->stmt;
2277 zend_long lval;
2278 zval *retval;
2279 ZEND_ASSERT(stmt);
2280
2281 ZVAL_NULL(rv);
2282 if (zend_string_equals_literal(name, "queryString")) {
2283 return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2284 } else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2285 retval = row_read_column_number(stmt, lval, rv);
2286 } else {
2287 retval = row_read_column_name(stmt, name, rv);
2288 }
2289 if (UNEXPECTED(!retval)) {
2290 // TODO throw an error on master
2291 //if (type != BP_VAR_IS) {
2292 // if (is_numeric) {
2293 // zend_value_error("Invalid column index");
2294 // } else {
2295 // zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name));
2296 // }
2297 //}
2298 //return &EG(uninitialized_zval);
2299 ZVAL_NULL(rv);
2300 return rv;
2301 }
2302 return retval;
2303 }
2304
row_dim_read(zend_object * object,zval * offset,int type,zval * rv)2305 static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv)
2306 {
2307 if (UNEXPECTED(!offset)) {
2308 zend_throw_error(NULL, "Cannot append to PDORow offset");
2309 return NULL;
2310 }
2311 if (Z_TYPE_P(offset) == IS_LONG) {
2312 pdo_row_t *row = (pdo_row_t *)object;
2313 pdo_stmt_t *stmt = row->stmt;
2314 ZEND_ASSERT(stmt);
2315
2316 ZVAL_NULL(rv);
2317 if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) {
2318 fetch_value(stmt, rv, Z_LVAL_P(offset), NULL);
2319 }
2320 return rv;
2321 } else {
2322 zend_string *member = zval_try_get_string(offset);
2323 if (!member) {
2324 return NULL;
2325 }
2326 zval *result = row_prop_read(object, member, type, NULL, rv);
2327 zend_string_release_ex(member, false);
2328 return result;
2329 }
2330 }
2331
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2332 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2333 {
2334 zend_throw_error(NULL, "Cannot write to PDORow property");
2335 return value;
2336 }
2337
row_dim_write(zend_object * object,zval * member,zval * value)2338 static void row_dim_write(zend_object *object, zval *member, zval *value)
2339 {
2340 if (!member) {
2341 zend_throw_error(NULL, "Cannot append to PDORow offset");
2342 } else {
2343 zend_throw_error(NULL, "Cannot write to PDORow offset");
2344 }
2345 }
2346
2347 // todo: make row_prop_exists return bool as well
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2348 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2349 {
2350 pdo_row_t *row = (pdo_row_t *)object;
2351 pdo_stmt_t *stmt = row->stmt;
2352 zend_long lval;
2353 zval tmp_val;
2354 zval *retval = NULL;
2355 ZEND_ASSERT(stmt);
2356
2357 if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2358 retval = row_read_column_number(stmt, lval, &tmp_val);
2359 } else {
2360 retval = row_read_column_name(stmt, name, &tmp_val);
2361 }
2362
2363 if (!retval) {
2364 return false;
2365 }
2366 ZEND_ASSERT(retval == &tmp_val);
2367 bool res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2368 zval_ptr_dtor_nogc(retval);
2369
2370 return res;
2371 }
2372
2373
2374 // todo: make row_dim_exists return bool as well
row_dim_exists(zend_object * object,zval * offset,int check_empty)2375 static int row_dim_exists(zend_object *object, zval *offset, int check_empty)
2376 {
2377 if (Z_TYPE_P(offset) == IS_LONG) {
2378 pdo_row_t *row = (pdo_row_t *)object;
2379 pdo_stmt_t *stmt = row->stmt;
2380 ZEND_ASSERT(stmt);
2381 zend_long column = Z_LVAL_P(offset);
2382
2383 if (!check_empty) {
2384 return column >= 0 && column < stmt->column_count;
2385 }
2386
2387 zval tmp_val;
2388 zval *retval = row_read_column_number(stmt, column, &tmp_val);
2389 if (!retval) {
2390 return false;
2391 }
2392 ZEND_ASSERT(retval == &tmp_val);
2393 bool res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2394 zval_ptr_dtor_nogc(retval);
2395 return res;
2396 } else {
2397 zend_string *member = zval_try_get_string(offset);
2398 if (!member) {
2399 return 0;
2400 }
2401 int result = row_prop_exists(object, member, check_empty, NULL);
2402 zend_string_release_ex(member, false);
2403 return result;
2404 }
2405 }
2406
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2407 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2408 {
2409 zend_throw_error(NULL, "Cannot unset PDORow property");
2410 }
2411
row_dim_delete(zend_object * object,zval * offset)2412 static void row_dim_delete(zend_object *object, zval *offset)
2413 {
2414 zend_throw_error(NULL, "Cannot unset PDORow offset");
2415 }
2416
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2417 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2418 {
2419 pdo_row_t *row = (pdo_row_t *)object;
2420 pdo_stmt_t *stmt = row->stmt;
2421 HashTable *props;
2422 int i;
2423 ZEND_ASSERT(stmt);
2424
2425 if (purpose != ZEND_PROP_PURPOSE_DEBUG) {
2426 return zend_std_get_properties_for(object, purpose);
2427 }
2428
2429 props = zend_array_dup(zend_std_get_properties_ex(&stmt->std));
2430 for (i = 0; i < stmt->column_count; i++) {
2431 if (zend_string_equals_literal(stmt->columns[i].name, "queryString")) {
2432 continue;
2433 }
2434
2435 zval val;
2436 fetch_value(stmt, &val, i, NULL);
2437
2438 zend_hash_update(props, stmt->columns[i].name, &val);
2439 }
2440 return props;
2441 }
2442
row_get_ctor(zend_object * object)2443 static zend_function *row_get_ctor(zend_object *object)
2444 {
2445 zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2446 return NULL;
2447 }
2448
pdo_row_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)2449 static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
2450 {
2451 ZEND_IGNORE_VALUE(object);
2452 ZEND_IGNORE_VALUE(name);
2453 ZEND_IGNORE_VALUE(type);
2454 ZEND_IGNORE_VALUE(cache_slot);
2455
2456 return NULL;
2457 }
2458
pdo_row_free_storage(zend_object * std)2459 void pdo_row_free_storage(zend_object *std)
2460 {
2461 pdo_row_t *row = (pdo_row_t *)std;
2462 if (row->stmt) {
2463 ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2464 OBJ_RELEASE(&row->stmt->std);
2465 }
2466 }
2467
pdo_row_new(zend_class_entry * ce)2468 zend_object *pdo_row_new(zend_class_entry *ce)
2469 {
2470 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2471 zend_object_std_init(&row->std, ce);
2472
2473 return &row->std;
2474 }
2475
pdo_stmt_init(void)2476 void pdo_stmt_init(void)
2477 {
2478 pdo_dbstmt_ce = register_class_PDOStatement(zend_ce_aggregate);
2479 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2480 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2481 pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers;
2482
2483 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2484 pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2485 pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2486 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2487 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2488 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2489 pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable;
2490 pdo_dbstmt_object_handlers.clone_obj = NULL;
2491
2492 pdo_row_ce = register_class_PDORow();
2493 pdo_row_ce->create_object = pdo_row_new;
2494 pdo_row_ce->default_object_handlers = &pdo_row_object_handlers;
2495
2496 memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2497 pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2498 pdo_row_object_handlers.clone_obj = NULL;
2499 pdo_row_object_handlers.get_property_ptr_ptr = pdo_row_get_property_ptr_ptr;
2500 pdo_row_object_handlers.read_property = row_prop_read;
2501 pdo_row_object_handlers.write_property = row_prop_write;
2502 pdo_row_object_handlers.has_property = row_prop_exists;
2503 pdo_row_object_handlers.unset_property = row_prop_delete;
2504 pdo_row_object_handlers.read_dimension = row_dim_read;
2505 pdo_row_object_handlers.write_dimension = row_dim_write;
2506 pdo_row_object_handlers.has_dimension = row_dim_exists;
2507 pdo_row_object_handlers.unset_dimension = row_dim_delete;
2508 pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2509 pdo_row_object_handlers.get_constructor = row_get_ctor;
2510 pdo_row_object_handlers.compare = zend_objects_not_comparable;
2511 }
2512