xref: /PHP-8.2/ext/pdo/pdo_stmt.c (revision d6a0b3af)
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  * If return_value is not null, store values into it according to HOW. */
do_fetch(pdo_stmt_t * stmt,zval * return_value,enum pdo_fetch_type how,enum pdo_fetch_orientation ori,zend_long offset,zval * return_all)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 no return value we are done */
748 	if (!return_value) {
749 		return true;
750 	}
751 
752 	if (how == PDO_FETCH_LAZY) {
753 		get_lazy_object(stmt, return_value);
754 		return 1;
755 	}
756 
757 	RETVAL_FALSE;
758 
759 	switch (how) {
760 		case PDO_FETCH_USE_DEFAULT:
761 		case PDO_FETCH_ASSOC:
762 		case PDO_FETCH_BOTH:
763 		case PDO_FETCH_NUM:
764 		case PDO_FETCH_NAMED:
765 			if (!return_all) {
766 				array_init_size(return_value, stmt->column_count);
767 			} else {
768 				array_init(return_value);
769 			}
770 			break;
771 
772 		case PDO_FETCH_KEY_PAIR:
773 			if (stmt->column_count != 2) {
774 				/* TODO: Error? */
775 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain exactly 2 columns.");
776 				return 0;
777 			}
778 			if (!return_all) {
779 				array_init(return_value);
780 			}
781 			break;
782 
783 		case PDO_FETCH_COLUMN:
784 			if (colno < 0 ) {
785 				zend_value_error("Column index must be greater than or equal to 0");
786 				return false;
787 			}
788 
789 			if (colno >= stmt->column_count) {
790 				zend_value_error("Invalid column index");
791 				return false;
792 			}
793 
794 			if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
795 				fetch_value(stmt, return_value, 1, NULL);
796 			} else if (flags == PDO_FETCH_GROUP && colno) {
797 				fetch_value(stmt, return_value, 0, NULL);
798 			} else {
799 				fetch_value(stmt, return_value, colno, NULL);
800 			}
801 			if (!return_all) {
802 				return 1;
803 			}
804 			break;
805 
806 		case PDO_FETCH_OBJ:
807 			object_init_ex(return_value, ZEND_STANDARD_CLASS_DEF_PTR);
808 			break;
809 
810 		case PDO_FETCH_CLASS:
811 			if (flags & PDO_FETCH_CLASSTYPE) {
812 				zval val;
813 				zend_class_entry *cep;
814 
815 				old_ce = stmt->fetch.cls.ce;
816 				ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
817 				old_arg_count = stmt->fetch.cls.fci.param_count;
818 				do_fetch_opt_finish(stmt, 0);
819 
820 				fetch_value(stmt, &val, i++, NULL);
821 				if (Z_TYPE(val) != IS_NULL) {
822 					if (!try_convert_to_string(&val)) {
823 						return 0;
824 					}
825 					if ((cep = zend_lookup_class(Z_STR(val))) == NULL) {
826 						stmt->fetch.cls.ce = ZEND_STANDARD_CLASS_DEF_PTR;
827 					} else {
828 						stmt->fetch.cls.ce = cep;
829 					}
830 				}
831 
832 				do_fetch_class_prepare(stmt);
833 				zval_ptr_dtor_str(&val);
834 			}
835 			ce = stmt->fetch.cls.ce;
836 			/* TODO: Make this an assertion and ensure this is true higher up? */
837 			if (!ce) {
838 				/* TODO Error? */
839 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch class specified");
840 				return 0;
841 			}
842 			if ((flags & PDO_FETCH_SERIALIZE) == 0) {
843 				if (UNEXPECTED(object_init_ex(return_value, ce) != SUCCESS)) {
844 					return 0;
845 				}
846 				if (!stmt->fetch.cls.fci.size) {
847 					if (!do_fetch_class_prepare(stmt)) {
848 						zval_ptr_dtor(return_value);
849 						return 0;
850 					}
851 				}
852 				if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
853 					stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
854 					stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
855 					if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
856 						/* TODO Error? */
857 						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
858 						return 0;
859 					} else {
860 						if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
861 							zval_ptr_dtor(&stmt->fetch.cls.retval);
862 							ZVAL_UNDEF(&stmt->fetch.cls.retval);
863 						}
864 					}
865 				}
866 			}
867 			break;
868 
869 		case PDO_FETCH_INTO:
870 			/* TODO: Make this an assertion and ensure this is true higher up? */
871 			if (Z_ISUNDEF(stmt->fetch.into)) {
872 				/* TODO ArgumentCountError? */
873 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch-into object specified.");
874 				return 0;
875 				break;
876 			}
877 
878 			ZVAL_COPY(return_value, &stmt->fetch.into);
879 
880 			if (Z_OBJ_P(return_value)->ce == ZEND_STANDARD_CLASS_DEF_PTR) {
881 				how = PDO_FETCH_OBJ;
882 			}
883 			break;
884 
885 		case PDO_FETCH_FUNC:
886 			/* TODO: Make this an assertion and ensure this is true higher up? */
887 			if (Z_ISUNDEF(stmt->fetch.func.function)) {
888 				/* TODO ArgumentCountError? */
889 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified");
890 				return 0;
891 			}
892 			if (!stmt->fetch.func.fci.size) {
893 				if (!do_fetch_func_prepare(stmt))
894 				{
895 					return 0;
896 				}
897 			}
898 			break;
899 		EMPTY_SWITCH_DEFAULT_CASE();
900 	}
901 
902 	if (return_all && how != PDO_FETCH_KEY_PAIR) {
903 		if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN && stmt->fetch.column > 0) {
904 			fetch_value(stmt, &grp_val, colno, NULL);
905 		} else {
906 			fetch_value(stmt, &grp_val, i, NULL);
907 		}
908 		convert_to_string(&grp_val);
909 		if (how == PDO_FETCH_COLUMN) {
910 			i = stmt->column_count; /* no more data to fetch */
911 		} else {
912 			i++;
913 		}
914 	}
915 
916 	for (idx = 0; i < stmt->column_count; i++, idx++) {
917 		zval val;
918 		fetch_value(stmt, &val, i, NULL);
919 
920 		switch (how) {
921 			case PDO_FETCH_ASSOC:
922 				zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
923 				break;
924 
925 			case PDO_FETCH_KEY_PAIR:
926 				{
927 					zval tmp;
928 					fetch_value(stmt, &tmp, ++i, NULL);
929 
930 					if (Z_TYPE(val) == IS_LONG) {
931 						zend_hash_index_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_LVAL(val), &tmp);
932 					} else {
933 						convert_to_string(&val);
934 						zend_symtable_update((return_all ? Z_ARRVAL_P(return_all) : Z_ARRVAL_P(return_value)), Z_STR(val), &tmp);
935 					}
936 					zval_ptr_dtor(&val);
937 					return 1;
938 				}
939 				break;
940 
941 			case PDO_FETCH_USE_DEFAULT:
942 			case PDO_FETCH_BOTH:
943 				zend_symtable_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
944 				if (zend_hash_index_add(Z_ARRVAL_P(return_value), i, &val) != NULL) {
945 					Z_TRY_ADDREF(val);
946 				}
947 				break;
948 
949 			case PDO_FETCH_NAMED:
950 				/* already have an item with this name? */
951 				{
952 					zval *curr_val;
953 					if ((curr_val = zend_hash_find(Z_ARRVAL_P(return_value), stmt->columns[i].name))) {
954 						zval arr;
955 						if (Z_TYPE_P(curr_val) != IS_ARRAY) {
956 							/* a little bit of black magic here:
957 							 * we're creating a new array and swapping it for the
958 							 * zval that's already stored in the hash under the name
959 							 * we want.  We then add that zval to the array.
960 							 * This is effectively the same thing as:
961 							 * if (!is_array($hash[$name])) {
962 							 *   $hash[$name] = array($hash[$name]);
963 							 * }
964 							 * */
965 							zval cur;
966 
967 							array_init(&arr);
968 
969 							ZVAL_COPY_VALUE(&cur, curr_val);
970 							ZVAL_COPY_VALUE(curr_val, &arr);
971 
972 							zend_hash_next_index_insert_new(Z_ARRVAL(arr), &cur);
973 						} else {
974 							ZVAL_COPY_VALUE(&arr, curr_val);
975 						}
976 						zend_hash_next_index_insert_new(Z_ARRVAL(arr), &val);
977 					} else {
978 						zend_hash_update(Z_ARRVAL_P(return_value), stmt->columns[i].name, &val);
979 					}
980 				}
981 				break;
982 
983 			case PDO_FETCH_NUM:
984 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &val);
985 				break;
986 
987 			case PDO_FETCH_OBJ:
988 			case PDO_FETCH_INTO:
989 				zend_update_property_ex(NULL, Z_OBJ_P(return_value),
990 					stmt->columns[i].name,
991 					&val);
992 				zval_ptr_dtor(&val);
993 				break;
994 
995 			case PDO_FETCH_CLASS:
996 				if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
997 					zend_update_property_ex(ce, Z_OBJ_P(return_value),
998 						stmt->columns[i].name,
999 						&val);
1000 					zval_ptr_dtor(&val);
1001 				} else {
1002 					if (!ce->unserialize) {
1003 						zval_ptr_dtor(&val);
1004 						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1005 						return 0;
1006 					} 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) {
1007 						zval_ptr_dtor(&val);
1008 						pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "cannot unserialize class");
1009 						zval_ptr_dtor(return_value);
1010 						ZVAL_NULL(return_value);
1011 						return 0;
1012 					} else {
1013 						zval_ptr_dtor(&val);
1014 					}
1015 				}
1016 				break;
1017 
1018 			case PDO_FETCH_FUNC:
1019 				ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val);
1020 				ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]);
1021 				break;
1022 
1023 			default:
1024 				zval_ptr_dtor(&val);
1025 				zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants");
1026 				return 0;
1027 		}
1028 	}
1029 
1030 	switch (how) {
1031 		case PDO_FETCH_CLASS:
1032 			if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
1033 				stmt->fetch.cls.fci.object = Z_OBJ_P(return_value);
1034 				stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value);
1035 				if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) {
1036 					/* TODO Error? */
1037 					pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor");
1038 					return 0;
1039 				} else {
1040 					if (!Z_ISUNDEF(stmt->fetch.cls.retval)) {
1041 						zval_ptr_dtor(&stmt->fetch.cls.retval);
1042 					}
1043 				}
1044 			}
1045 			if (flags & PDO_FETCH_CLASSTYPE) {
1046 				do_fetch_opt_finish(stmt, 0);
1047 				stmt->fetch.cls.ce = old_ce;
1048 				ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1049 				stmt->fetch.cls.fci.param_count = old_arg_count;
1050 			}
1051 			break;
1052 
1053 		case PDO_FETCH_FUNC:
1054 			stmt->fetch.func.fci.param_count = idx;
1055 			stmt->fetch.func.fci.retval = &retval;
1056 			if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc) == FAILURE) {
1057 				/* TODO Error? */
1058 				pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call user-supplied function");
1059 				return 0;
1060 			} else {
1061 				if (return_all) {
1062 					zval_ptr_dtor(return_value); /* we don't need that */
1063 					ZVAL_COPY_VALUE(return_value, &retval);
1064 				} else if (!Z_ISUNDEF(retval)) {
1065 					ZVAL_COPY_VALUE(return_value, &retval);
1066 				}
1067 			}
1068 			while (idx--) {
1069 				zval_ptr_dtor(&stmt->fetch.func.values[idx]);
1070 			}
1071 			break;
1072 
1073 		default:
1074 			break;
1075 	}
1076 
1077 	if (return_all) {
1078 		if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
1079 			zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), return_value);
1080 		} else {
1081 			zval grp;
1082 			if ((pgrp = zend_symtable_find(Z_ARRVAL_P(return_all), Z_STR(grp_val))) == NULL) {
1083 				array_init(&grp);
1084 				zend_symtable_update(Z_ARRVAL_P(return_all), Z_STR(grp_val), &grp);
1085 			} else {
1086 				ZVAL_COPY_VALUE(&grp, pgrp);
1087 			}
1088 			zend_hash_next_index_insert(Z_ARRVAL(grp), return_value);
1089 		}
1090 		zval_ptr_dtor_str(&grp_val);
1091 	}
1092 
1093 	return 1;
1094 }
1095 /* }}} */
1096 
pdo_stmt_verify_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,bool fetch_all)1097 static bool pdo_stmt_verify_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num, bool fetch_all) /* {{{ */
1098 {
1099 	int flags = mode & PDO_FETCH_FLAGS;
1100 
1101 	mode = mode & ~PDO_FETCH_FLAGS;
1102 
1103 	if (mode < 0 || mode > PDO_FETCH__MAX) {
1104 		zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1105 		return 0;
1106 	}
1107 
1108 	if (mode == PDO_FETCH_USE_DEFAULT) {
1109 		flags = stmt->default_fetch_type & PDO_FETCH_FLAGS;
1110 		mode = stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1111 	}
1112 
1113 	switch(mode) {
1114 		case PDO_FETCH_FUNC:
1115 			if (!fetch_all) {
1116 				zend_value_error("Can only use PDO::FETCH_FUNC in PDOStatement::fetchAll()");
1117 				return 0;
1118 			}
1119 			return 1;
1120 
1121 		case PDO_FETCH_LAZY:
1122 			if (fetch_all) {
1123 				zend_argument_value_error(mode_arg_num, "cannot be PDO::FETCH_LAZY in PDOStatement::fetchAll()");
1124 				return 0;
1125 			}
1126 			ZEND_FALLTHROUGH;
1127 		default:
1128 			if ((flags & PDO_FETCH_SERIALIZE) == PDO_FETCH_SERIALIZE) {
1129 				zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_SERIALIZE with PDO::FETCH_CLASS");
1130 				return 0;
1131 			}
1132 			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1133 				zend_argument_value_error(mode_arg_num, "must use PDO::FETCH_CLASSTYPE with PDO::FETCH_CLASS");
1134 				return 0;
1135 			}
1136 			if (mode >= PDO_FETCH__MAX) {
1137 				zend_argument_value_error(mode_arg_num, "must be a bitmask of PDO::FETCH_* constants");
1138 				return 0;
1139 			}
1140 			ZEND_FALLTHROUGH;
1141 
1142 		case PDO_FETCH_CLASS:
1143 			if (flags & PDO_FETCH_SERIALIZE) {
1144 				php_error_docref(NULL, E_DEPRECATED, "The PDO::FETCH_SERIALIZE mode is deprecated");
1145 			}
1146 			return 1;
1147 	}
1148 }
1149 /* }}} */
1150 
1151 /* {{{ Fetches the next row and returns it, or false if there are no more rows */
PHP_METHOD(PDOStatement,fetch)1152 PHP_METHOD(PDOStatement, fetch)
1153 {
1154 	zend_long how = PDO_FETCH_USE_DEFAULT;
1155 	zend_long ori = PDO_FETCH_ORI_NEXT;
1156 	zend_long off = 0;
1157 
1158 	ZEND_PARSE_PARAMETERS_START(0, 3)
1159 		Z_PARAM_OPTIONAL
1160 		Z_PARAM_LONG(how)
1161 		Z_PARAM_LONG(ori)
1162 		Z_PARAM_LONG(off)
1163 	ZEND_PARSE_PARAMETERS_END();
1164 
1165 	PHP_STMT_GET_OBJ;
1166 	PDO_STMT_CLEAR_ERR();
1167 
1168 	if (!pdo_stmt_verify_mode(stmt, how, 1, false)) {
1169 		RETURN_THROWS();
1170 	}
1171 
1172 	if (!do_fetch(stmt, return_value, how, ori, off, NULL)) {
1173 		PDO_HANDLE_STMT_ERR();
1174 		RETURN_FALSE;
1175 	}
1176 }
1177 /* }}} */
1178 
1179 /* {{{ Fetches the next row and returns it as an object. */
PHP_METHOD(PDOStatement,fetchObject)1180 PHP_METHOD(PDOStatement, fetchObject)
1181 {
1182 	zend_class_entry *ce = NULL;
1183 	zend_class_entry *old_ce;
1184 	zval old_ctor_args, *ctor_args = NULL;
1185 	int old_arg_count;
1186 
1187 	ZEND_PARSE_PARAMETERS_START(0, 2)
1188 		Z_PARAM_OPTIONAL
1189 		Z_PARAM_CLASS_OR_NULL(ce)
1190 		Z_PARAM_ARRAY(ctor_args)
1191 	ZEND_PARSE_PARAMETERS_END();
1192 
1193 	PHP_STMT_GET_OBJ;
1194 	PDO_STMT_CLEAR_ERR();
1195 
1196 	old_ce = stmt->fetch.cls.ce;
1197 	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1198 	old_arg_count = stmt->fetch.cls.fci.param_count;
1199 
1200 	do_fetch_opt_finish(stmt, 0);
1201 
1202 	if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
1203 		ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(Z_ARRVAL_P(ctor_args)));
1204 	} else {
1205 		ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1206 	}
1207 	if (ce) {
1208 		stmt->fetch.cls.ce = ce;
1209 	} else {
1210 		stmt->fetch.cls.ce = zend_standard_class_def;
1211 	}
1212 
1213 	if (!do_fetch(stmt, return_value, PDO_FETCH_CLASS, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
1214 		PDO_HANDLE_STMT_ERR();
1215 		RETVAL_FALSE;
1216 	}
1217 	do_fetch_opt_finish(stmt, 1);
1218 
1219 	stmt->fetch.cls.ce = old_ce;
1220 	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1221 	stmt->fetch.cls.fci.param_count = old_arg_count;
1222 }
1223 /* }}} */
1224 
1225 /* {{{ Returns a data of the specified column in the result set. */
PHP_METHOD(PDOStatement,fetchColumn)1226 PHP_METHOD(PDOStatement, fetchColumn)
1227 {
1228 	zend_long col_n = 0;
1229 
1230 	ZEND_PARSE_PARAMETERS_START(0, 1)
1231 		Z_PARAM_OPTIONAL
1232 		Z_PARAM_LONG(col_n)
1233 	ZEND_PARSE_PARAMETERS_END();
1234 
1235 	PHP_STMT_GET_OBJ;
1236 	PDO_STMT_CLEAR_ERR();
1237 
1238 	if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0)) {
1239 		PDO_HANDLE_STMT_ERR();
1240 		RETURN_FALSE;
1241 	}
1242 
1243 	fetch_value(stmt, return_value, col_n, NULL);
1244 }
1245 /* }}} */
1246 
1247 /* {{{ Returns an array of all of the results. */
PHP_METHOD(PDOStatement,fetchAll)1248 PHP_METHOD(PDOStatement, fetchAll)
1249 {
1250 	zend_long how = PDO_FETCH_USE_DEFAULT;
1251 	zval data, *return_all = NULL;
1252 	zval *arg2 = NULL;
1253 	zend_class_entry *old_ce;
1254 	zval old_ctor_args, *ctor_args = NULL;
1255 	bool error = false;
1256 	int flags, old_arg_count;
1257 
1258 	ZEND_PARSE_PARAMETERS_START(0, 3)
1259 		Z_PARAM_OPTIONAL
1260 		Z_PARAM_LONG(how)
1261 		Z_PARAM_ZVAL_OR_NULL(arg2)
1262 		Z_PARAM_ARRAY_OR_NULL(ctor_args)
1263 	ZEND_PARSE_PARAMETERS_END();
1264 
1265 	PHP_STMT_GET_OBJ;
1266 	if (!pdo_stmt_verify_mode(stmt, how, 1, true)) {
1267 		RETURN_THROWS();
1268 	}
1269 
1270 	old_ce = stmt->fetch.cls.ce;
1271 	ZVAL_COPY_VALUE(&old_ctor_args, &stmt->fetch.cls.ctor_args);
1272 	old_arg_count = stmt->fetch.cls.fci.param_count;
1273 
1274 	do_fetch_opt_finish(stmt, 0);
1275 
1276 	/* TODO Would be good to reuse part of pdo_stmt_setup_fetch_mode() in some way */
1277 
1278 	switch (how & ~PDO_FETCH_FLAGS) {
1279 		case PDO_FETCH_CLASS:
1280 			/* Figure out correct class */
1281 			if (arg2) {
1282 				if (Z_TYPE_P(arg2) != IS_STRING) {
1283 					zend_argument_type_error(2, "must be of type string, %s given", zend_zval_value_name(arg2));
1284 					RETURN_THROWS();
1285 				}
1286 				stmt->fetch.cls.ce = zend_fetch_class(Z_STR_P(arg2), ZEND_FETCH_CLASS_AUTO);
1287 				if (!stmt->fetch.cls.ce) {
1288 					zend_argument_type_error(2, "must be a valid class");
1289 					RETURN_THROWS();
1290 				}
1291 			} else {
1292 				stmt->fetch.cls.ce = zend_standard_class_def;
1293 			}
1294 
1295 			if (ctor_args && zend_hash_num_elements(Z_ARRVAL_P(ctor_args)) > 0) {
1296 				ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, ctor_args); /* we're not going to free these */
1297 			} else {
1298 				ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1299 			}
1300 
1301 			do_fetch_class_prepare(stmt);
1302 			break;
1303 
1304 		case PDO_FETCH_FUNC: /* Cannot be a default fetch mode */
1305 			if (ZEND_NUM_ARGS() != 2) {
1306 				zend_string *func = get_active_function_or_method_name();
1307 				zend_argument_count_error("%s() expects exactly 2 argument for PDO::FETCH_FUNC, %d given",
1308 					ZSTR_VAL(func), ZEND_NUM_ARGS());
1309 				zend_string_release(func);
1310 				RETURN_THROWS();
1311 			}
1312 			if (arg2 == NULL) {
1313 				/* TODO use "must be of type callable" format? */
1314 				zend_argument_type_error(2, "must be a callable, null given");
1315 				RETURN_THROWS();
1316 			}
1317 			/* TODO Check it is a callable? */
1318 			ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2);
1319 			if (do_fetch_func_prepare(stmt) == false) {
1320 				RETURN_THROWS();
1321 			}
1322 			break;
1323 
1324 		case PDO_FETCH_COLUMN:
1325 			if (ZEND_NUM_ARGS() > 2) {
1326 				zend_string *func = get_active_function_or_method_name();
1327 				zend_argument_count_error("%s() expects at most 2 argument for the fetch mode provided, %d given",
1328 					ZSTR_VAL(func), ZEND_NUM_ARGS());
1329 				zend_string_release(func);
1330 				RETURN_THROWS();
1331 			}
1332 			/* Is column index passed? */
1333 			if (arg2) {
1334 				// Reuse convert_to_long(arg2); ?
1335 				if (Z_TYPE_P(arg2) != IS_LONG) {
1336 					zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(arg2));
1337 					RETURN_THROWS();
1338 				}
1339 				if (Z_LVAL_P(arg2) < 0) {
1340 					zend_argument_value_error(2, "must be greater than or equal to 0");
1341 					RETURN_THROWS();
1342 				}
1343 				stmt->fetch.column = Z_LVAL_P(arg2);
1344 			} else {
1345 				stmt->fetch.column = how & PDO_FETCH_GROUP ? -1 : 0;
1346 			}
1347 			break;
1348 
1349 		default:
1350 			/* No support for PDO_FETCH_INTO which takes 2 args??? */
1351 			if (ZEND_NUM_ARGS() > 1) {
1352 				zend_string *func = get_active_function_or_method_name();
1353 				zend_argument_count_error("%s() expects exactly 1 argument for the fetch mode provided, %d given",
1354 				ZSTR_VAL(func), ZEND_NUM_ARGS());
1355 				zend_string_release(func);
1356 				RETURN_THROWS();
1357 			}
1358 	}
1359 
1360 	flags = how & PDO_FETCH_FLAGS;
1361 
1362 	if ((how & ~PDO_FETCH_FLAGS) == PDO_FETCH_USE_DEFAULT) {
1363 		flags |= stmt->default_fetch_type & PDO_FETCH_FLAGS;
1364 		how |= stmt->default_fetch_type & ~PDO_FETCH_FLAGS;
1365 	}
1366 
1367 	PDO_STMT_CLEAR_ERR();
1368 	if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1369 		(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1370 	) {
1371 		array_init(return_value);
1372 		return_all = return_value;
1373 	}
1374 	if (!do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all)) {
1375 		error = true;
1376 	}
1377 
1378 	if (!error) {
1379 		if ((how & PDO_FETCH_GROUP) || how == PDO_FETCH_KEY_PAIR ||
1380 			(how == PDO_FETCH_USE_DEFAULT && stmt->default_fetch_type == PDO_FETCH_KEY_PAIR)
1381 		) {
1382 			while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, return_all));
1383 		} else {
1384 			array_init(return_value);
1385 			do {
1386 				zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &data);
1387 			} while (do_fetch(stmt, &data, how | flags, PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL));
1388 		}
1389 	}
1390 
1391 	do_fetch_opt_finish(stmt, 0);
1392 
1393 	/* Restore defaults which were changed by PDO_FETCH_CLASS mode */
1394 	stmt->fetch.cls.ce = old_ce;
1395 	ZVAL_COPY_VALUE(&stmt->fetch.cls.ctor_args, &old_ctor_args);
1396 	stmt->fetch.cls.fci.param_count = old_arg_count;
1397 
1398 	/* on no results, return an empty array */
1399 	if (error) {
1400 		PDO_HANDLE_STMT_ERR();
1401 		if (Z_TYPE_P(return_value) != IS_ARRAY) {
1402 			array_init(return_value);
1403 		}
1404 	}
1405 }
1406 /* }}} */
1407 
register_bound_param(INTERNAL_FUNCTION_PARAMETERS,int is_param)1408 static void register_bound_param(INTERNAL_FUNCTION_PARAMETERS, int is_param) /* {{{ */
1409 {
1410 	struct pdo_bound_param_data param;
1411 	zend_long param_type = PDO_PARAM_STR;
1412 	zval *parameter, *driver_params = NULL;
1413 
1414 	memset(&param, 0, sizeof(param));
1415 
1416 	ZEND_PARSE_PARAMETERS_START(2, 5)
1417 		Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1418 		Z_PARAM_ZVAL(parameter)
1419 		Z_PARAM_OPTIONAL
1420 		Z_PARAM_LONG(param_type)
1421 		Z_PARAM_LONG(param.max_value_len)
1422 		Z_PARAM_ZVAL_OR_NULL(driver_params)
1423 	ZEND_PARSE_PARAMETERS_END();
1424 
1425 	PHP_STMT_GET_OBJ;
1426 
1427 	param.param_type = (int) param_type;
1428 
1429 	if (param.name) {
1430 		if (ZSTR_LEN(param.name) == 0) {
1431 			zend_argument_value_error(1, "cannot be empty");
1432 			RETURN_THROWS();
1433 		}
1434 		param.paramno = -1;
1435 	} else if (param.paramno > 0) {
1436 		--param.paramno; /* make it zero-based internally */
1437 	} else {
1438 		zend_argument_value_error(1, "must be greater than or equal to 1");
1439 		RETURN_THROWS();
1440 	}
1441 
1442 	if (driver_params) {
1443 		ZVAL_COPY(&param.driver_params, driver_params);
1444 	}
1445 
1446 	ZVAL_COPY(&param.parameter, parameter);
1447 	if (!really_register_bound_param(&param, stmt, is_param)) {
1448 		if (!Z_ISUNDEF(param.parameter)) {
1449 			zval_ptr_dtor(&(param.parameter));
1450 		}
1451 
1452 		RETURN_FALSE;
1453 	}
1454 
1455 	RETURN_TRUE;
1456 } /* }}} */
1457 
1458 /* {{{ 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)1459 PHP_METHOD(PDOStatement, bindValue)
1460 {
1461 	struct pdo_bound_param_data param;
1462 	zend_long param_type = PDO_PARAM_STR;
1463 	zval *parameter;
1464 
1465 	memset(&param, 0, sizeof(param));
1466 
1467 	ZEND_PARSE_PARAMETERS_START(2, 3)
1468 		Z_PARAM_STR_OR_LONG(param.name, param.paramno)
1469 		Z_PARAM_ZVAL(parameter)
1470 		Z_PARAM_OPTIONAL
1471 		Z_PARAM_LONG(param_type)
1472 	ZEND_PARSE_PARAMETERS_END();
1473 
1474 	PHP_STMT_GET_OBJ;
1475 	param.param_type = (int) param_type;
1476 
1477 	if (param.name) {
1478 		if (ZSTR_LEN(param.name) == 0) {
1479 			zend_argument_value_error(1, "cannot be empty");
1480 			RETURN_THROWS();
1481 		}
1482 		param.paramno = -1;
1483 	} else if (param.paramno > 0) {
1484 		--param.paramno; /* make it zero-based internally */
1485 	} else {
1486 		zend_argument_value_error(1, "must be greater than or equal to 1");
1487 		RETURN_THROWS();
1488 	}
1489 
1490 	ZVAL_COPY(&param.parameter, parameter);
1491 	if (!really_register_bound_param(&param, stmt, TRUE)) {
1492 		if (!Z_ISUNDEF(param.parameter)) {
1493 			zval_ptr_dtor(&(param.parameter));
1494 			ZVAL_UNDEF(&param.parameter);
1495 		}
1496 		RETURN_FALSE;
1497 	}
1498 	RETURN_TRUE;
1499 }
1500 /* }}} */
1501 
1502 /* {{{ 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)1503 PHP_METHOD(PDOStatement, bindParam)
1504 {
1505 	register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
1506 }
1507 /* }}} */
1508 
1509 /* {{{ 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)1510 PHP_METHOD(PDOStatement, bindColumn)
1511 {
1512 	register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
1513 }
1514 /* }}} */
1515 
1516 /* {{{ 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)1517 PHP_METHOD(PDOStatement, rowCount)
1518 {
1519 	ZEND_PARSE_PARAMETERS_NONE();
1520 
1521 	PHP_STMT_GET_OBJ;
1522 	RETURN_LONG(stmt->row_count);
1523 }
1524 /* }}} */
1525 
1526 /* {{{ Fetch the error code associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorCode)1527 PHP_METHOD(PDOStatement, errorCode)
1528 {
1529 	ZEND_PARSE_PARAMETERS_NONE();
1530 
1531 	PHP_STMT_GET_OBJ;
1532 	if (stmt->error_code[0] == '\0') {
1533 		RETURN_NULL();
1534 	}
1535 
1536 	RETURN_STRING(stmt->error_code);
1537 }
1538 /* }}} */
1539 
1540 /* {{{ Fetch extended error information associated with the last operation on the statement handle */
PHP_METHOD(PDOStatement,errorInfo)1541 PHP_METHOD(PDOStatement, errorInfo)
1542 {
1543 	int error_count;
1544 	int error_count_diff = 0;
1545 	int error_expected_count = 3;
1546 
1547 	ZEND_PARSE_PARAMETERS_NONE();
1548 
1549 	PHP_STMT_GET_OBJ;
1550 	array_init(return_value);
1551 	add_next_index_string(return_value, stmt->error_code);
1552 
1553 	if (strncmp(stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) {
1554 		if (stmt->dbh->methods->fetch_err) {
1555 			stmt->dbh->methods->fetch_err(stmt->dbh, stmt, return_value);
1556 		}
1557 	}
1558 
1559 	error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1560 
1561 	if (error_expected_count > error_count) {
1562 		int current_index;
1563 
1564 		error_count_diff = error_expected_count - error_count;
1565 		for (current_index = 0; current_index < error_count_diff; current_index++) {
1566 			add_next_index_null(return_value);
1567 		}
1568 	}
1569 }
1570 /* }}} */
1571 
1572 /* {{{ Set an attribute */
PHP_METHOD(PDOStatement,setAttribute)1573 PHP_METHOD(PDOStatement, setAttribute)
1574 {
1575 	zend_long attr;
1576 	zval *value = NULL;
1577 
1578 	ZEND_PARSE_PARAMETERS_START(2, 2)
1579 		Z_PARAM_LONG(attr)
1580 		Z_PARAM_ZVAL_OR_NULL(value)
1581 	ZEND_PARSE_PARAMETERS_END();
1582 
1583 	PHP_STMT_GET_OBJ;
1584 
1585 	/* Driver hasn't registered a function for setting attributes */
1586 	if (!stmt->methods->set_attribute) {
1587 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "This driver doesn't support setting attributes");
1588 		RETURN_FALSE;
1589 	}
1590 
1591 	PDO_STMT_CLEAR_ERR();
1592 	if (stmt->methods->set_attribute(stmt, attr, value)) {
1593 		RETURN_TRUE;
1594 	}
1595 
1596 	/* Error while setting attribute */
1597 	PDO_HANDLE_STMT_ERR();
1598 	RETURN_FALSE;
1599 }
1600 /* }}} */
1601 
1602 /* {{{ Get an attribute */
1603 
generic_stmt_attr_get(pdo_stmt_t * stmt,zval * return_value,zend_long attr)1604 static bool generic_stmt_attr_get(pdo_stmt_t *stmt, zval *return_value, zend_long attr)
1605 {
1606 	switch (attr) {
1607 		case PDO_ATTR_EMULATE_PREPARES:
1608 			RETVAL_BOOL(stmt->supports_placeholders == PDO_PLACEHOLDER_NONE);
1609 			return 1;
1610 	}
1611 	return 0;
1612 }
1613 
PHP_METHOD(PDOStatement,getAttribute)1614 PHP_METHOD(PDOStatement, getAttribute)
1615 {
1616 	zend_long attr;
1617 
1618 	ZEND_PARSE_PARAMETERS_START(1, 1)
1619 		Z_PARAM_LONG(attr)
1620 	ZEND_PARSE_PARAMETERS_END();
1621 
1622 	PHP_STMT_GET_OBJ;
1623 	if (!stmt->methods->get_attribute) {
1624 		if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1625 			pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1626 				"This driver doesn't support getting attributes");
1627 			RETURN_FALSE;
1628 		}
1629 		return;
1630 	}
1631 
1632 	PDO_STMT_CLEAR_ERR();
1633 	switch (stmt->methods->get_attribute(stmt, attr, return_value)) {
1634 		case -1:
1635 			PDO_HANDLE_STMT_ERR();
1636 			RETURN_FALSE;
1637 
1638 		case 0:
1639 			if (!generic_stmt_attr_get(stmt, return_value, attr)) {
1640 				/* XXX: should do something better here */
1641 				pdo_raise_impl_error(stmt->dbh, stmt, "IM001",
1642 					"driver doesn't support getting that attribute");
1643 				RETURN_FALSE;
1644 			}
1645 			return;
1646 
1647 		default:
1648 			return;
1649 	}
1650 }
1651 /* }}} */
1652 
1653 /* {{{ Returns the number of columns in the result set */
PHP_METHOD(PDOStatement,columnCount)1654 PHP_METHOD(PDOStatement, columnCount)
1655 {
1656 	ZEND_PARSE_PARAMETERS_NONE();
1657 
1658 	PHP_STMT_GET_OBJ;
1659 	RETURN_LONG(stmt->column_count);
1660 }
1661 /* }}} */
1662 
1663 /* {{{ Returns meta data for a numbered column */
PHP_METHOD(PDOStatement,getColumnMeta)1664 PHP_METHOD(PDOStatement, getColumnMeta)
1665 {
1666 	zend_long colno;
1667 	struct pdo_column_data *col;
1668 
1669 	ZEND_PARSE_PARAMETERS_START(1, 1)
1670 		Z_PARAM_LONG(colno)
1671 	ZEND_PARSE_PARAMETERS_END();
1672 
1673 	PHP_STMT_GET_OBJ;
1674 	if (colno < 0) {
1675 		zend_argument_value_error(1, "must be greater than or equal to 0");
1676 		RETURN_THROWS();
1677 	}
1678 
1679 	if (!stmt->methods->get_column_meta) {
1680 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver doesn't support meta data");
1681 		RETURN_FALSE;
1682 	}
1683 
1684 	PDO_STMT_CLEAR_ERR();
1685 	if (FAILURE == stmt->methods->get_column_meta(stmt, colno, return_value)) {
1686 		PDO_HANDLE_STMT_ERR();
1687 		RETURN_FALSE;
1688 	}
1689 
1690 	/* add stock items */
1691 	col = &stmt->columns[colno];
1692 	add_assoc_str(return_value, "name", zend_string_copy(col->name));
1693 	add_assoc_long(return_value, "len", col->maxlen); /* FIXME: unsigned ? */
1694 	add_assoc_long(return_value, "precision", col->precision);
1695 }
1696 /* }}} */
1697 
1698 /* {{{ Changes the default fetch mode for subsequent fetches (params have different meaning for different fetch modes) */
1699 
pdo_stmt_setup_fetch_mode(pdo_stmt_t * stmt,zend_long mode,uint32_t mode_arg_num,zval * args,uint32_t variadic_num_args)1700 bool pdo_stmt_setup_fetch_mode(pdo_stmt_t *stmt, zend_long mode, uint32_t mode_arg_num,
1701 	zval *args, uint32_t variadic_num_args)
1702 {
1703 	int flags = 0;
1704 	uint32_t arg1_arg_num = mode_arg_num + 1;
1705 	uint32_t constructor_arg_num = mode_arg_num + 2;
1706 	uint32_t total_num_args = mode_arg_num + variadic_num_args;
1707 
1708 	switch (stmt->default_fetch_type) {
1709 		case PDO_FETCH_INTO:
1710 			if (!Z_ISUNDEF(stmt->fetch.into)) {
1711 				zval_ptr_dtor(&stmt->fetch.into);
1712 				ZVAL_UNDEF(&stmt->fetch.into);
1713 			}
1714 			break;
1715 		default:
1716 			;
1717 	}
1718 
1719 	stmt->default_fetch_type = PDO_FETCH_BOTH;
1720 
1721 	flags = mode & PDO_FETCH_FLAGS;
1722 
1723 	if (!pdo_stmt_verify_mode(stmt, mode, mode_arg_num, false)) {
1724 		return false;
1725 	}
1726 
1727 	switch (mode & ~PDO_FETCH_FLAGS) {
1728 		case PDO_FETCH_USE_DEFAULT:
1729 		case PDO_FETCH_LAZY:
1730 		case PDO_FETCH_ASSOC:
1731 		case PDO_FETCH_NUM:
1732 		case PDO_FETCH_BOTH:
1733 		case PDO_FETCH_OBJ:
1734 		case PDO_FETCH_BOUND:
1735 		case PDO_FETCH_NAMED:
1736 		case PDO_FETCH_KEY_PAIR:
1737 			if (variadic_num_args != 0) {
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), mode_arg_num, total_num_args);
1741 				zend_string_release(func);
1742 				return false;
1743 			}
1744 			break;
1745 
1746 		case PDO_FETCH_COLUMN:
1747 			if (variadic_num_args != 1) {
1748 				zend_string *func = get_active_function_or_method_name();
1749 				zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1750 					ZSTR_VAL(func), arg1_arg_num, total_num_args);
1751 				zend_string_release(func);
1752 				return false;
1753 			}
1754 			if (Z_TYPE(args[0]) != IS_LONG) {
1755 				zend_argument_type_error(arg1_arg_num, "must be of type int, %s given", zend_zval_value_name(&args[0]));
1756 				return false;
1757 			}
1758 			if (Z_LVAL(args[0]) < 0) {
1759 				zend_argument_value_error(arg1_arg_num, "must be greater than or equal to 0");
1760 				return false;
1761 			}
1762 			stmt->fetch.column = Z_LVAL(args[0]);
1763 			break;
1764 
1765 		case PDO_FETCH_CLASS: {
1766 			HashTable *constructor_args = NULL;
1767 			/* Undef constructor arguments */
1768 			ZVAL_UNDEF(&stmt->fetch.cls.ctor_args);
1769 			/* Gets its class name from 1st column */
1770 			if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
1771 				if (variadic_num_args != 0) {
1772 					zend_string *func = get_active_function_or_method_name();
1773 					zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1774 						ZSTR_VAL(func), mode_arg_num, total_num_args);
1775 					zend_string_release(func);
1776 					return false;
1777 				}
1778 				stmt->fetch.cls.ce = NULL;
1779 			} else {
1780 				zend_class_entry *cep;
1781 				if (variadic_num_args == 0) {
1782 					zend_string *func = get_active_function_or_method_name();
1783 					zend_argument_count_error("%s() expects at least %d arguments for the fetch mode provided, %d given",
1784 						ZSTR_VAL(func), arg1_arg_num, total_num_args);
1785 					zend_string_release(func);
1786 					return false;
1787 				}
1788 				/* constructor_arguments can be null/not passed */
1789 				if (variadic_num_args > 2) {
1790 					zend_string *func = get_active_function_or_method_name();
1791 					zend_argument_count_error("%s() expects at most %d arguments for the fetch mode provided, %d given",
1792 						ZSTR_VAL(func), constructor_arg_num, total_num_args);
1793 					zend_string_release(func);
1794 					return false;
1795 				}
1796 				if (Z_TYPE(args[0]) != IS_STRING) {
1797 					zend_argument_type_error(arg1_arg_num, "must be of type string, %s given", zend_zval_value_name(&args[0]));
1798 					return false;
1799 				}
1800 				cep = zend_lookup_class(Z_STR(args[0]));
1801 				if (!cep) {
1802 					zend_argument_type_error(arg1_arg_num, "must be a valid class");
1803 					return false;
1804 				}
1805 				/* Verify constructor_args (args[1]) is ?array */
1806 				/* TODO: Improve logic? */
1807 				if (variadic_num_args == 2) {
1808 					if (Z_TYPE(args[1]) != IS_NULL && Z_TYPE(args[1]) != IS_ARRAY) {
1809 						zend_argument_type_error(constructor_arg_num, "must be of type ?array, %s given",
1810 							zend_zval_value_name(&args[1]));
1811 						return false;
1812 					}
1813 					if (Z_TYPE(args[1]) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL(args[1]))) {
1814 						constructor_args = Z_ARRVAL(args[1]);
1815 					}
1816 				}
1817 				stmt->fetch.cls.ce = cep;
1818 
1819 				/* If constructor arguments are present and not empty */
1820 				if (constructor_args) {
1821 					ZVAL_ARR(&stmt->fetch.cls.ctor_args, zend_array_dup(constructor_args));
1822 				}
1823 			}
1824 
1825 			do_fetch_class_prepare(stmt);
1826 			break;
1827 		}
1828 		case PDO_FETCH_INTO:
1829 			if (total_num_args != arg1_arg_num) {
1830 				zend_string *func = get_active_function_or_method_name();
1831 				zend_argument_count_error("%s() expects exactly %d arguments for the fetch mode provided, %d given",
1832 					ZSTR_VAL(func), arg1_arg_num, total_num_args);
1833 				zend_string_release(func);
1834 				return false;
1835 			}
1836 			if (Z_TYPE(args[0]) != IS_OBJECT) {
1837 				zend_argument_type_error(arg1_arg_num, "must be of type object, %s given", zend_zval_value_name(&args[0]));
1838 				return false;
1839 			}
1840 
1841 			ZVAL_COPY(&stmt->fetch.into, &args[0]);
1842 			break;
1843 		default:
1844 			zend_argument_value_error(mode_arg_num, "must be one of the PDO::FETCH_* constants");
1845 			return false;
1846 	}
1847 
1848 	stmt->default_fetch_type = mode;
1849 
1850 	return true;
1851 }
1852 
PHP_METHOD(PDOStatement,setFetchMode)1853 PHP_METHOD(PDOStatement, setFetchMode)
1854 {
1855 	zend_long fetch_mode;
1856 	zval *args = NULL;
1857 	uint32_t num_args = 0;
1858 
1859 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "l*", &fetch_mode, &args, &num_args) == FAILURE) {
1860 		RETURN_THROWS();
1861 	}
1862 
1863 	PHP_STMT_GET_OBJ;
1864 
1865 	do_fetch_opt_finish(stmt, 1);
1866 
1867 	if (!pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 1, args, num_args)) {
1868 		RETURN_THROWS();
1869 	}
1870 
1871 	// TODO Void return?
1872 	RETURN_TRUE;
1873 }
1874 /* }}} */
1875 
1876 /* {{{ Advances to the next rowset in a multi-rowset statement handle. Returns true if it succeeded, false otherwise */
1877 
pdo_stmt_do_next_rowset(pdo_stmt_t * stmt)1878 static bool pdo_stmt_do_next_rowset(pdo_stmt_t *stmt)
1879 {
1880 	pdo_stmt_reset_columns(stmt);
1881 
1882 	if (!stmt->methods->next_rowset(stmt)) {
1883 		/* Set the executed flag to 0 to reallocate columns on next execute */
1884 		stmt->executed = 0;
1885 		return 0;
1886 	}
1887 
1888 	pdo_stmt_describe_columns(stmt);
1889 
1890 	return 1;
1891 }
1892 
PHP_METHOD(PDOStatement,nextRowset)1893 PHP_METHOD(PDOStatement, nextRowset)
1894 {
1895 	ZEND_PARSE_PARAMETERS_NONE();
1896 
1897 	PHP_STMT_GET_OBJ;
1898 	if (!stmt->methods->next_rowset) {
1899 		pdo_raise_impl_error(stmt->dbh, stmt, "IM001", "driver does not support multiple rowsets");
1900 		RETURN_FALSE;
1901 	}
1902 
1903 	PDO_STMT_CLEAR_ERR();
1904 
1905 	if (!pdo_stmt_do_next_rowset(stmt)) {
1906 		PDO_HANDLE_STMT_ERR();
1907 		RETURN_FALSE;
1908 	}
1909 
1910 	RETURN_TRUE;
1911 }
1912 /* }}} */
1913 
1914 /* {{{ Closes the cursor, leaving the statement ready for re-execution. */
PHP_METHOD(PDOStatement,closeCursor)1915 PHP_METHOD(PDOStatement, closeCursor)
1916 {
1917 	ZEND_PARSE_PARAMETERS_NONE();
1918 
1919 	PHP_STMT_GET_OBJ;
1920 	if (!stmt->methods->cursor_closer) {
1921 		/* emulate it by fetching and discarding rows */
1922 		do {
1923 			while (stmt->methods->fetcher(stmt, PDO_FETCH_ORI_NEXT, 0))
1924 				;
1925 			if (!stmt->methods->next_rowset) {
1926 				break;
1927 			}
1928 
1929 			if (!pdo_stmt_do_next_rowset(stmt)) {
1930 				break;
1931 			}
1932 
1933 		} while (1);
1934 		stmt->executed = 0;
1935 		RETURN_TRUE;
1936 	}
1937 
1938 	PDO_STMT_CLEAR_ERR();
1939 
1940 	if (!stmt->methods->cursor_closer(stmt)) {
1941 		PDO_HANDLE_STMT_ERR();
1942 		RETURN_FALSE;
1943 	}
1944 	stmt->executed = 0;
1945 	RETURN_TRUE;
1946 }
1947 /* }}} */
1948 
1949 /* {{{ A utility for internals hackers to debug parameter internals */
PHP_METHOD(PDOStatement,debugDumpParams)1950 PHP_METHOD(PDOStatement, debugDumpParams)
1951 {
1952 	ZEND_PARSE_PARAMETERS_NONE();
1953 
1954 	php_stream *out = php_stream_open_wrapper("php://output", "w", 0, NULL);
1955 	struct pdo_bound_param_data *param;
1956 
1957 	ZEND_PARSE_PARAMETERS_NONE();
1958 
1959 	PHP_STMT_GET_OBJ;
1960 
1961 	if (out == NULL) {
1962 		RETURN_FALSE;
1963 	}
1964 
1965 	/* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1966 	php_stream_printf(out, "SQL: [%zd] ", ZSTR_LEN(stmt->query_string));
1967 	php_stream_write(out, ZSTR_VAL(stmt->query_string), ZSTR_LEN(stmt->query_string));
1968 	php_stream_write(out, "\n", 1);
1969 
1970 	/* show parsed SQL if emulated prepares enabled */
1971 	/* pointers will be equal if PDO::query() was invoked */
1972 	if (stmt->active_query_string != NULL && stmt->active_query_string != stmt->query_string) {
1973 		/* break into multiple operations so query string won't be truncated at FORMAT_CONV_MAX_PRECISION */
1974 		php_stream_printf(out, "Sent SQL: [%zd] ", ZSTR_LEN(stmt->active_query_string));
1975 		php_stream_write(out, ZSTR_VAL(stmt->active_query_string), ZSTR_LEN(stmt->active_query_string));
1976 		php_stream_write(out, "\n", 1);
1977 	}
1978 
1979 	php_stream_printf(out, "Params:  %d\n",
1980 		stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0);
1981 
1982 	if (stmt->bound_params) {
1983 		zend_ulong num;
1984 		zend_string *key = NULL;
1985 		ZEND_HASH_FOREACH_KEY_PTR(stmt->bound_params, num, key, param) {
1986 			if (key) {
1987 				php_stream_printf(out, "Key: Name: [%zd] %.*s\n",
1988 					ZSTR_LEN(key), (int) ZSTR_LEN(key), ZSTR_VAL(key));
1989 			} else {
1990 				php_stream_printf(out, "Key: Position #" ZEND_ULONG_FMT ":\n", num);
1991 			}
1992 
1993 			php_stream_printf(out,
1994 				"paramno=" ZEND_LONG_FMT "\n"
1995 				"name=[%zd] \"%.*s\"\n"
1996 				"is_param=%d\n"
1997 				"param_type=%d\n",
1998 				param->paramno, param->name ? ZSTR_LEN(param->name) : 0, param->name ? (int) ZSTR_LEN(param->name) : 0,
1999 				param->name ? ZSTR_VAL(param->name) : "",
2000 				param->is_param,
2001 				param->param_type);
2002 
2003 		} ZEND_HASH_FOREACH_END();
2004 	}
2005 
2006 	php_stream_close(out);
2007 }
2008 /* }}} */
2009 
PHP_METHOD(PDOStatement,getIterator)2010 PHP_METHOD(PDOStatement, getIterator)
2011 {
2012 	if (zend_parse_parameters_none() == FAILURE) {
2013 		return;
2014 	}
2015 
2016 	zend_create_internal_iterator_zval(return_value, ZEND_THIS);
2017 }
2018 
2019 /* {{{ overloaded handlers for PDOStatement class */
dbstmt_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2020 static zval *dbstmt_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2021 {
2022 	if (zend_string_equals_literal(name, "queryString")) {
2023 		zval *query_string = OBJ_PROP_NUM(object, 0);
2024 		if (!Z_ISUNDEF_P(query_string)) {
2025 			zend_throw_error(NULL, "Property queryString is read only");
2026 			return value;
2027 		}
2028 	}
2029 	return zend_std_write_property(object, name, value, cache_slot);
2030 }
2031 
dbstmt_prop_delete(zend_object * object,zend_string * name,void ** cache_slot)2032 static void dbstmt_prop_delete(zend_object *object, zend_string *name, void **cache_slot)
2033 {
2034 	if (zend_string_equals_literal(name, "queryString")) {
2035 		zend_throw_error(NULL, "Property queryString is read only");
2036 	} else {
2037 		zend_std_unset_property(object, name, cache_slot);
2038 	}
2039 }
2040 
dbstmt_method_get(zend_object ** object_pp,zend_string * method_name,const zval * key)2041 static zend_function *dbstmt_method_get(zend_object **object_pp, zend_string *method_name, const zval *key)
2042 {
2043 	zend_function *fbc = NULL;
2044 	zend_string *lc_method_name;
2045 	zend_object *object = *object_pp;
2046 
2047 	lc_method_name = zend_string_tolower(method_name);
2048 
2049 	if ((fbc = zend_hash_find_ptr(&object->ce->function_table, lc_method_name)) == NULL) {
2050 		pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(object);
2051 		/* instance not created by PDO object */
2052 		if (!stmt->dbh) {
2053 			goto out;
2054 		}
2055 		/* not a pre-defined method, nor a user-defined method; check
2056 		 * the driver specific methods */
2057 		if (!stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2058 			if (!pdo_hash_methods(Z_PDO_OBJECT_P(&stmt->database_object_handle),
2059 				PDO_DBH_DRIVER_METHOD_KIND_STMT)
2060 				|| !stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT]) {
2061 				goto out;
2062 			}
2063 		}
2064 
2065 		if ((fbc = zend_hash_find_ptr(stmt->dbh->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_STMT], lc_method_name)) == NULL) {
2066 			goto out;
2067 		}
2068 		/* got it */
2069 	}
2070 
2071 out:
2072 	zend_string_release_ex(lc_method_name, 0);
2073 	if (!fbc) {
2074 		fbc = zend_std_get_method(object_pp, method_name, key);
2075 	}
2076 	return fbc;
2077 }
2078 
2079 zend_object_handlers pdo_dbstmt_object_handlers;
2080 zend_object_handlers pdo_row_object_handlers;
2081 
php_pdo_free_statement(pdo_stmt_t * stmt)2082 PDO_API void php_pdo_free_statement(pdo_stmt_t *stmt)
2083 {
2084 	if (stmt->bound_params) {
2085 		zend_hash_destroy(stmt->bound_params);
2086 		FREE_HASHTABLE(stmt->bound_params);
2087 		stmt->bound_params = NULL;
2088 	}
2089 	if (stmt->bound_param_map) {
2090 		zend_hash_destroy(stmt->bound_param_map);
2091 		FREE_HASHTABLE(stmt->bound_param_map);
2092 		stmt->bound_param_map = NULL;
2093 	}
2094 	if (stmt->bound_columns) {
2095 		zend_hash_destroy(stmt->bound_columns);
2096 		FREE_HASHTABLE(stmt->bound_columns);
2097 		stmt->bound_columns = NULL;
2098 	}
2099 
2100 	if (stmt->methods && stmt->methods->dtor) {
2101 		stmt->methods->dtor(stmt);
2102 	}
2103 	if (stmt->active_query_string) {
2104 		zend_string_release(stmt->active_query_string);
2105 	}
2106 	if (stmt->query_string) {
2107 		zend_string_release(stmt->query_string);
2108 	}
2109 
2110 	pdo_stmt_reset_columns(stmt);
2111 
2112 	if (!Z_ISUNDEF(stmt->fetch.into) && stmt->default_fetch_type == PDO_FETCH_INTO) {
2113 		zval_ptr_dtor(&stmt->fetch.into);
2114 		ZVAL_UNDEF(&stmt->fetch.into);
2115 	}
2116 
2117 	do_fetch_opt_finish(stmt, 1);
2118 
2119 	if (!Z_ISUNDEF(stmt->database_object_handle)) {
2120 		zval_ptr_dtor(&stmt->database_object_handle);
2121 	}
2122 	zend_object_std_dtor(&stmt->std);
2123 }
2124 
pdo_dbstmt_free_storage(zend_object * std)2125 void pdo_dbstmt_free_storage(zend_object *std)
2126 {
2127 	pdo_stmt_t *stmt = php_pdo_stmt_fetch_object(std);
2128 	php_pdo_free_statement(stmt);
2129 }
2130 
pdo_dbstmt_new(zend_class_entry * ce)2131 zend_object *pdo_dbstmt_new(zend_class_entry *ce)
2132 {
2133 	pdo_stmt_t *stmt;
2134 
2135 	stmt = zend_object_alloc(sizeof(pdo_stmt_t), ce);
2136 	zend_object_std_init(&stmt->std, ce);
2137 	object_properties_init(&stmt->std, ce);
2138 
2139 	return &stmt->std;
2140 }
2141 /* }}} */
2142 
2143 /* {{{ statement iterator */
2144 
2145 struct php_pdo_iterator {
2146 	zend_object_iterator iter;
2147 	zend_ulong key;
2148 	zval fetch_ahead;
2149 };
2150 
pdo_stmt_iter_dtor(zend_object_iterator * iter)2151 static void pdo_stmt_iter_dtor(zend_object_iterator *iter)
2152 {
2153 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2154 
2155 	zval_ptr_dtor(&I->iter.data);
2156 
2157 	if (!Z_ISUNDEF(I->fetch_ahead)) {
2158 		zval_ptr_dtor(&I->fetch_ahead);
2159 	}
2160 }
2161 
pdo_stmt_iter_valid(zend_object_iterator * iter)2162 static zend_result pdo_stmt_iter_valid(zend_object_iterator *iter)
2163 {
2164 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2165 
2166 	return Z_ISUNDEF(I->fetch_ahead) ? FAILURE : SUCCESS;
2167 }
2168 
pdo_stmt_iter_get_data(zend_object_iterator * iter)2169 static zval *pdo_stmt_iter_get_data(zend_object_iterator *iter)
2170 {
2171 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2172 
2173 	/* sanity */
2174 	if (Z_ISUNDEF(I->fetch_ahead)) {
2175 		return NULL;
2176 	}
2177 
2178 	return &I->fetch_ahead;
2179 }
2180 
pdo_stmt_iter_get_key(zend_object_iterator * iter,zval * key)2181 static void pdo_stmt_iter_get_key(zend_object_iterator *iter, zval *key)
2182 {
2183 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2184 
2185 	if (I->key == (zend_ulong)-1) {
2186 		ZVAL_NULL(key);
2187 	} else {
2188 		ZVAL_LONG(key, I->key);
2189 	}
2190 }
2191 
pdo_stmt_iter_move_forwards(zend_object_iterator * iter)2192 static void pdo_stmt_iter_move_forwards(zend_object_iterator *iter)
2193 {
2194 	struct php_pdo_iterator *I = (struct php_pdo_iterator*)iter;
2195 	pdo_stmt_t *stmt = Z_PDO_STMT_P(&I->iter.data); /* for PDO_HANDLE_STMT_ERR() */
2196 
2197 	if (!Z_ISUNDEF(I->fetch_ahead)) {
2198 		zval_ptr_dtor(&I->fetch_ahead);
2199 	}
2200 
2201 	if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2202 			PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2203 
2204 		PDO_HANDLE_STMT_ERR();
2205 		I->key = (zend_ulong)-1;
2206 		ZVAL_UNDEF(&I->fetch_ahead);
2207 
2208 		return;
2209 	}
2210 
2211 	I->key++;
2212 }
2213 
2214 static const zend_object_iterator_funcs pdo_stmt_iter_funcs = {
2215 	pdo_stmt_iter_dtor,
2216 	pdo_stmt_iter_valid,
2217 	pdo_stmt_iter_get_data,
2218 	pdo_stmt_iter_get_key,
2219 	pdo_stmt_iter_move_forwards,
2220 	NULL,
2221 	NULL,
2222 	NULL, /* get_gc */
2223 };
2224 
pdo_stmt_iter_get(zend_class_entry * ce,zval * object,int by_ref)2225 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object, int by_ref)
2226 {
2227 	if (by_ref) {
2228 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2229 		return NULL;
2230 	}
2231 
2232 	pdo_stmt_t *stmt = Z_PDO_STMT_P(object);
2233 	if (!stmt->dbh) {
2234 		zend_throw_error(NULL, "%s object is uninitialized", ZSTR_VAL(ce->name));
2235 		return NULL;
2236 	}
2237 
2238 	struct php_pdo_iterator *I = ecalloc(1, sizeof(struct php_pdo_iterator));
2239 	zend_iterator_init(&I->iter);
2240 	I->iter.funcs = &pdo_stmt_iter_funcs;
2241 	Z_ADDREF_P(object);
2242 	ZVAL_OBJ(&I->iter.data, Z_OBJ_P(object));
2243 
2244 	if (!do_fetch(stmt, &I->fetch_ahead, PDO_FETCH_USE_DEFAULT,
2245 			PDO_FETCH_ORI_NEXT, /* offset */ 0, NULL)) {
2246 		PDO_HANDLE_STMT_ERR();
2247 		I->key = (zend_ulong)-1;
2248 		ZVAL_UNDEF(&I->fetch_ahead);
2249 	}
2250 
2251 	return &I->iter;
2252 }
2253 
2254 /* }}} */
2255 
2256 /* {{{ overloaded handlers for PDORow class (used by PDO_FETCH_LAZY) */
2257 
row_prop_read(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)2258 static zval *row_prop_read(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
2259 {
2260 	pdo_row_t *row = (pdo_row_t *)object;
2261 	pdo_stmt_t *stmt = row->stmt;
2262 	int colno = -1;
2263 	zend_long lval;
2264 	ZEND_ASSERT(stmt);
2265 
2266 	ZVAL_NULL(rv);
2267 	if (zend_string_equals_literal(name, "queryString")) {
2268 		return zend_std_read_property(&stmt->std, name, type, cache_slot, rv);
2269 	} else if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG) {
2270 		if (lval >= 0 && lval < stmt->column_count) {
2271 			fetch_value(stmt, rv, lval, NULL);
2272 		}
2273 	} else {
2274 		/* TODO: replace this with a hash of available column names to column
2275 		 * numbers */
2276 		for (colno = 0; colno < stmt->column_count; colno++) {
2277 			if (zend_string_equals(stmt->columns[colno].name, name)) {
2278 				fetch_value(stmt, rv, colno, NULL);
2279 				return rv;
2280 			}
2281 		}
2282 	}
2283 
2284 	return rv;
2285 }
2286 
row_dim_read(zend_object * object,zval * member,int type,zval * rv)2287 static zval *row_dim_read(zend_object *object, zval *member, int type, zval *rv)
2288 {
2289 	pdo_row_t *row = (pdo_row_t *)object;
2290 	pdo_stmt_t *stmt = row->stmt;
2291 	int colno = -1;
2292 	zend_long lval;
2293 	ZEND_ASSERT(stmt);
2294 
2295 	ZVAL_NULL(rv);
2296 	if (Z_TYPE_P(member) == IS_LONG) {
2297 		if (Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count) {
2298 			fetch_value(stmt, rv, Z_LVAL_P(member), NULL);
2299 		}
2300 	} else if (Z_TYPE_P(member) == IS_STRING
2301 		   && is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG)	{
2302 		if (lval >= 0 && lval < stmt->column_count) {
2303 			fetch_value(stmt, rv, lval, NULL);
2304 		}
2305 	} else {
2306 		if (!try_convert_to_string(member)) {
2307 			return &EG(uninitialized_zval);
2308 		}
2309 
2310 		if (zend_string_equals_literal(Z_STR_P(member), "queryString")) {
2311 			return zend_std_read_property(&stmt->std, Z_STR_P(member), type, NULL, rv);
2312 		}
2313 
2314 		/* TODO: replace this with a hash of available column names to column
2315 		 * numbers */
2316 		for (colno = 0; colno < stmt->column_count; colno++) {
2317 			if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2318 				fetch_value(stmt, rv, colno, NULL);
2319 				return rv;
2320 			}
2321 		}
2322 	}
2323 
2324 	return rv;
2325 }
2326 
row_prop_write(zend_object * object,zend_string * name,zval * value,void ** cache_slot)2327 static zval *row_prop_write(zend_object *object, zend_string *name, zval *value, void **cache_slot)
2328 {
2329 	zend_throw_error(NULL, "Cannot write to PDORow property");
2330 	return value;
2331 }
2332 
row_dim_write(zend_object * object,zval * member,zval * value)2333 static void row_dim_write(zend_object *object, zval *member, zval *value)
2334 {
2335 	zend_throw_error(NULL, "Cannot write to PDORow offset");
2336 }
2337 
row_prop_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)2338 static int row_prop_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
2339 {
2340 	pdo_row_t *row = (pdo_row_t *)object;
2341 	pdo_stmt_t *stmt = row->stmt;
2342 	int colno = -1;
2343 	zend_long lval;
2344 	ZEND_ASSERT(stmt);
2345 
2346 	if (is_numeric_string(ZSTR_VAL(name), ZSTR_LEN(name), &lval, NULL, 0) == IS_LONG)	{
2347 		return lval >=0 && lval < stmt->column_count;
2348 	}
2349 
2350 	/* TODO: replace this with a hash of available column names to column
2351 	 * numbers */
2352 	for (colno = 0; colno < stmt->column_count; colno++) {
2353 		if (zend_string_equals(stmt->columns[colno].name, name)) {
2354 			int res;
2355 			zval val;
2356 
2357 			fetch_value(stmt, &val, colno, NULL);
2358 			res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2359 			zval_ptr_dtor_nogc(&val);
2360 
2361 			return res;
2362 		}
2363 	}
2364 
2365 	return 0;
2366 }
2367 
row_dim_exists(zend_object * object,zval * member,int check_empty)2368 static int row_dim_exists(zend_object *object, zval *member, int check_empty)
2369 {
2370 	pdo_row_t *row = (pdo_row_t *)object;
2371 	pdo_stmt_t *stmt = row->stmt;
2372 	int colno = -1;
2373 	zend_long lval;
2374 	ZEND_ASSERT(stmt);
2375 
2376 	if (Z_TYPE_P(member) == IS_LONG) {
2377 		return Z_LVAL_P(member) >= 0 && Z_LVAL_P(member) < stmt->column_count;
2378 	} else if (Z_TYPE_P(member) == IS_STRING) {
2379 		if (is_numeric_string(Z_STRVAL_P(member), Z_STRLEN_P(member), &lval, NULL, 0) == IS_LONG)	{
2380 			return lval >=0 && lval < stmt->column_count;
2381 		}
2382 	} else {
2383 		if (!try_convert_to_string(member)) {
2384 			return 0;
2385 		}
2386 	}
2387 
2388 	/* TODO: replace this with a hash of available column names to column
2389 	 * numbers */
2390 	for (colno = 0; colno < stmt->column_count; colno++) {
2391 		if (zend_string_equals(stmt->columns[colno].name, Z_STR_P(member))) {
2392 			int res;
2393 			zval val;
2394 
2395 			fetch_value(stmt, &val, colno, NULL);
2396 			res = check_empty ? i_zend_is_true(&val) : Z_TYPE(val) != IS_NULL;
2397 			zval_ptr_dtor_nogc(&val);
2398 
2399 			return res;
2400 		}
2401 	}
2402 
2403 	return 0;
2404 }
2405 
row_prop_delete(zend_object * object,zend_string * offset,void ** cache_slot)2406 static void row_prop_delete(zend_object *object, zend_string *offset, void **cache_slot)
2407 {
2408 	zend_throw_error(NULL, "Cannot unset PDORow property");
2409 }
2410 
row_dim_delete(zend_object * object,zval * offset)2411 static void row_dim_delete(zend_object *object, zval *offset)
2412 {
2413 	zend_throw_error(NULL, "Cannot unset PDORow offset");
2414 }
2415 
row_get_properties_for(zend_object * object,zend_prop_purpose purpose)2416 static HashTable *row_get_properties_for(zend_object *object, zend_prop_purpose purpose)
2417 {
2418 	pdo_row_t *row = (pdo_row_t *)object;
2419 	pdo_stmt_t *stmt = row->stmt;
2420 	HashTable *props;
2421 	int i;
2422 	ZEND_ASSERT(stmt);
2423 
2424 	if (purpose != ZEND_PROP_PURPOSE_DEBUG) {
2425 		return zend_std_get_properties_for(object, purpose);
2426 	}
2427 
2428 	if (!stmt->std.properties) {
2429 		rebuild_object_properties(&stmt->std);
2430 	}
2431 	props = zend_array_dup(stmt->std.properties);
2432 	for (i = 0; i < stmt->column_count; i++) {
2433 		if (zend_string_equals_literal(stmt->columns[i].name, "queryString")) {
2434 			continue;
2435 		}
2436 
2437 		zval val;
2438 		fetch_value(stmt, &val, i, NULL);
2439 
2440 		zend_hash_update(props, stmt->columns[i].name, &val);
2441 	}
2442 	return props;
2443 }
2444 
row_get_ctor(zend_object * object)2445 static zend_function *row_get_ctor(zend_object *object)
2446 {
2447 	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You may not create a PDORow manually");
2448 	return NULL;
2449 }
2450 
pdo_row_free_storage(zend_object * std)2451 void pdo_row_free_storage(zend_object *std)
2452 {
2453 	pdo_row_t *row = (pdo_row_t *)std;
2454 	if (row->stmt) {
2455 		ZVAL_UNDEF(&row->stmt->lazy_object_ref);
2456 		OBJ_RELEASE(&row->stmt->std);
2457 	}
2458 }
2459 
pdo_row_new(zend_class_entry * ce)2460 zend_object *pdo_row_new(zend_class_entry *ce)
2461 {
2462 	pdo_row_t *row = ecalloc(1, sizeof(pdo_row_t));
2463 	zend_object_std_init(&row->std, ce);
2464 
2465 	return &row->std;
2466 }
2467 
pdo_stmt_init(void)2468 void pdo_stmt_init(void)
2469 {
2470 	pdo_dbstmt_ce = register_class_PDOStatement(zend_ce_aggregate);
2471 	pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
2472 	pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
2473 	pdo_dbstmt_ce->default_object_handlers = &pdo_dbstmt_object_handlers;
2474 
2475 	memcpy(&pdo_dbstmt_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2476 	pdo_dbstmt_object_handlers.offset = XtOffsetOf(pdo_stmt_t, std);
2477 	pdo_dbstmt_object_handlers.free_obj = pdo_dbstmt_free_storage;
2478 	pdo_dbstmt_object_handlers.write_property = dbstmt_prop_write;
2479 	pdo_dbstmt_object_handlers.unset_property = dbstmt_prop_delete;
2480 	pdo_dbstmt_object_handlers.get_method = dbstmt_method_get;
2481 	pdo_dbstmt_object_handlers.compare = zend_objects_not_comparable;
2482 	pdo_dbstmt_object_handlers.clone_obj = NULL;
2483 
2484 	pdo_row_ce = register_class_PDORow();
2485 	pdo_row_ce->create_object = pdo_row_new;
2486 	pdo_row_ce->default_object_handlers = &pdo_row_object_handlers;
2487 
2488 	memcpy(&pdo_row_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2489 	pdo_row_object_handlers.free_obj = pdo_row_free_storage;
2490 	pdo_row_object_handlers.clone_obj = NULL;
2491 	pdo_row_object_handlers.get_property_ptr_ptr = NULL;
2492 	pdo_row_object_handlers.read_property = row_prop_read;
2493 	pdo_row_object_handlers.write_property = row_prop_write;
2494 	pdo_row_object_handlers.has_property = row_prop_exists;
2495 	pdo_row_object_handlers.unset_property = row_prop_delete;
2496 	pdo_row_object_handlers.read_dimension = row_dim_read;
2497 	pdo_row_object_handlers.write_dimension = row_dim_write;
2498 	pdo_row_object_handlers.has_dimension = row_dim_exists;
2499 	pdo_row_object_handlers.unset_dimension = row_dim_delete;
2500 	pdo_row_object_handlers.get_properties_for = row_get_properties_for;
2501 	pdo_row_object_handlers.get_constructor = row_get_ctor;
2502 	pdo_row_object_handlers.compare = zend_objects_not_comparable;
2503 }
2504