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