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