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 (stmt->dbh->methods->fetch_err) {
1621 stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1622 }
1623
1624 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1625
1626 if (error_expected_count > error_count) {
1627 int current_index;
1628
1629 error_count_diff = error_expected_count - error_count;
1630 for (current_index = 0; current_index < error_count_diff; current_index++) {
1631 add_next_index_null(return_value);
1632 }
1633 }
1634 }
1635 /* }}} */
1636
1637 /* {{{ Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1638 PHP_METHOD(PDOStatement, setAttribute)
1639 {
1640 zend_long attr;
1641 zval *value = NULL;
1642
1643 ZEND_PARSE_PARAMETERS_START(2, 2)
1644 Z_PARAM_LONG(attr)
1645 Z_PARAM_ZVAL_OR_NULL(value)
1646 ZEND_PARSE_PARAMETERS_END();
1647
1648 PHP_STMT_GET_OBJ;
1649
1650 /* Driver hasn't registered a function for setting attributes */
1651 if (!stmt->methods->set_attribute) {
1652 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1653 RETURN_FALSE;
1654 }
1655
1656 PDO_STMT_CLEAR_ERR();
1657 if (stmt->methods->set_attribute(stmt, attr, value)) {
1658 RETURN_TRUE;
1659 }
1660
1661 /* Error while setting attribute */
1662 PDO_HANDLE_STMT_ERR();
1663 RETURN_FALSE;
1664 }
1665 /* }}} */
1666
1667 /* {{{ Get an attribute */
1668
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1669 static bool generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1670 {
1671 switch (attr) {
1672 case PDO_ATTR_EMULATE_PREPARES:
1673 RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1674 return 1;
1675 }
1676 return 0;
1677 }
1678
PHP_METHOD(PDOStatement,getAttribute)1679 PHP_METHOD(PDOStatement, getAttribute)
1680 {
1681 zend_long attr;
1682
1683 ZEND_PARSE_PARAMETERS_START(1, 1)
1684 Z_PARAM_LONG(attr)
1685 ZEND_PARSE_PARAMETERS_END();
1686
1687 PHP_STMT_GET_OBJ;
1688 if (!stmt->methods->get_attribute) {
1689 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1690 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1691 "This driver doesn't support getting attributes");
1692 RETURN_FALSE;
1693 }
1694 return;
1695 }
1696
1697 PDO_STMT_CLEAR_ERR();
1698 switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1699 case -1:
1700 PDO_HANDLE_STMT_ERR();
1701 RETURN_FALSE;
1702
1703 case 0:
1704 if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1705 /* XXX: should do something better here */
1706 pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1707 "driver doesn't support getting that attribute");
1708 RETURN_FALSE;
1709 }
1710 return;
1711
1712 default:
1713 return;
1714 }
1715 }
1716 /* }}} */
1717
1718 /* {{{ Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1719 PHP_METHOD(PDOStatement, columnCount)
1720 {
1721 ZEND_PARSE_PARAMETERS_NONE();
1722
1723 PHP_STMT_GET_OBJ;
1724 RETURN_LONG(stmt->column_count);
1725 }
1726 /* }}} */
1727
1728 /* {{{ Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1729 PHP_METHOD(PDOStatement, getColumnMeta)
1730 {
1731 zend_long colno;
1732 struct pdo_column_data *col;
1733
1734 ZEND_PARSE_PARAMETERS_START(1, 1)
1735 Z_PARAM_LONG(colno)
1736 ZEND_PARSE_PARAMETERS_END();
1737
1738 PHP_STMT_GET_OBJ;
1739 if (colno < 0) {
1740 zend_argument_value_error(1, "must be greater than or equal to 0");
1741 RETURN_THROWS();
1742 }
1743
1744 if (!stmt->methods->get_column_meta) {
1745 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1746 RETURN_FALSE;
1747 }
1748
1749 PDO_STMT_CLEAR_ERR();
1750 if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1751 PDO_HANDLE_STMT_ERR();
1752 RETURN_FALSE;
1753 }
1754
1755 /* add stock items */
1756 col = &stmt->columns[colno];
1757 add_assoc_str(return_value, "name", zend_string_copy(col->name));
1758 add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1759 add_assoc_long(return_value, "precision", col->precision);
1760 if (col->param_type != PDO_PARAM_ZVAL) {
1761 /* if param_type is PDO_PARAM_ZVAL the driver has to provide correct data */
1762 add_assoc_long(return_value, "pdo_type", col->param_type);
1763 }
1764 }
1765 /* }}} */
1766
1767 /* {{{ Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1768
pdo_stmt_setup_fetch_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,zval * args,uint32_t variadic_num_args)1769 bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num,
1770 zval *args, uint32_t variadic_num_args)
1771 {
1772 int flags = 0;
1773 uint32_t arg1_arg_num = mode_arg_num + 1;
1774 uint32_t constructor_arg_num = mode_arg_num + 2;
1775 uint32_t total_num_args = mode_arg_num + variadic_num_args;
1776
1777 switch (stmt->default_fetch_type) {
1778 case PDO_FETCH_INTO:
1779 if (!Z_ISUNDEF(stmt->fetch.into)) {
1780 zval_ptr_dtor(&stmt->fetch.into);
1781 ZVAL_UNDEF(&stmt->fetch.into);
1782 }
1783 break;
1784 default:
1785 ;
1786 }
1787
1788 stmt->default_fetch_type = PDO_FETCH_BOTH;
1789
1790 flags = mode & PDO_FETCH_FLAGS;
1791
1792 if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {
1793 return false;
1794 }
1795
1796 switch (mode & ~PDO_FETCH_FLAGS) {
1797 case PDO_FETCH_USE_DEFAULT:
1798 case PDO_FETCH_LAZY:
1799 case PDO_FETCH_ASSOC:
1800 case PDO_FETCH_NUM:
1801 case PDO_FETCH_BOTH:
1802 case PDO_FETCH_OBJ:
1803 case PDO_FETCH_BOUND:
1804 case PDO_FETCH_NAMED:
1805 case PDO_FETCH_KEY_PAIR:
1806 if (variadic_num_args != 0) {
1807 zend_string *func = get_active_function_or_method_name();
1808 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1809 ZSTR_VAL(func), mode_arg_num, total_num_args);
1810 zend_string_release(func);
1811 return false;
1812 }
1813 break;
1814
1815 case PDO_FETCH_COLUMN:
1816 if (variadic_num_args != 1) {
1817 zend_string *func = get_active_function_or_method_name();
1818 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1819 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1820 zend_string_release(func);
1821 return false;
1822 }
1823 if (Z_TYPE(args[0]) != IS_LONG) {
1824 zend_argument_type_error(arg1_arg_num, "must be of type int, %s given", zend_zval_type_name(&args[0]));
1825 return false;
1826 }
1827 if (Z_LVAL(args[0]) < 0) {
1828 zend_argument_value_error(arg1_arg_num, "must be greater than or equal to 0");
1829 return false;
1830 }
1831 stmt->fetch.column = Z_LVAL(args[0]);
1832 break;
1833
1834 case PDO_FETCH_CLASS: {
1835 HashTable *constructor_args = NULL;
1836 /* Undef constructor arguments */
1837 ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1838 /* Gets its class name from 1st column */
1839 if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1840 if (variadic_num_args != 0) {
1841 zend_string *func = get_active_function_or_method_name();
1842 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1843 ZSTR_VAL(func), mode_arg_num, total_num_args);
1844 zend_string_release(func);
1845 return false;
1846 }
1847 stmt->fetch.cls.ce = NULL;
1848 } else {
1849 zend_class_entry *cep;
1850 if (variadic_num_args == 0) {
1851 zend_string *func = get_active_function_or_method_name();
1852 zend_argument_count_error("%s() expects at least %d arguments for the fetch mode provided, %d given",
1853 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1854 zend_string_release(func);
1855 return false;
1856 }
1857 /* constructor_arguments can be null/not passed */
1858 if (variadic_num_args > 2) {
1859 zend_string *func = get_active_function_or_method_name();
1860 zend_argument_count_error("%s() expects at most %d arguments for the fetch mode provided, %d given",
1861 ZSTR_VAL(func), constructor_arg_num, total_num_args);
1862 zend_string_release(func);
1863 return false;
1864 }
1865 if (Z_TYPE(args[0]) != IS_STRING) {
1866 zend_argument_type_error(arg1_arg_num, "must be of type string, %s given", zend_zval_type_name(&args[0]));
1867 return false;
1868 }
1869 cep = zend_lookup_class(Z_STR(args[0]));
1870 if (!cep) {
1871 zend_argument_type_error(arg1_arg_num, "must be a valid class");
1872 return false;
1873 }
1874 /* Verify constructor_args (args[1]) is ?array */
1875 /* TODO: Improve logic? */
1876 if (variadic_num_args == 2) {
1877 if (Z_TYPE(args[1]) != IS_NULL && Z_TYPE(args[1]) != IS_ARRAY) {
1878 zend_argument_type_error(constructor_arg_num, "must be of type ?array, %s given",
1879 zend_zval_type_name(&args[1]));
1880 return false;
1881 }
1882 if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) {
1883 constructor_args = Z_ARRVAL(args[1]);
1884 }
1885 }
1886 stmt->fetch.cls.ce = cep;
1887
1888 /* If constructor arguments are present and not empty */
1889 if (constructor_args) {
1890 ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(constructor_args));
1891 }
1892 }
1893
1894 do_fetch_class_prepare(stmt);
1895 break;
1896 }
1897 case PDO_FETCH_INTO:
1898 if (total_num_args != arg1_arg_num) {
1899 zend_string *func = get_active_function_or_method_name();
1900 zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1901 ZSTR_VAL(func), arg1_arg_num, total_num_args);
1902 zend_string_release(func);
1903 return false;
1904 }
1905 if (Z_TYPE(args[0]) != IS_OBJECT) {
1906 zend_argument_type_error(arg1_arg_num, "must be of type object, %s given", zend_zval_type_name(&args[0]));
1907 return false;
1908 }
1909
1910 ZVAL_COPY(&stmt->fetch.into, &args[0]);
1911 break;
1912 default:
1913 zend_argument_value_error(mode_arg_num, "must be one of the PDO::FETCH_* constants");
1914 return false;
1915 }
1916
1917 stmt->default_fetch_type = mode;
1918
1919 return true;
1920 }
1921
PHP_METHOD(PDOStatement,setFetchMode)1922 PHP_METHOD(PDOStatement, setFetchMode)
1923 {
1924 zend_long fetch_mode;
1925 zval *args = NULL;
1926 uint32_t num_args = 0;
1927
1928 if (zend_parse_parameters(ZEND_NUM_ARGS(), "l*", &fetch_mode, &args, &num_args) == FAILURE) {
1929 RETURN_THROWS();
1930 }
1931
1932 PHP_STMT_GET_OBJ;
1933
1934 do_fetch_opt_finish(stmt, 1);
1935
1936 if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
1937 RETURN_THROWS();
1938 }
1939
1940 // TODO Void return?
1941 RETURN_TRUE;
1942 }
1943 /* }}} */
1944
1945 /* {{{ Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
1946
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)1947 static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
1948 {
1949 pdo_stmt_reset_columns(stmt);
1950
1951 if (!stmt->methods->next_rowset(stmt)) {
1952 /* Set the executed flag to 0 to reallocate columns on next execute */
1953 stmt->executed = 0;
1954 return 0;
1955 }
1956
1957 pdo_stmt_describe_columns(stmt);
1958
1959 return 1;
1960 }
1961
PHP_METHOD(PDOStatement,nextRowset)1962 PHP_METHOD(PDOStatement, nextRowset)
1963 {
1964 ZEND_PARSE_PARAMETERS_NONE();
1965
1966 PHP_STMT_GET_OBJ;
1967 if (!stmt->methods->next_rowset) {
1968 pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
1969 RETURN_FALSE;
1970 }
1971
1972 PDO_STMT_CLEAR_ERR();
1973
1974 if (!pdo_stmt_do_next_rowset(stmt)) {
1975 PDO_HANDLE_STMT_ERR();
1976 RETURN_FALSE;
1977 }
1978
1979 RETURN_TRUE;
1980 }
1981 /* }}} */
1982
1983 /* {{{ Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)1984 PHP_METHOD(PDOStatement, closeCursor)
1985 {
1986 ZEND_PARSE_PARAMETERS_NONE();
1987
1988 PHP_STMT_GET_OBJ;
1989 if (!stmt->methods->cursor_closer) {
1990 /* emulate it by fetching and discarding rows */
1991 do {
1992 while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
1993 ;
1994 if (!stmt->methods->next_rowset) {
1995 break;
1996 }
1997
1998 if (!pdo_stmt_do_next_rowset(stmt)) {
1999 break;
2000 }
2001
2002 } while (1);
2003 stmt->executed = 0;
2004 RETURN_TRUE;
2005 }
2006
2007 PDO_STMT_CLEAR_ERR();
2008
2009 if (!stmt->methods->cursor_closer(stmt)) {
2010 PDO_HANDLE_STMT_ERR();
2011 RETURN_FALSE;
2012 }
2013 stmt->executed = 0;
2014 RETURN_TRUE;
2015 }
2016 /* }}} */
2017
2018 /* {{{ A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)2019 PHP_METHOD(PDOStatement, debugDumpParams)
2020 {
2021 ZEND_PARSE_PARAMETERS_NONE();
2022
2023 php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
2024 struct pdo_bound_param_data *param;
2025
2026 ZEND_PARSE_PARAMETERS_NONE();
2027
2028 PHP_STMT_GET_OBJ;
2029
2030 if (out == NULL) {
2031 RETURN_FALSE;
2032 }
2033
2034 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
2035 php_stream_printf(out, "SQL: [%zd] ", stmt->query_stringlen);
2036 php_stream_write(out, stmt->query_string, stmt->query_stringlen);
2037 php_stream_write(out, "\n", 1);
2038
2039 /* show parsed SQL if emulated prepares enabled */
2040 /* pointers will be equal if PDO::query() was invoked */
2041 if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
2042 /* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
2043 php_stream_printf(out, "Sent SQL: [%zd] ", stmt->active_query_stringlen);
2044 php_stream_write(out, stmt->active_query_string, stmt->active_query_stringlen);
2045 php_stream_write(out, "\n", 1);
2046 }
2047
2048 php_stream_printf(out, "Params: %d\n",
2049 stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
2050
2051 if (stmt->bound_params) {
2052 zend_ulong num;
2053 zend_string *key = NULL;
2054 ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
2055 if (key) {
2056 php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
2057 ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
2058 } else {
2059 php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
2060 }
2061
2062 php_stream_printf(out,
2063 "paramno=" ZEND_LONG_FMT "\n"
2064 "name=[%zd] \"%.*s\"\n"
2065 "is_param=%d\n"
2066 "param_type=%d\n",
2067 param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
2068 param->name ? ZSTR_VAL(param->name) : "",
2069 param->is_param,
2070 param->param_type);
2071
2072 } ZEND_HASH_FOREACH_END();
2073 }
2074
2075 php_stream_close(out);
2076 }
2077 /* }}} */
2078
PHP_METHOD(PDOStatement,getIterator)2079 PHP_METHOD(PDOStatement, getIterator)
2080 {
2081 if (zend_parse_parameters_none() == FAILURE) {
2082 return;
2083 }
2084
2085 zend_create_internal_iterator_zval(return_value, ZEND_THIS);
2086 }
2087
2088 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2089 static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2090 {
2091 if (strcmp(ZSTR_VAL(name), "queryString") == 0) {
2092 zend_throw_error(NULL, "Property queryString is read only");
2093 return value;
2094 } else {
2095 return zend_std_write_property(object, name, value, cache_slot);
2096 }
2097 }
2098
dbstmt_prop_delete(zend_object * object,zend_string * name,void ** cache_slot)2099 static void dbstmt_prop_delete(zend_object *object, zend_string *name, void **cache_slot)
2100 {
2101 if (strcmp(ZSTR_VAL(name), "queryString") == 0) {
2102 zend_throw_error(NULL, "Property queryString is read only");
2103 } else {
2104 zend_std_unset_property(object, name, cache_slot);
2105 }
2106 }
2107
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2108 static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2109 {
2110 zend_function *fbc = NULL;
2111 zend_string *lc_method_name;
2112 zend_object *object = *object_pp;
2113
2114 lc_method_name = zend_string_tolower(method_name);
2115
2116 if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2117 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2118 /* instance not created by PDO object */
2119 if (!stmt->dbh) {
2120 goto out;
2121 }
2122 /* not a pre-defined method, nor a user-defined method; check
2123 * the driver specific methods */
2124 if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2125 if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2126 PDO_DBH_DRIVER_METHOD_KIND_STMT)
2127 || !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2128 goto out;
2129 }
2130 }
2131
2132 if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2133 goto out;
2134 }
2135 /* got it */
2136 }
2137
2138 out:
2139 zend_string_release_ex(lc_method_name, 0);
2140 if (!fbc) {
2141 fbc = zend_std_get_method(object_pp, method_name, key);
2142 }
2143 return fbc;
2144 }
2145
dbstmt_compare(zval * object1,zval * object2)2146 static int dbstmt_compare(zval *object1, zval *object2)
2147 {
2148 return ZEND_UNCOMPARABLE;
2149 }
2150
2151 zend_object_handlers pdo_dbstmt_object_handlers;
2152 zend_object_handlers pdo_row_object_handlers;
2153
php_pdo_free_statement(pdo_stmt_t * stmt)2154 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2155 {
2156 if (stmt->bound_params) {
2157 zend_hash_destroy(stmt->bound_params);
2158 FREE_HASHTABLE(stmt->bound_params);
2159 stmt->bound_params = NULL;
2160 }
2161 if (stmt->bound_param_map) {
2162 zend_hash_destroy(stmt->bound_param_map);
2163 FREE_HASHTABLE(stmt->bound_param_map);
2164 stmt->bound_param_map = NULL;
2165 }
2166 if (stmt->bound_columns) {
2167 zend_hash_destroy(stmt->bound_columns);
2168 FREE_HASHTABLE(stmt->bound_columns);
2169 stmt->bound_columns = NULL;
2170 }
2171
2172 if (stmt->methods && stmt->methods->dtor) {
2173 stmt->methods->dtor(stmt);
2174 }
2175 if (stmt->active_query_string && stmt->active_query_string != stmt->query_string) {
2176 efree(stmt->active_query_string);
2177 }
2178 if (stmt->query_string) {
2179 efree(stmt->query_string);
2180 }
2181
2182 pdo_stmt_reset_columns(stmt);
2183
2184 if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2185 zval_ptr_dtor(&stmt->fetch.into);
2186 ZVAL_UNDEF(&stmt->fetch.into);
2187 }
2188
2189 do_fetch_opt_finish(stmt, 1);
2190
2191 if (!Z_ISUNDEF(stmt->database_object_handle)) {
2192 zval_ptr_dtor(&stmt->database_object_handle);
2193 }
2194 zend_object_std_dtor(&stmt->std);
2195 }
2196
pdo_dbstmt_free_storage(zend_object * std)2197 void pdo_dbstmt_free_storage(zend_object *std)
2198 {
2199 pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2200 php_pdo_free_statement(stmt);
2201 }
2202
pdo_dbstmt_new(zend_class_entry * ce)2203 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2204 {
2205 pdo_stmt_t *stmt;
2206
2207 stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2208 zend_object_std_init(&stmt->std, ce);
2209 object_properties_init(&stmt->std, ce);
2210
2211 stmt->std.handlers = &pdo_dbstmt_object_handlers;
2212
2213 return &stmt->std;
2214 }
2215 /* }}} */
2216
2217 /* {{{ statement iterator */
2218
2219 struct php_pdo_iterator {
2220 zend_object_iterator iter;
2221 zend_ulong key;
2222 zval fetch_ahead;
2223 };
2224
pdo_stmt_iter_dtor(zend_object_iterator * iter)2225 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2226 {
2227 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2228
2229 zval_ptr_dtor(&I->iter.data);
2230
2231 if (!Z_ISUNDEF(I->fetch_ahead)) {
2232 zval_ptr_dtor(&I->fetch_ahead);
2233 }
2234 }
2235
pdo_stmt_iter_valid(zend_object_iterator * iter)2236 static int pdo_stmt_iter_valid(zend_object_iterator *iter)
2237 {
2238 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2239
2240 return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2241 }
2242
pdo_stmt_iter_get_data(zend_object_iterator * iter)2243 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2244 {
2245 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2246
2247 /* sanity */
2248 if (Z_ISUNDEF(I->fetch_ahead)) {
2249 return NULL;
2250 }
2251
2252 return &I->fetch_ahead;
2253 }
2254
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2255 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2256 {
2257 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2258
2259 if (I->key == (zend_ulong)-1) {
2260 ZVAL_NULL(key);
2261 } else {
2262 ZVAL_LONG(key, I->key);
2263 }
2264 }
2265
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2266 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2267 {
2268 struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2269 pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2270
2271 if (!Z_ISUNDEF(I->fetch_ahead)) {
2272 zval_ptr_dtor(&I->fetch_ahead);
2273 }
2274
2275 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2276 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2277
2278 PDO_HANDLE_STMT_ERR();
2279 I->key = (zend_ulong)-1;
2280 ZVAL_UNDEF(&I->fetch_ahead);
2281
2282 return;
2283 }
2284
2285 I->key++;
2286 }
2287
2288 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2289 pdo_stmt_iter_dtor,
2290 pdo_stmt_iter_valid,
2291 pdo_stmt_iter_get_data,
2292 pdo_stmt_iter_get_key,
2293 pdo_stmt_iter_move_forwards,
2294 NULL,
2295 NULL,
2296 NULL, /* get_gc */
2297 };
2298
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2299 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2300 {
2301 if (by_ref) {
2302 zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2303 return NULL;
2304 }
2305
2306 pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2307 if (!stmt->dbh) {
2308 zend_throw_error(NULL, "PDO object is uninitialized");
2309 return NULL;
2310 }
2311
2312 struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2313 zend_iterator_init(&I->iter);
2314 I->iter.funcs = &pdo_stmt_iter_funcs;
2315 Z_ADDREF_P(object);
2316 ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2317
2318 if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2319 PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2320 PDO_HANDLE_STMT_ERR();
2321 I->key = (zend_ulong)-1;
2322 ZVAL_UNDEF(&I->fetch_ahead);
2323 }
2324
2325 return &I->iter;
2326 }
2327
2328 /* }}} */
2329
2330 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2331
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2332 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2333 {
2334 pdo_row_t *row = (pdo_row_t *)object;
2335 pdo_stmt_t *stmt = row->stmt;
2336 int colno = -1;
2337 zend_long lval;
2338
2339 ZVAL_NULL(rv);
2340 if (stmt) {
2341 if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2342 if (lval >= 0 && lval < stmt->column_count) {
2343 fetch_value(stmt, rv, lval, NULL);
2344 }
2345 } else {
2346 /* TODO: replace this with a hash of available column names to column
2347 * numbers */
2348 for (colno = 0; colno < stmt->column_count; colno++) {
2349 if (ZSTR_LEN(stmt->columns[colno].name) == ZSTR_LEN(name) &&
2350 strncmp(ZSTR_VAL(stmt->columns[colno].name), ZSTR_VAL(name), ZSTR_LEN(name)) == 0) {
2351 fetch_value(stmt, rv, colno, NULL);
2352 return rv;
2353 }
2354 }
2355 if (strcmp(ZSTR_VAL(name), "queryString") == 0) {
2356 //zval_ptr_dtor(rv);
2357 return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2358 }
2359 }
2360 }
2361
2362 return rv;
2363 }
2364
row_dim_read(zend_object * object,zval * member,int type,zval * rv)2365 static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv)
2366 {
2367 pdo_row_t *row = (pdo_row_t *)object;
2368 pdo_stmt_t *stmt = row->stmt;
2369 int colno = -1;
2370 zend_long lval;
2371
2372 ZVAL_NULL(rv);
2373 if (stmt) {
2374 if (Z_TYPE_P(member) == IS_LONG) {
2375 if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2376 fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2377 }
2378 } else if (Z_TYPE_P(member) == IS_STRING
2379 && is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2380 if (lval >= 0 && lval < stmt->column_count) {
2381 fetch_value(stmt, rv, lval, NULL);
2382 }
2383 } else {
2384 if (!try_convert_to_string(member)) {
2385 return &EG(uninitialized_zval);
2386 }
2387
2388 /* TODO: replace this with a hash of available column names to column
2389 * numbers */
2390 for (colno = 0; colno < stmt->column_count; colno++) {
2391 if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2392 strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2393 fetch_value(stmt, rv, colno, NULL);
2394 return rv;
2395 }
2396 }
2397 if (strcmp(Z_STRVAL_P(member), "queryString") == 0) {
2398 //zval_ptr_dtor(rv);
2399 return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv);
2400 }
2401 }
2402 }
2403
2404 return rv;
2405 }
2406
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2407 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2408 {
2409 zend_throw_error(NULL, "Cannot write to PDORow property");
2410 return value;
2411 }
2412
row_dim_write(zend_object * object,zval * member,zval * value)2413 static void row_dim_write(zend_object *object, zval *member, zval *value)
2414 {
2415 zend_throw_error(NULL, "Cannot write to PDORow offset");
2416 }
2417
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2418 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2419 {
2420 pdo_row_t *row = (pdo_row_t *)object;
2421 pdo_stmt_t *stmt = row->stmt;
2422 int colno = -1;
2423 zend_long lval;
2424
2425 if (stmt) {
2426 if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2427 return lval >=0 && lval < stmt->column_count;
2428 }
2429
2430 /* TODO: replace this with a hash of available column names to column
2431 * numbers */
2432 for (colno = 0; colno < stmt->column_count; colno++) {
2433 if (ZSTR_LEN(stmt->columns[colno].name) == ZSTR_LEN(name) &&
2434 strncmp(ZSTR_VAL(stmt->columns[colno].name), ZSTR_VAL(name), ZSTR_LEN(name)) == 0) {
2435 int res;
2436 zval val;
2437
2438 fetch_value(stmt, &val, colno, NULL);
2439 res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2440 zval_ptr_dtor_nogc(&val);
2441
2442 return res;
2443 }
2444 }
2445 }
2446
2447 return 0;
2448 }
2449
row_dim_exists(zend_object * object,zval * member,int check_empty)2450 static int row_dim_exists(zend_object *object, zval *member, int check_empty)
2451 {
2452 pdo_row_t *row = (pdo_row_t *)object;
2453 pdo_stmt_t *stmt = row->stmt;
2454 int colno = -1;
2455 zend_long lval;
2456
2457 if (stmt) {
2458 if (Z_TYPE_P(member) == IS_LONG) {
2459 return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2460 } else if (Z_TYPE_P(member) == IS_STRING) {
2461 if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG) {
2462 return lval >=0 && lval < stmt->column_count;
2463 }
2464 } else {
2465 if (!try_convert_to_string(member)) {
2466 return 0;
2467 }
2468 }
2469
2470 /* TODO: replace this with a hash of available column names to column
2471 * numbers */
2472 for (colno = 0; colno < stmt->column_count; colno++) {
2473 if (ZSTR_LEN(stmt->columns[colno].name) == Z_STRLEN_P(member) &&
2474 strncmp(ZSTR_VAL(stmt->columns[colno].name), Z_STRVAL_P(member), Z_STRLEN_P(member)) == 0) {
2475 int res;
2476 zval val;
2477
2478 fetch_value(stmt, &val, colno, NULL);
2479 res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2480 zval_ptr_dtor_nogc(&val);
2481
2482 return res;
2483 }
2484 }
2485 }
2486
2487 return 0;
2488 }
2489
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2490 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2491 {
2492 zend_throw_error(NULL, "Cannot unset PDORow property");
2493 }
2494
row_dim_delete(zend_object * object,zval * offset)2495 static void row_dim_delete(zend_object *object, zval *offset)
2496 {
2497 zend_throw_error(NULL, "Cannot unset PDORow offset");
2498 }
2499
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2500 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2501 {
2502 pdo_row_t *row = (pdo_row_t *)object;
2503 pdo_stmt_t *stmt = row->stmt;
2504 HashTable *props;
2505 int i;
2506
2507 if (purpose != ZEND_PROP_PURPOSE_DEBUG || stmt == NULL) {
2508 return zend_std_get_properties_for(object, purpose);
2509 }
2510
2511 if (!stmt->std.properties) {
2512 rebuild_object_properties(&stmt->std);
2513 }
2514 props = zend_array_dup(stmt->std.properties);
2515 for (i = 0; i < stmt->column_count; i++) {
2516 zval val;
2517 fetch_value(stmt, &val, i, NULL);
2518
2519 zend_hash_update(props, stmt->columns[i].name, &val);
2520 }
2521 return props;
2522 }
2523
row_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2524 static zend_function *row_method_get(
2525 zend_object **object_pp,
2526 zend_string *method_name, const zval *key)
2527 {
2528 zend_function *fbc;
2529 zend_string *lc_method_name;
2530
2531 lc_method_name = zend_string_tolower(method_name);
2532
2533 if ((fbc = zend_hash_find_ptr(&pdo_row_ce->function_table, lc_method_name)) == NULL) {
2534 zend_string_release_ex(lc_method_name, 0);
2535 return NULL;
2536 }
2537
2538 zend_string_release_ex(lc_method_name, 0);
2539
2540 return fbc;
2541 }
2542
row_get_ctor(zend_object * object)2543 static zend_function *row_get_ctor(zend_object *object)
2544 {
2545 zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2546 return NULL;
2547 }
2548
row_get_classname(const zend_object * object)2549 static zend_string *row_get_classname(const zend_object *object)
2550 {
2551 return zend_string_init("PDORow", sizeof("PDORow") - 1, 0);
2552 }
2553
row_compare(zval * object1,zval * object2)2554 static int row_compare(zval *object1, zval *object2)
2555 {
2556 return ZEND_UNCOMPARABLE;
2557 }
2558
pdo_row_free_storage(zend_object * std)2559 void pdo_row_free_storage(zend_object *std)
2560 {
2561 pdo_row_t *row = (pdo_row_t *)std;
2562 if (row->stmt) {
2563 ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2564 OBJ_RELEASE(&row->stmt->std);
2565 }
2566 }
2567
pdo_row_new(zend_class_entry * ce)2568 zend_object *pdo_row_new(zend_class_entry *ce)
2569 {
2570 pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2571 zend_object_std_init(&row->std, ce);
2572 row->std.handlers = &pdo_row_object_handlers;
2573
2574 return &row->std;
2575 }
2576
pdo_stmt_init(void)2577 void pdo_stmt_init(void)
2578 {
2579 zend_class_entry ce;
2580
2581 INIT_CLASS_ENTRY(ce, "PDOStatement", class_PDOStatement_methods);
2582 pdo_dbstmt_ce = zend_register_internal_class(&ce);
2583 pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2584 pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2585 pdo_dbstmt_ce->serialize = zend_class_serialize_deny;
2586 pdo_dbstmt_ce->unserialize = zend_class_unserialize_deny;
2587 zend_class_implements(pdo_dbstmt_ce, 1, zend_ce_aggregate);
2588 zend_declare_property_null(pdo_dbstmt_ce, "queryString", sizeof("queryString")-1, ZEND_ACC_PUBLIC);
2589
2590 memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2591 pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2592 pdo_dbstmt_object_handlers.dtor_obj = zend_objects_destroy_object;
2593 pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2594 pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2595 pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2596 pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2597 pdo_dbstmt_object_handlers.compare = dbstmt_compare;
2598 pdo_dbstmt_object_handlers.clone_obj = NULL;
2599
2600 INIT_CLASS_ENTRY(ce, "PDORow", class_PDORow_methods);
2601 pdo_row_ce = zend_register_internal_class(&ce);
2602 pdo_row_ce->ce_flags |= ZEND_ACC_FINAL; /* when removing this a lot of handlers need to be redone */
2603 pdo_row_ce->create_object = pdo_row_new;
2604 pdo_row_ce->serialize = zend_class_serialize_deny;
2605 pdo_row_ce->unserialize = zend_class_unserialize_deny;
2606
2607 memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2608 pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2609 pdo_row_object_handlers.clone_obj = NULL;
2610 pdo_row_object_handlers.get_property_ptr_ptr = NULL;
2611 pdo_row_object_handlers.read_property = row_prop_read;
2612 pdo_row_object_handlers.write_property = row_prop_write;
2613 pdo_row_object_handlers.has_property = row_prop_exists;
2614 pdo_row_object_handlers.unset_property = row_prop_delete;
2615 pdo_row_object_handlers.read_dimension = row_dim_read;
2616 pdo_row_object_handlers.write_dimension = row_dim_write;
2617 pdo_row_object_handlers.has_dimension = row_dim_exists;
2618 pdo_row_object_handlers.unset_dimension = row_dim_delete;
2619 pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2620 pdo_row_object_handlers.get_method = row_method_get;
2621 pdo_row_object_handlers.get_constructor = row_get_ctor;
2622 pdo_row_object_handlers.get_class_name = row_get_classname;
2623 pdo_row_object_handlers.compare = row_compare;
2624 }
2625