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
dbstmt_get_gc(zend_object * object,zval ** gc_data,int * gc_count)2074 static HashTable *dbstmt_get_gc(zend_object *object, zval **gc_data, int *gc_count)
2075 {
2076 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2077 *gc_data = &stmt->fetch.into;
2078 *gc_count = 1;
2079
2080 /**
2081 * If there are no dynamic properties and the default property is 1 (that is, there is only one property
2082 * of string that does not participate in GC), there is no need to call zend_std_get_properties().
2083 */
2084 if (object->properties == NULL && object->ce->default_properties_count <= 1) {
2085 return NULL;
2086 } else {
2087 return zend_std_get_properties(object);
2088 }
2089 }
2090
2091 zend_object_handlers pdo_dbstmt_object_handlers;
2092 zend_object_handlers pdo_row_object_handlers;
2093
php_pdo_free_statement(pdo_stmt_t * stmt)2094 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2095 {
2096 if (stmt->bound_params) {
2097 zend_hash_destroy(stmt->bound_params);
2098 FREE_HASHTABLE(stmt->bound_params);
2099 stmt->bound_params = NULL;
2100 }
2101 if (stmt->bound_param_map) {
2102 zend_hash_destroy(stmt->bound_param_map);
2103 FREE_HASHTABLE(stmt->bound_param_map);
2104 stmt->bound_param_map = NULL;
2105 }
2106 if (stmt->bound_columns) {
2107 zend_hash_destroy(stmt->bound_columns);
2108 FREE_HASHTABLE(stmt->bound_columns);
2109 stmt->bound_columns = NULL;
2110 }
2111
2112 if (stmt->methods && stmt->methods->dtor) {
2113 stmt->methods->dtor(stmt);
2114 }
2115 if (stmt->active_query_string) {
2116 zend_string_release(stmt->active_query_string);
2117 }
2118 if (stmt->query_string) {
2119 zend_string_release(stmt->query_string);
2120 }
2121
2122 pdo_stmt_reset_columns(stmt);
2123
2124 if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2125 zval_ptr_dtor(&stmt->fetch.into);
2126 ZVAL_UNDEF(&stmt->fetch.into);
2127 }
2128
2129 do_fetch_opt_finish(stmt, 1);
2130
2131 if (!Z_ISUNDEF(stmt->database_object_handle)) {
2132 zval_ptr_dtor(&stmt->database_object_handle);
2133 }
2134 zend_object_std_dtor(&stmt->std);
2135 }
2136
pdo_dbstmt_free_storage(zend_object * std)2137 void pdo_dbstmt_free_storage(zend_object *std)
2138 {
2139 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2140 php_pdo_free_statement(stmt);
2141 }
2142
pdo_dbstmt_new(zend_class_entry * ce)2143 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2144 {
2145 pdo_stmt_t *stmt;
2146
2147 stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2148 zend_object_std_init(&stmt->std, ce);
2149 object_properties_init(&stmt->std, ce);
2150
2151 return &stmt->std;
2152 }
2153 /* }}} */
2154
2155 /* {{{ statement iterator */
2156
2157 struct php_pdo_iterator {
2158 zend_object_iterator iter;
2159 zend_ulong key;
2160 zval fetch_ahead;
2161 };
2162
pdo_stmt_iter_dtor(zend_object_iterator * iter)2163 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2164 {
2165 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2166
2167 zval_ptr_dtor(&I->iter.data);
2168
2169 if (!Z_ISUNDEF(I->fetch_ahead)) {
2170 zval_ptr_dtor(&I->fetch_ahead);
2171 }
2172 }
2173
pdo_stmt_iter_valid(zend_object_iterator * iter)2174 static zend_result pdo_stmt_iter_valid(zend_object_iterator *iter)
2175 {
2176 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2177
2178 return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2179 }
2180
pdo_stmt_iter_get_data(zend_object_iterator * iter)2181 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2182 {
2183 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2184
2185 /* sanity */
2186 if (Z_ISUNDEF(I->fetch_ahead)) {
2187 return NULL;
2188 }
2189
2190 return &I->fetch_ahead;
2191 }
2192
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2193 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2194 {
2195 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2196
2197 if (I->key == (zend_ulong)-1) {
2198 ZVAL_NULL(key);
2199 } else {
2200 ZVAL_LONG(key, I->key);
2201 }
2202 }
2203
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2204 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2205 {
2206 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2207 pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2208
2209 if (!Z_ISUNDEF(I->fetch_ahead)) {
2210 zval_ptr_dtor(&I->fetch_ahead);
2211 }
2212
2213 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2214 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2215
2216 PDO_HANDLE_STMT_ERR();
2217 I->key = (zend_ulong)-1;
2218 ZVAL_UNDEF(&I->fetch_ahead);
2219
2220 return;
2221 }
2222
2223 I->key++;
2224 }
2225
2226 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2227 pdo_stmt_iter_dtor,
2228 pdo_stmt_iter_valid,
2229 pdo_stmt_iter_get_data,
2230 pdo_stmt_iter_get_key,
2231 pdo_stmt_iter_move_forwards,
2232 NULL,
2233 NULL,
2234 NULL, /* get_gc */
2235 };
2236
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2237 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2238 {
2239 if (by_ref) {
2240 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2241 return NULL;
2242 }
2243
2244 pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2245 if (!stmt->dbh) {
2246 zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(ce->name));
2247 return NULL;
2248 }
2249
2250 struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2251 zend_iterator_init(&I->iter);
2252 I->iter.funcs = &pdo_stmt_iter_funcs;
2253 Z_ADDREF_P(object);
2254 ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2255
2256 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2257 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2258 PDO_HANDLE_STMT_ERR();
2259 I->key = (zend_ulong)-1;
2260 ZVAL_UNDEF(&I->fetch_ahead);
2261 }
2262
2263 return &I->iter;
2264 }
2265
2266 /* }}} */
2267
2268 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
row_read_column_name(pdo_stmt_t * stmt,zend_string * name,zval * rv)2269 static zval *row_read_column_name(pdo_stmt_t *stmt, zend_string *name, zval *rv)
2270 {
2271 /* TODO: replace this with a hash of available column names to column numbers */
2272 for (int colno = 0; colno < stmt->column_count; colno++) {
2273 if (zend_string_equals(stmt->columns[colno].name, name)) {
2274 fetch_value(stmt, rv, colno, NULL);
2275 return rv;
2276 }
2277 }
2278 return NULL;
2279 }
2280
row_read_column_number(pdo_stmt_t * stmt,zend_long column,zval * rv)2281 static zval *row_read_column_number(pdo_stmt_t *stmt, zend_long column, zval *rv)
2282 {
2283 if (column >= 0 && column < stmt->column_count) {
2284 fetch_value(stmt, rv, column, NULL);
2285 return rv;
2286 }
2287 return NULL;
2288 }
2289
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2290 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2291 {
2292 pdo_row_t *row = (pdo_row_t *)object;
2293 pdo_stmt_t *stmt = row->stmt;
2294 zend_long lval;
2295 zval *retval;
2296 ZEND_ASSERT(stmt);
2297
2298 ZVAL_NULL(rv);
2299 if (zend_string_equals_literal(name, "queryString")) {
2300 return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2301 } else if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2302 retval = row_read_column_number(stmt, lval, rv);
2303 } else {
2304 retval = row_read_column_name(stmt, name, rv);
2305 }
2306 if (UNEXPECTED(!retval)) {
2307 // TODO throw an error on master
2308 //if (type != BP_VAR_IS) {
2309 // if (is_numeric) {
2310 // zend_value_error("Invalid column index");
2311 // } else {
2312 // zend_throw_error(NULL, "No column named \"%s\" exists", ZSTR_VAL(name));
2313 // }
2314 //}
2315 //return &EG(uninitialized_zval);
2316 ZVAL_NULL(rv);
2317 return rv;
2318 }
2319 return retval;
2320 }
2321
row_dim_read(zend_object * object,zval * offset,int type,zval * rv)2322 static zval *row_dim_read(zend_object *object, zval *offset, int type, zval *rv)
2323 {
2324 if (UNEXPECTED(!offset)) {
2325 zend_throw_error(NULL, "Cannot append to PDORow offset");
2326 return NULL;
2327 }
2328 if (Z_TYPE_P(offset) == IS_LONG) {
2329 pdo_row_t *row = (pdo_row_t *)object;
2330 pdo_stmt_t *stmt = row->stmt;
2331 ZEND_ASSERT(stmt);
2332
2333 ZVAL_NULL(rv);
2334 if (Z_LVAL_P(offset) >= 0 && Z_LVAL_P(offset) < stmt->column_count) {
2335 fetch_value(stmt, rv, Z_LVAL_P(offset), NULL);
2336 }
2337 return rv;
2338 } else {
2339 zend_string *member = zval_try_get_string(offset);
2340 if (!member) {
2341 return NULL;
2342 }
2343 zval *result = row_prop_read(object, member, type, NULL, rv);
2344 zend_string_release_ex(member, false);
2345 return result;
2346 }
2347 }
2348
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2349 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2350 {
2351 zend_throw_error(NULL, "Cannot write to PDORow property");
2352 return value;
2353 }
2354
row_dim_write(zend_object * object,zval * member,zval * value)2355 static void row_dim_write(zend_object *object, zval *member, zval *value)
2356 {
2357 if (!member) {
2358 zend_throw_error(NULL, "Cannot append to PDORow offset");
2359 } else {
2360 zend_throw_error(NULL, "Cannot write to PDORow offset");
2361 }
2362 }
2363
2364 // todo: make row_prop_exists return bool as well
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2365 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2366 {
2367 pdo_row_t *row = (pdo_row_t *)object;
2368 pdo_stmt_t *stmt = row->stmt;
2369 zend_long lval;
2370 zval tmp_val;
2371 zval *retval = NULL;
2372 ZEND_ASSERT(stmt);
2373
2374 if (is_numeric_str_function(name, &lval, /* dval */ NULL) == IS_LONG) {
2375 retval = row_read_column_number(stmt, lval, &tmp_val);
2376 } else {
2377 retval = row_read_column_name(stmt, name, &tmp_val);
2378 }
2379
2380 if (!retval) {
2381 return false;
2382 }
2383 ZEND_ASSERT(retval == &tmp_val);
2384 bool res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2385 zval_ptr_dtor_nogc(retval);
2386
2387 return res;
2388 }
2389
2390
2391 // todo: make row_dim_exists return bool as well
row_dim_exists(zend_object * object,zval * offset,int check_empty)2392 static int row_dim_exists(zend_object *object, zval *offset, int check_empty)
2393 {
2394 if (Z_TYPE_P(offset) == IS_LONG) {
2395 pdo_row_t *row = (pdo_row_t *)object;
2396 pdo_stmt_t *stmt = row->stmt;
2397 ZEND_ASSERT(stmt);
2398 zend_long column = Z_LVAL_P(offset);
2399
2400 if (!check_empty) {
2401 return column >= 0 && column < stmt->column_count;
2402 }
2403
2404 zval tmp_val;
2405 zval *retval = row_read_column_number(stmt, column, &tmp_val);
2406 if (!retval) {
2407 return false;
2408 }
2409 ZEND_ASSERT(retval == &tmp_val);
2410 bool res = check_empty ? i_zend_is_true(retval) : Z_TYPE(tmp_val) != IS_NULL;
2411 zval_ptr_dtor_nogc(retval);
2412 return res;
2413 } else {
2414 zend_string *member = zval_try_get_string(offset);
2415 if (!member) {
2416 return 0;
2417 }
2418 int result = row_prop_exists(object, member, check_empty, NULL);
2419 zend_string_release_ex(member, false);
2420 return result;
2421 }
2422 }
2423
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2424 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2425 {
2426 zend_throw_error(NULL, "Cannot unset PDORow property");
2427 }
2428
row_dim_delete(zend_object * object,zval * offset)2429 static void row_dim_delete(zend_object *object, zval *offset)
2430 {
2431 zend_throw_error(NULL, "Cannot unset PDORow offset");
2432 }
2433
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2434 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2435 {
2436 pdo_row_t *row = (pdo_row_t *)object;
2437 pdo_stmt_t *stmt = row->stmt;
2438 HashTable *props;
2439 int i;
2440 ZEND_ASSERT(stmt);
2441
2442 if (purpose != ZEND_PROP_PURPOSE_DEBUG) {
2443 return zend_std_get_properties_for(object, purpose);
2444 }
2445
2446 props = zend_array_dup(zend_std_get_properties_ex(&stmt->std));
2447 for (i = 0; i < stmt->column_count; i++) {
2448 if (zend_string_equals_literal(stmt->columns[i].name, "queryString")) {
2449 continue;
2450 }
2451
2452 zval val;
2453 fetch_value(stmt, &val, i, NULL);
2454
2455 zend_hash_update(props, stmt->columns[i].name, &val);
2456 }
2457 return props;
2458 }
2459
row_get_ctor(zend_object * object)2460 static zend_function *row_get_ctor(zend_object *object)
2461 {
2462 zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2463 return NULL;
2464 }
2465
pdo_row_get_property_ptr_ptr(zend_object * object,zend_string * name,int type,void ** cache_slot)2466 static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name, int type, void **cache_slot)
2467 {
2468 ZEND_IGNORE_VALUE(object);
2469 ZEND_IGNORE_VALUE(name);
2470 ZEND_IGNORE_VALUE(type);
2471 ZEND_IGNORE_VALUE(cache_slot);
2472
2473 return NULL;
2474 }
2475
pdo_row_free_storage(zend_object * std)2476 void pdo_row_free_storage(zend_object *std)
2477 {
2478 pdo_row_t *row = (pdo_row_t *)std;
2479 if (row->stmt) {
2480 ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2481 OBJ_RELEASE(&row->stmt->std);
2482 }
2483 }
2484
pdo_row_new(zend_class_entry * ce)2485 zend_object *pdo_row_new(zend_class_entry *ce)
2486 {
2487 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2488 zend_object_std_init(&row->std, ce);
2489
2490 return &row->std;
2491 }
2492
pdo_stmt_init(void)2493 void pdo_stmt_init(void)
2494 {
2495 pdo_dbstmt_ce = register_class_PDOStatement(zend_ce_aggregate);
2496 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2497 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2498 pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers;
2499
2500 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2501 pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2502 pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2503 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2504 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2505 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2506 pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable;
2507 pdo_dbstmt_object_handlers.clone_obj = NULL;
2508 pdo_dbstmt_object_handlers.get_gc = dbstmt_get_gc;
2509
2510 pdo_row_ce = register_class_PDORow();
2511 pdo_row_ce->create_object = pdo_row_new;
2512 pdo_row_ce->default_object_handlers = &pdo_row_object_handlers;
2513
2514 memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2515 pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2516 pdo_row_object_handlers.clone_obj = NULL;
2517 pdo_row_object_handlers.get_property_ptr_ptr = pdo_row_get_property_ptr_ptr;
2518 pdo_row_object_handlers.read_property = row_prop_read;
2519 pdo_row_object_handlers.write_property = row_prop_write;
2520 pdo_row_object_handlers.has_property = row_prop_exists;
2521 pdo_row_object_handlers.unset_property = row_prop_delete;
2522 pdo_row_object_handlers.read_dimension = row_dim_read;
2523 pdo_row_object_handlers.write_dimension = row_dim_write;
2524 pdo_row_object_handlers.has_dimension = row_dim_exists;
2525 pdo_row_object_handlers.unset_dimension = row_dim_delete;
2526 pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2527 pdo_row_object_handlers.get_constructor = row_get_ctor;
2528 pdo_row_object_handlers.compare = zend_objects_not_comparable;
2529 }
2530