xref: /PHP-8.0/ext/pdo/pdo_stmt.c (revision df52903e)
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(&param->parameter);
244 		ZVAL_UNDEF(&param->parameter);
245 	}
246 	if (!Z_ISUNDEF(param->driver_params)) {
247 		zval_ptr_dtor(&param->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 = &param->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(&param->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(&param, 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(&param.parameter, tmp);
428 
429 			if (!really_register_bound_param(&param, stmt, 1)) {
430 				if (!Z_ISUNDEF(param.parameter)) {
431 					zval_ptr_dtor(&param.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 *)&param->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(&param, 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(&param.driver_params, driver_params);
1511 	}
1512 
1513 	ZVAL_COPY(&param.parameter, parameter);
1514 	if (!really_register_bound_param(&param, 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(&param, 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(&param.parameter, parameter);
1558 	if (!really_register_bound_param(&param, stmt, TRUE)) {
1559 		if (!Z_ISUNDEF(param.parameter)) {
1560 			zval_ptr_dtor(&(param.parameter));
1561 			ZVAL_UNDEF(&param.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