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