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