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 (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