1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 7                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2018 The PHP Group                                |
6  +----------------------------------------------------------------------+
7  | This source file is subject to version 3.01 of the PHP license,      |
8  | that is bundled with this package in the file LICENSE, and is        |
9  | available through the world-wide-web at the following url:           |
10  | http://www.php.net/license/3_01.txt                                  |
11  | If you did not receive a copy of the PHP license and are unable to   |
12  | obtain it through the world-wide-web, please send a note to          |
13  | license@php.net so we can mail you a copy immediately.               |
14  +----------------------------------------------------------------------+
15  | Author: Wez Furlong <wez@php.net>                                    |
16  |         Marcus Boerger <helly@php.net>                               |
17  |         Sterling Hughes <sterling@php.net>                           |
18  +----------------------------------------------------------------------+
19*/
20
21/* The PDO Database Handle Class */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "php.h"
28#include "php_ini.h"
29#include "ext/standard/info.h"
30#include "php_pdo.h"
31#include "php_pdo_driver.h"
32#include "php_pdo_int.h"
33#include "zend_exceptions.h"
34#include "zend_object_handlers.h"
35#include "zend_hash.h"
36
37static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value);
38
39void pdo_throw_exception(unsigned int driver_errcode, char *driver_errmsg, pdo_error_type *pdo_error)
40{
41		zval error_info,pdo_exception;
42		char *pdo_exception_message;
43
44		object_init_ex(&pdo_exception, php_pdo_get_exception());
45		array_init(&error_info);
46
47		add_next_index_string(&error_info, *pdo_error);
48		add_next_index_long(&error_info, driver_errcode);
49		add_next_index_string(&error_info, driver_errmsg);
50
51		spprintf(&pdo_exception_message, 0,"SQLSTATE[%s] [%d] %s",*pdo_error, driver_errcode, driver_errmsg);
52		zend_update_property(php_pdo_get_exception(), &pdo_exception, "errorInfo", sizeof("errorInfo")-1, &error_info);
53		zend_update_property_long(php_pdo_get_exception(), &pdo_exception, "code", sizeof("code")-1, driver_errcode);
54		zend_update_property_string(
55			php_pdo_get_exception(),
56			&pdo_exception,
57			"message",
58			sizeof("message")-1,
59			pdo_exception_message
60		);
61		efree(pdo_exception_message);
62		zval_ptr_dtor(&error_info);
63		zend_throw_exception_object(&pdo_exception);
64}
65
66void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate, const char *supp) /* {{{ */
67{
68	pdo_error_type *pdo_err = &dbh->error_code;
69	char *message = NULL;
70	const char *msg;
71
72	if (dbh && dbh->error_mode == PDO_ERRMODE_SILENT) {
73#if 0
74		/* BUG: if user is running in silent mode and hits an error at the driver level
75		 * when they use the PDO methods to call up the error information, they may
76		 * get bogus information */
77		return;
78#endif
79	}
80
81	if (stmt) {
82		pdo_err = &stmt->error_code;
83	}
84
85	strncpy(*pdo_err, sqlstate, 6);
86
87	/* hash sqlstate to error messages */
88	msg = pdo_sqlstate_state_to_description(*pdo_err);
89	if (!msg) {
90		msg = "<<Unknown error>>";
91	}
92
93	if (supp) {
94		spprintf(&message, 0, "SQLSTATE[%s]: %s: %s", *pdo_err, msg, supp);
95	} else {
96		spprintf(&message, 0, "SQLSTATE[%s]: %s", *pdo_err, msg);
97	}
98
99	if (dbh && dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
100		php_error_docref(NULL, E_WARNING, "%s", message);
101	} else {
102		zval ex, info;
103		zend_class_entry *def_ex = php_pdo_get_exception_base(1), *pdo_ex = php_pdo_get_exception();
104
105		object_init_ex(&ex, pdo_ex);
106
107		zend_update_property_string(def_ex, &ex, "message", sizeof("message")-1, message);
108		zend_update_property_string(def_ex, &ex, "code", sizeof("code")-1, *pdo_err);
109
110		array_init(&info);
111
112		add_next_index_string(&info, *pdo_err);
113		add_next_index_long(&info, 0);
114		zend_update_property(pdo_ex, &ex, "errorInfo", sizeof("errorInfo")-1, &info);
115		zval_ptr_dtor(&info);
116
117		zend_throw_exception_object(&ex);
118	}
119
120	if (message) {
121		efree(message);
122	}
123}
124/* }}} */
125
126PDO_API void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt) /* {{{ */
127{
128	pdo_error_type *pdo_err = &dbh->error_code;
129	const char *msg = "<<Unknown>>";
130	char *supp = NULL;
131	zend_long native_code = 0;
132	zend_string *message = NULL;
133	zval info;
134
135	if (dbh == NULL || dbh->error_mode == PDO_ERRMODE_SILENT) {
136		return;
137	}
138
139	if (stmt) {
140		pdo_err = &stmt->error_code;
141	}
142
143	/* hash sqlstate to error messages */
144	msg = pdo_sqlstate_state_to_description(*pdo_err);
145	if (!msg) {
146		msg = "<<Unknown error>>";
147	}
148
149	ZVAL_UNDEF(&info);
150	if (dbh->methods->fetch_err) {
151		array_init(&info);
152
153		add_next_index_string(&info, *pdo_err);
154
155		if (dbh->methods->fetch_err(dbh, stmt, &info)) {
156			zval *item;
157
158			if ((item = zend_hash_index_find(Z_ARRVAL(info), 1)) != NULL) {
159				native_code = Z_LVAL_P(item);
160			}
161
162			if ((item = zend_hash_index_find(Z_ARRVAL(info), 2)) != NULL) {
163				supp = estrndup(Z_STRVAL_P(item), Z_STRLEN_P(item));
164			}
165		}
166	}
167
168	if (supp) {
169		message = strpprintf(0, "SQLSTATE[%s]: %s: " ZEND_LONG_FMT " %s", *pdo_err, msg, native_code, supp);
170	} else {
171		message = strpprintf(0, "SQLSTATE[%s]: %s", *pdo_err, msg);
172	}
173
174	if (dbh->error_mode == PDO_ERRMODE_WARNING) {
175		php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(message));
176	} else if (EG(exception) == NULL) {
177		zval ex;
178		zend_class_entry *def_ex = php_pdo_get_exception_base(1), *pdo_ex = php_pdo_get_exception();
179
180		object_init_ex(&ex, pdo_ex);
181
182		zend_update_property_str(def_ex, &ex, "message", sizeof("message") - 1, message);
183		zend_update_property_string(def_ex, &ex, "code", sizeof("code") - 1, *pdo_err);
184
185		if (!Z_ISUNDEF(info)) {
186			zend_update_property(pdo_ex, &ex, "errorInfo", sizeof("errorInfo") - 1, &info);
187		}
188
189		zend_throw_exception_object(&ex);
190	}
191
192	if (!Z_ISUNDEF(info)) {
193		zval_ptr_dtor(&info);
194	}
195
196	if (message) {
197		zend_string_release_ex(message, 0);
198	}
199
200	if (supp) {
201		efree(supp);
202	}
203}
204/* }}} */
205
206static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */
207{
208	php_stream *stream;
209	char *dsn = NULL;
210
211	stream = php_stream_open_wrapper(uri, "rb", REPORT_ERRORS, NULL);
212	if (stream) {
213		dsn = php_stream_get_line(stream, buf, buflen, NULL);
214		php_stream_close(stream);
215	}
216	return dsn;
217}
218/* }}} */
219
220/* {{{ proto PDO::__construct(string dsn[, string username[, string passwd [, array options]]])
221   */
222static PHP_METHOD(PDO, dbh_constructor)
223{
224	zval *object = getThis();
225	pdo_dbh_t *dbh = NULL;
226	zend_bool is_persistent = 0;
227	char *data_source;
228	size_t data_source_len;
229	char *colon;
230	char *username=NULL, *password=NULL;
231	size_t usernamelen, passwordlen;
232	pdo_driver_t *driver = NULL;
233	zval *options = NULL;
234	char alt_dsn[512];
235	int call_factory = 1;
236	zend_error_handling zeh;
237
238	ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)
239		Z_PARAM_STRING(data_source, data_source_len)
240		Z_PARAM_OPTIONAL
241		Z_PARAM_STRING_EX(username, usernamelen, 1, 0)
242		Z_PARAM_STRING_EX(password, passwordlen, 1, 0)
243		Z_PARAM_ARRAY_EX(options, 1, 0)
244	ZEND_PARSE_PARAMETERS_END();
245
246	/* parse the data source name */
247	colon = strchr(data_source, ':');
248
249	if (!colon) {
250		/* let's see if this string has a matching dsn in the php.ini */
251		char *ini_dsn = NULL;
252
253		snprintf(alt_dsn, sizeof(alt_dsn), "pdo.dsn.%s", data_source);
254		if (FAILURE == cfg_get_string(alt_dsn, &ini_dsn)) {
255			zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name");
256			return;
257		}
258
259		data_source = ini_dsn;
260		colon = strchr(data_source, ':');
261
262		if (!colon) {
263			zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name (via INI: %s)", alt_dsn);
264			return;
265		}
266	}
267
268	if (!strncmp(data_source, "uri:", sizeof("uri:")-1)) {
269		/* the specified URI holds connection details */
270		data_source = dsn_from_uri(data_source + sizeof("uri:")-1, alt_dsn, sizeof(alt_dsn));
271		if (!data_source) {
272			zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source URI");
273			return;
274		}
275		colon = strchr(data_source, ':');
276		if (!colon) {
277			zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name (via URI)");
278			return;
279		}
280	}
281
282	driver = pdo_find_driver(data_source, colon - data_source);
283
284	if (!driver) {
285		/* NB: don't want to include the data_source in the error message as
286		 * it might contain a password */
287		zend_throw_exception_ex(php_pdo_get_exception(), 0, "could not find driver");
288		return;
289	}
290
291	dbh = Z_PDO_DBH_P(object);
292
293	/* is this supposed to be a persistent connection ? */
294	if (options) {
295		int plen = 0;
296		char *hashkey = NULL;
297		zend_resource *le;
298		pdo_dbh_t *pdbh = NULL;
299		zval *v;
300
301		if ((v = zend_hash_index_find_deref(Z_ARRVAL_P(options), PDO_ATTR_PERSISTENT)) != NULL) {
302			if (Z_TYPE_P(v) == IS_STRING &&
303				!is_numeric_string(Z_STRVAL_P(v), Z_STRLEN_P(v), NULL, NULL, 0) && Z_STRLEN_P(v) > 0) {
304				/* user specified key */
305				plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", data_source,
306						username ? username : "",
307						password ? password : "",
308						Z_STRVAL_P(v));
309				is_persistent = 1;
310			} else {
311				is_persistent = zval_get_long(v) ? 1 : 0;
312				plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s", data_source,
313						username ? username : "",
314						password ? password : "");
315			}
316		}
317
318		if (is_persistent) {
319			/* let's see if we have one cached.... */
320			if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashkey, plen)) != NULL) {
321				if (le->type == php_pdo_list_entry()) {
322					pdbh = (pdo_dbh_t*)le->ptr;
323
324					/* is the connection still alive ? */
325					if (pdbh->methods->check_liveness && FAILURE == (pdbh->methods->check_liveness)(pdbh)) {
326						/* nope... need to kill it */
327						pdbh->refcount--;
328						zend_list_close(le);
329						pdbh = NULL;
330					}
331				}
332			}
333
334			if (pdbh) {
335				call_factory = 0;
336			} else {
337				/* need a brand new pdbh */
338				pdbh = pecalloc(1, sizeof(*pdbh), 1);
339
340				pdbh->refcount = 1;
341				pdbh->is_persistent = 1;
342				pdbh->persistent_id = pemalloc(plen + 1, 1);
343				memcpy((char *)pdbh->persistent_id, hashkey, plen+1);
344				pdbh->persistent_id_len = plen;
345				pdbh->def_stmt_ce = dbh->def_stmt_ce;
346			}
347		}
348
349		if (pdbh) {
350			efree(dbh);
351			/* switch over to the persistent one */
352			Z_PDO_OBJECT_P(object)->inner = pdbh;
353			pdbh->refcount++;
354			dbh = pdbh;
355		}
356
357		if (hashkey) {
358			efree(hashkey);
359		}
360	}
361
362	if (call_factory) {
363		dbh->data_source_len = strlen(colon + 1);
364		dbh->data_source = (const char*)pestrdup(colon + 1, is_persistent);
365		dbh->username = username ? pestrdup(username, is_persistent) : NULL;
366		dbh->password = password ? pestrdup(password, is_persistent) : NULL;
367		dbh->default_fetch_type = PDO_FETCH_BOTH;
368	}
369
370	dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
371
372	if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
373		php_error_docref(NULL, E_ERROR, "out of memory");
374	}
375
376	zend_replace_error_handling(EH_THROW, pdo_exception_ce, &zeh);
377
378	if (!call_factory) {
379		/* we got a persistent guy from our cache */
380		goto options;
381	}
382
383	if (driver->db_handle_factory(dbh, options)) {
384		/* all set */
385
386		if (is_persistent) {
387			/* register in the persistent list etc. */
388			/* we should also need to replace the object store entry,
389			   since it was created with emalloc */
390			if ((zend_register_persistent_resource(
391						(char*)dbh->persistent_id, dbh->persistent_id_len, dbh, php_pdo_list_entry())) == NULL) {
392				php_error_docref(NULL, E_ERROR, "Failed to register persistent entry");
393			}
394		}
395
396		dbh->driver = driver;
397options:
398		if (options) {
399			zval *attr_value;
400			zend_ulong long_key;
401			zend_string *str_key = NULL;
402
403			ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), long_key, str_key, attr_value) {
404				if (str_key) {
405					continue;
406				}
407				ZVAL_DEREF(attr_value);
408				pdo_dbh_attribute_set(dbh, long_key, attr_value);
409			} ZEND_HASH_FOREACH_END();
410		}
411
412		zend_restore_error_handling(&zeh);
413		return;
414	}
415
416	/* the connection failed; things will tidy up in free_storage */
417	/* XXX raise exception */
418	zend_restore_error_handling(&zeh);
419	if (!EG(exception)) {
420		zend_throw_exception(pdo_exception_ce, "Constructor failed", 0);
421	}
422}
423/* }}} */
424
425static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
426{
427	if (!Z_ISUNDEF_P(ctor_args)) {
428		if (Z_TYPE_P(ctor_args) != IS_ARRAY) {
429			pdo_raise_impl_error(dbh, NULL, "HY000", "constructor arguments must be passed as an array");
430			return NULL;
431		}
432		if (!dbstmt_ce->constructor) {
433			pdo_raise_impl_error(dbh, NULL, "HY000", "user-supplied statement does not accept constructor arguments");
434			return NULL;
435		}
436	}
437
438	if (UNEXPECTED(object_init_ex(object, dbstmt_ce) != SUCCESS)) {
439		return NULL;
440	}
441
442	return object;
443} /* }}} */
444
445static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
446{
447	zval query_string;
448	zval z_key;
449
450	ZVAL_STRINGL(&query_string, stmt->query_string, stmt->query_stringlen);
451	ZVAL_STRINGL(&z_key, "queryString", sizeof("queryString") - 1);
452	zend_std_write_property(object, &z_key, &query_string, NULL);
453	zval_ptr_dtor(&query_string);
454	zval_ptr_dtor(&z_key);
455
456	if (dbstmt_ce->constructor) {
457		zend_fcall_info fci;
458		zend_fcall_info_cache fcc;
459		zval retval;
460
461		fci.size = sizeof(zend_fcall_info);
462		ZVAL_UNDEF(&fci.function_name);
463		fci.object = Z_OBJ_P(object);
464		fci.retval = &retval;
465		fci.param_count = 0;
466		fci.params = NULL;
467		fci.no_separation = 1;
468
469		zend_fcall_info_args(&fci, ctor_args);
470
471		fcc.function_handler = dbstmt_ce->constructor;
472		fcc.called_scope = Z_OBJCE_P(object);
473		fcc.object = Z_OBJ_P(object);
474
475		if (zend_call_function(&fci, &fcc) != FAILURE) {
476			zval_ptr_dtor(&retval);
477		}
478
479		zend_fcall_info_args_clear(&fci, 1);
480	}
481}
482/* }}} */
483
484/* {{{ proto object PDO::prepare(string statement [, array options])
485   Prepares a statement for execution and returns a statement object */
486static PHP_METHOD(PDO, prepare)
487{
488	pdo_stmt_t *stmt;
489	char *statement;
490	size_t statement_len;
491	zval *options = NULL, *opt, *item, ctor_args;
492	zend_class_entry *dbstmt_ce, *pce;
493	pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
494	pdo_dbh_t *dbh = dbh_obj->inner;
495
496	ZEND_PARSE_PARAMETERS_START(1, 2)
497		Z_PARAM_STRING(statement, statement_len)
498		Z_PARAM_OPTIONAL
499		Z_PARAM_ARRAY(options)
500	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
501
502	PDO_DBH_CLEAR_ERR();
503	PDO_CONSTRUCT_CHECK;
504
505	if (ZEND_NUM_ARGS() > 1 && (opt = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
506		if (Z_TYPE_P(opt) != IS_ARRAY || (item = zend_hash_index_find(Z_ARRVAL_P(opt), 0)) == NULL
507			|| Z_TYPE_P(item) != IS_STRING
508			|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
509		) {
510			pdo_raise_impl_error(dbh, NULL, "HY000",
511				"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
512				"the classname must be a string specifying an existing class"
513				);
514			PDO_HANDLE_DBH_ERR();
515			RETURN_FALSE;
516		}
517		dbstmt_ce = pce;
518		if (!instanceof_function(dbstmt_ce, pdo_dbstmt_ce)) {
519			pdo_raise_impl_error(dbh, NULL, "HY000",
520				"user-supplied statement class must be derived from PDOStatement");
521			PDO_HANDLE_DBH_ERR();
522			RETURN_FALSE;
523		}
524		if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
525			pdo_raise_impl_error(dbh, NULL, "HY000",
526				"user-supplied statement class cannot have a public constructor");
527			PDO_HANDLE_DBH_ERR();
528			RETURN_FALSE;
529		}
530		if ((item = zend_hash_index_find(Z_ARRVAL_P(opt), 1)) != NULL) {
531			if (Z_TYPE_P(item) != IS_ARRAY) {
532				pdo_raise_impl_error(dbh, NULL, "HY000",
533					"PDO::ATTR_STATEMENT_CLASS requires format array(classname, ctor_args); "
534					"ctor_args must be an array"
535				);
536				PDO_HANDLE_DBH_ERR();
537				RETURN_FALSE;
538			}
539			ZVAL_COPY_VALUE(&ctor_args, item);
540		} else {
541			ZVAL_UNDEF(&ctor_args);
542		}
543	} else {
544		dbstmt_ce = dbh->def_stmt_ce;
545		ZVAL_COPY_VALUE(&ctor_args, &dbh->def_stmt_ctor_args);
546	}
547
548	if (!pdo_stmt_instantiate(dbh, return_value, dbstmt_ce, &ctor_args)) {
549		if (EXPECTED(!EG(exception))) {
550			pdo_raise_impl_error(dbh, NULL, "HY000",
551				"failed to instantiate user-supplied statement class"
552				);
553		}
554		PDO_HANDLE_DBH_ERR();
555		RETURN_FALSE;
556	}
557	stmt = Z_PDO_STMT_P(return_value);
558
559	/* unconditionally keep this for later reference */
560	stmt->query_string = estrndup(statement, statement_len);
561	stmt->query_stringlen = statement_len;
562	stmt->default_fetch_type = dbh->default_fetch_type;
563	stmt->dbh = dbh;
564	/* give it a reference to me */
565	ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
566	Z_ADDREF(stmt->database_object_handle);
567	/* we haven't created a lazy object yet */
568	ZVAL_UNDEF(&stmt->lazy_object_ref);
569
570	if (dbh->methods->preparer(dbh, statement, statement_len, stmt, options)) {
571		pdo_stmt_construct(execute_data, stmt, return_value, dbstmt_ce, &ctor_args);
572		return;
573	}
574
575	PDO_HANDLE_DBH_ERR();
576
577	/* kill the object handle for the stmt here */
578	zval_ptr_dtor(return_value);
579
580	RETURN_FALSE;
581}
582/* }}} */
583
584/* {{{ proto bool PDO::beginTransaction()
585   Initiates a transaction */
586static PHP_METHOD(PDO, beginTransaction)
587{
588	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
589
590	if (zend_parse_parameters_none() == FAILURE) {
591		return;
592	}
593	PDO_CONSTRUCT_CHECK;
594
595	if (dbh->in_txn) {
596		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is already an active transaction");
597		RETURN_FALSE;
598	}
599
600	if (!dbh->methods->begin) {
601		/* TODO: this should be an exception; see the auto-commit mode
602		 * comments below */
603		zend_throw_exception_ex(php_pdo_get_exception(), 0, "This driver doesn't support transactions");
604		RETURN_FALSE;
605	}
606
607	if (dbh->methods->begin(dbh)) {
608		dbh->in_txn = 1;
609		RETURN_TRUE;
610	}
611
612	PDO_HANDLE_DBH_ERR();
613	RETURN_FALSE;
614}
615/* }}} */
616
617/* {{{ proto bool PDO::commit()
618   Commit a transaction */
619static PHP_METHOD(PDO, commit)
620{
621	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
622
623	if (zend_parse_parameters_none() == FAILURE) {
624		return;
625	}
626	PDO_CONSTRUCT_CHECK;
627
628	if (!dbh->in_txn) {
629		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
630		RETURN_FALSE;
631	}
632
633	if (dbh->methods->commit(dbh)) {
634		dbh->in_txn = 0;
635		RETURN_TRUE;
636	}
637
638	PDO_HANDLE_DBH_ERR();
639	RETURN_FALSE;
640}
641/* }}} */
642
643/* {{{ proto bool PDO::rollBack()
644   roll back a transaction */
645static PHP_METHOD(PDO, rollBack)
646{
647	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
648
649	if (zend_parse_parameters_none() == FAILURE) {
650		return;
651	}
652	PDO_CONSTRUCT_CHECK;
653
654	if (!dbh->in_txn) {
655		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
656		RETURN_FALSE;
657	}
658
659	if (dbh->methods->rollback(dbh)) {
660		dbh->in_txn = 0;
661		RETURN_TRUE;
662	}
663
664	PDO_HANDLE_DBH_ERR();
665	RETURN_FALSE;
666}
667/* }}} */
668
669/* {{{ proto bool PDO::inTransaction()
670   determine if inside a transaction */
671static PHP_METHOD(PDO, inTransaction)
672{
673	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
674
675	if (zend_parse_parameters_none() == FAILURE) {
676		return;
677	}
678	PDO_CONSTRUCT_CHECK;
679
680	if (!dbh->methods->in_transaction) {
681		RETURN_BOOL(dbh->in_txn);
682	}
683
684	RETURN_BOOL(dbh->methods->in_transaction(dbh));
685}
686/* }}} */
687
688static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /* {{{ */
689{
690	zend_long lval;
691
692#define PDO_LONG_PARAM_CHECK \
693	if (Z_TYPE_P(value) != IS_LONG && Z_TYPE_P(value) != IS_STRING && Z_TYPE_P(value) != IS_FALSE && Z_TYPE_P(value) != IS_TRUE) { \
694		pdo_raise_impl_error(dbh, NULL, "HY000", "attribute value must be an integer"); \
695		PDO_HANDLE_DBH_ERR(); \
696		return FAILURE; \
697	} \
698
699	switch (attr) {
700		case PDO_ATTR_ERRMODE:
701			PDO_LONG_PARAM_CHECK;
702			lval = zval_get_long(value);
703			switch (lval) {
704				case PDO_ERRMODE_SILENT:
705				case PDO_ERRMODE_WARNING:
706				case PDO_ERRMODE_EXCEPTION:
707					dbh->error_mode = lval;
708					return SUCCESS;
709				default:
710					pdo_raise_impl_error(dbh, NULL, "HY000", "invalid error mode");
711					PDO_HANDLE_DBH_ERR();
712					return FAILURE;
713			}
714			return FAILURE;
715
716		case PDO_ATTR_CASE:
717			PDO_LONG_PARAM_CHECK;
718			lval = zval_get_long(value);
719			switch (lval) {
720				case PDO_CASE_NATURAL:
721				case PDO_CASE_UPPER:
722				case PDO_CASE_LOWER:
723					dbh->desired_case = lval;
724					return SUCCESS;
725				default:
726					pdo_raise_impl_error(dbh, NULL, "HY000", "invalid case folding mode");
727					PDO_HANDLE_DBH_ERR();
728					return FAILURE;
729			}
730			return FAILURE;
731
732		case PDO_ATTR_ORACLE_NULLS:
733			PDO_LONG_PARAM_CHECK;
734			dbh->oracle_nulls = zval_get_long(value);
735			return SUCCESS;
736
737		case PDO_ATTR_DEFAULT_FETCH_MODE:
738			if (Z_TYPE_P(value) == IS_ARRAY) {
739				zval *tmp;
740				if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
741					if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) {
742						pdo_raise_impl_error(dbh, NULL, "HY000", "FETCH_INTO and FETCH_CLASS are not yet supported as default fetch modes");
743						return FAILURE;
744					}
745				}
746			} else {
747				PDO_LONG_PARAM_CHECK;
748			}
749			lval = zval_get_long(value);
750			if (lval == PDO_FETCH_USE_DEFAULT) {
751				pdo_raise_impl_error(dbh, NULL, "HY000", "invalid fetch mode type");
752				return FAILURE;
753			}
754			dbh->default_fetch_type = lval;
755			return SUCCESS;
756
757		case PDO_ATTR_STRINGIFY_FETCHES:
758			PDO_LONG_PARAM_CHECK;
759			dbh->stringify = zval_get_long(value) ? 1 : 0;
760			return SUCCESS;
761
762		case PDO_ATTR_STATEMENT_CLASS: {
763			/* array(string classname, array(mixed ctor_args)) */
764			zend_class_entry *pce;
765			zval *item;
766
767			if (dbh->is_persistent) {
768				pdo_raise_impl_error(dbh, NULL, "HY000",
769					"PDO::ATTR_STATEMENT_CLASS cannot be used with persistent PDO instances"
770					);
771				PDO_HANDLE_DBH_ERR();
772				return FAILURE;
773			}
774			if (Z_TYPE_P(value) != IS_ARRAY
775				|| (item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL
776				|| Z_TYPE_P(item) != IS_STRING
777				|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
778			) {
779				pdo_raise_impl_error(dbh, NULL, "HY000",
780					"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
781					"the classname must be a string specifying an existing class"
782					);
783				PDO_HANDLE_DBH_ERR();
784				return FAILURE;
785			}
786			if (!instanceof_function(pce, pdo_dbstmt_ce)) {
787				pdo_raise_impl_error(dbh, NULL, "HY000",
788					"user-supplied statement class must be derived from PDOStatement");
789				PDO_HANDLE_DBH_ERR();
790				return FAILURE;
791			}
792			if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
793				pdo_raise_impl_error(dbh, NULL, "HY000",
794					"user-supplied statement class cannot have a public constructor");
795				PDO_HANDLE_DBH_ERR();
796				return FAILURE;
797			}
798			dbh->def_stmt_ce = pce;
799			if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
800				zval_ptr_dtor(&dbh->def_stmt_ctor_args);
801				ZVAL_UNDEF(&dbh->def_stmt_ctor_args);
802			}
803			if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
804				if (Z_TYPE_P(item) != IS_ARRAY) {
805					pdo_raise_impl_error(dbh, NULL, "HY000",
806						"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
807						"ctor_args must be an array"
808					);
809					PDO_HANDLE_DBH_ERR();
810					return FAILURE;
811				}
812				ZVAL_COPY(&dbh->def_stmt_ctor_args, item);
813			}
814			return SUCCESS;
815		}
816
817		default:
818			;
819	}
820
821	if (!dbh->methods->set_attribute) {
822		goto fail;
823	}
824
825	PDO_DBH_CLEAR_ERR();
826	if (dbh->methods->set_attribute(dbh, attr, value)) {
827		return SUCCESS;
828	}
829
830fail:
831	if (attr == PDO_ATTR_AUTOCOMMIT) {
832		zend_throw_exception_ex(php_pdo_get_exception(), 0, "The auto-commit mode cannot be changed for this driver");
833	} else if (!dbh->methods->set_attribute) {
834		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support setting attributes");
835	} else {
836		PDO_HANDLE_DBH_ERR();
837	}
838	return FAILURE;
839}
840/* }}} */
841
842/* {{{ proto bool PDO::setAttribute(int attribute, mixed value)
843   Set an attribute */
844static PHP_METHOD(PDO, setAttribute)
845{
846	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
847	zend_long attr;
848	zval *value;
849
850	ZEND_PARSE_PARAMETERS_START(2, 2)
851		Z_PARAM_LONG(attr)
852		Z_PARAM_ZVAL(value)
853	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
854
855	PDO_DBH_CLEAR_ERR();
856	PDO_CONSTRUCT_CHECK;
857
858	if (pdo_dbh_attribute_set(dbh, attr, value) != FAILURE) {
859 		RETURN_TRUE;
860 	}
861 	RETURN_FALSE;
862}
863/* }}} */
864
865/* {{{ proto mixed PDO::getAttribute(int attribute)
866   Get an attribute */
867static PHP_METHOD(PDO, getAttribute)
868{
869	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
870	zend_long attr;
871
872	ZEND_PARSE_PARAMETERS_START(1, 1)
873		Z_PARAM_LONG(attr)
874	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
875
876	PDO_DBH_CLEAR_ERR();
877	PDO_CONSTRUCT_CHECK;
878
879	/* handle generic PDO-level attributes */
880	switch (attr) {
881		case PDO_ATTR_PERSISTENT:
882			RETURN_BOOL(dbh->is_persistent);
883
884		case PDO_ATTR_CASE:
885			RETURN_LONG(dbh->desired_case);
886
887		case PDO_ATTR_ORACLE_NULLS:
888			RETURN_LONG(dbh->oracle_nulls);
889
890		case PDO_ATTR_ERRMODE:
891			RETURN_LONG(dbh->error_mode);
892
893		case PDO_ATTR_DRIVER_NAME:
894			RETURN_STRINGL((char*)dbh->driver->driver_name, dbh->driver->driver_name_len);
895
896		case PDO_ATTR_STATEMENT_CLASS:
897			array_init(return_value);
898			add_next_index_str(return_value, zend_string_copy(dbh->def_stmt_ce->name));
899			if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
900				Z_TRY_ADDREF(dbh->def_stmt_ctor_args);
901				add_next_index_zval(return_value, &dbh->def_stmt_ctor_args);
902			}
903			return;
904		case PDO_ATTR_DEFAULT_FETCH_MODE:
905			RETURN_LONG(dbh->default_fetch_type);
906		default:
907			break;
908	}
909
910	if (!dbh->methods->get_attribute) {
911		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support getting attributes");
912		RETURN_FALSE;
913	}
914
915	switch (dbh->methods->get_attribute(dbh, attr, return_value)) {
916		case -1:
917			PDO_HANDLE_DBH_ERR();
918			RETURN_FALSE;
919
920		case 0:
921			pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support that attribute");
922			RETURN_FALSE;
923
924		default:
925			return;
926	}
927}
928/* }}} */
929
930/* {{{ proto int PDO::exec(string query)
931   Execute a query that does not return a row set, returning the number of affected rows */
932static PHP_METHOD(PDO, exec)
933{
934	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
935	char *statement;
936	size_t statement_len;
937	zend_long ret;
938
939	ZEND_PARSE_PARAMETERS_START(1, 1)
940		Z_PARAM_STRING(statement, statement_len)
941	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
942
943	if (!statement_len) {
944		pdo_raise_impl_error(dbh, NULL, "HY000",  "trying to execute an empty query");
945		RETURN_FALSE;
946	}
947	PDO_DBH_CLEAR_ERR();
948	PDO_CONSTRUCT_CHECK;
949	ret = dbh->methods->doer(dbh, statement, statement_len);
950	if(ret == -1) {
951		PDO_HANDLE_DBH_ERR();
952		RETURN_FALSE;
953	} else {
954		RETURN_LONG(ret);
955	}
956}
957/* }}} */
958
959/* {{{ proto string PDO::lastInsertId([string seqname])
960   Returns the id of the last row that we affected on this connection.  Some databases require a sequence or table name to be passed in.  Not always meaningful. */
961static PHP_METHOD(PDO, lastInsertId)
962{
963	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
964	char *name = NULL;
965	size_t namelen;
966
967	ZEND_PARSE_PARAMETERS_START(0, 1)
968		Z_PARAM_OPTIONAL
969		Z_PARAM_STRING_EX(name, namelen, 1, 0)
970	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
971
972	PDO_DBH_CLEAR_ERR();
973	PDO_CONSTRUCT_CHECK;
974	if (!dbh->methods->last_id) {
975		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support lastInsertId()");
976		RETURN_FALSE;
977	} else {
978		size_t id_len;
979		char *id;
980		id = dbh->methods->last_id(dbh, name, &id_len);
981		if (!id) {
982			PDO_HANDLE_DBH_ERR();
983			RETURN_FALSE;
984		} else {
985			//??? use zend_string ?
986			RETVAL_STRINGL(id, id_len);
987			efree(id);
988		}
989	}
990}
991/* }}} */
992
993/* {{{ proto string PDO::errorCode()
994   Fetch the error code associated with the last operation on the database handle */
995static PHP_METHOD(PDO, errorCode)
996{
997	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
998
999	if (zend_parse_parameters_none() == FAILURE) {
1000		return;
1001	}
1002	PDO_CONSTRUCT_CHECK;
1003
1004	if (dbh->query_stmt) {
1005		RETURN_STRING(dbh->query_stmt->error_code);
1006	}
1007
1008	if (dbh->error_code[0] == '\0') {
1009		RETURN_NULL();
1010	}
1011
1012	/**
1013	 * Making sure that we fallback to the default implementation
1014	 * if the dbh->error_code is not null.
1015	 */
1016	RETURN_STRING(dbh->error_code);
1017}
1018/* }}} */
1019
1020/* {{{ proto int PDO::errorInfo()
1021   Fetch extended error information associated with the last operation on the database handle */
1022static PHP_METHOD(PDO, errorInfo)
1023{
1024	int error_count;
1025	int error_count_diff 	 = 0;
1026	int error_expected_count = 3;
1027
1028	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1029
1030	if (zend_parse_parameters_none() == FAILURE) {
1031		return;
1032	}
1033
1034	PDO_CONSTRUCT_CHECK;
1035
1036	array_init(return_value);
1037
1038	if (dbh->query_stmt) {
1039		add_next_index_string(return_value, dbh->query_stmt->error_code);
1040		if(!strncmp(dbh->query_stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1041	} else {
1042		add_next_index_string(return_value, dbh->error_code);
1043		if(!strncmp(dbh->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1044	}
1045
1046	if (dbh->methods->fetch_err) {
1047		dbh->methods->fetch_err(dbh, dbh->query_stmt, return_value);
1048	}
1049
1050fill_array:
1051	/**
1052	 * In order to be consistent, we have to make sure we add the good amount
1053	 * of nulls depending on the current number of elements. We make a simple
1054	 * difference and add the needed elements
1055	 */
1056	error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1057
1058	if (error_expected_count > error_count) {
1059		int current_index;
1060
1061		error_count_diff = error_expected_count - error_count;
1062		for (current_index = 0; current_index < error_count_diff; current_index++) {
1063			add_next_index_null(return_value);
1064		}
1065	}
1066}
1067/* }}} */
1068
1069/* {{{ proto object PDO::query(string sql [, PDOStatement::setFetchMode() args])
1070   Prepare and execute $sql; returns the statement object for iteration */
1071static PHP_METHOD(PDO, query)
1072{
1073	pdo_stmt_t *stmt;
1074	char *statement;
1075	size_t statement_len;
1076	pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
1077	pdo_dbh_t *dbh = dbh_obj->inner;
1078
1079	/* Return a meaningful error when no parameters were passed */
1080	if (!ZEND_NUM_ARGS()) {
1081		zend_parse_parameters(0, "z|z", NULL, NULL);
1082		RETURN_FALSE;
1083	}
1084
1085	if (FAILURE == zend_parse_parameters(1, "s", &statement,
1086			&statement_len)) {
1087		RETURN_FALSE;
1088	}
1089
1090	PDO_DBH_CLEAR_ERR();
1091	PDO_CONSTRUCT_CHECK;
1092
1093	if (!pdo_stmt_instantiate(dbh, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args)) {
1094		if (EXPECTED(!EG(exception))) {
1095			pdo_raise_impl_error(dbh, NULL, "HY000", "failed to instantiate user supplied statement class");
1096		}
1097		return;
1098	}
1099	stmt = Z_PDO_STMT_P(return_value);
1100
1101	/* unconditionally keep this for later reference */
1102	stmt->query_string = estrndup(statement, statement_len);
1103	stmt->query_stringlen = statement_len;
1104
1105	stmt->default_fetch_type = dbh->default_fetch_type;
1106	stmt->active_query_string = stmt->query_string;
1107	stmt->active_query_stringlen = statement_len;
1108	stmt->dbh = dbh;
1109	/* give it a reference to me */
1110	ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
1111	Z_ADDREF(stmt->database_object_handle);
1112	/* we haven't created a lazy object yet */
1113	ZVAL_UNDEF(&stmt->lazy_object_ref);
1114
1115	if (dbh->methods->preparer(dbh, statement, statement_len, stmt, NULL)) {
1116		PDO_STMT_CLEAR_ERR();
1117		if (ZEND_NUM_ARGS() == 1 || SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, 1)) {
1118
1119			/* now execute the statement */
1120			PDO_STMT_CLEAR_ERR();
1121			if (stmt->methods->executer(stmt)) {
1122				int ret = 1;
1123				if (!stmt->executed) {
1124					if (stmt->dbh->alloc_own_columns) {
1125						ret = pdo_stmt_describe_columns(stmt);
1126					}
1127					stmt->executed = 1;
1128				}
1129				if (ret) {
1130					pdo_stmt_construct(execute_data, stmt, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args);
1131					return;
1132				}
1133			}
1134		}
1135		/* something broke */
1136		dbh->query_stmt = stmt;
1137		ZVAL_COPY_VALUE(&dbh->query_stmt_zval, return_value);
1138		Z_DELREF(stmt->database_object_handle);
1139		ZVAL_UNDEF(&stmt->database_object_handle);
1140		PDO_HANDLE_STMT_ERR();
1141	} else {
1142		PDO_HANDLE_DBH_ERR();
1143		zval_ptr_dtor(return_value);
1144	}
1145
1146	RETURN_FALSE;
1147}
1148/* }}} */
1149
1150/* {{{ proto string PDO::quote(string string [, int paramtype])
1151   quotes string for use in a query.  The optional paramtype acts as a hint for drivers that have alternate quoting styles.  The default value is PDO_PARAM_STR */
1152static PHP_METHOD(PDO, quote)
1153{
1154	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1155	char *str;
1156	size_t str_len;
1157	zend_long paramtype = PDO_PARAM_STR;
1158	char *qstr;
1159	size_t qlen;
1160
1161	ZEND_PARSE_PARAMETERS_START(1, 2)
1162		Z_PARAM_STRING(str, str_len)
1163		Z_PARAM_OPTIONAL
1164		Z_PARAM_LONG(paramtype)
1165	ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE);
1166
1167	PDO_DBH_CLEAR_ERR();
1168	PDO_CONSTRUCT_CHECK;
1169	if (!dbh->methods->quoter) {
1170		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting");
1171		RETURN_FALSE;
1172	}
1173
1174	if (dbh->methods->quoter(dbh, str, str_len, &qstr, &qlen, paramtype)) {
1175		RETVAL_STRINGL(qstr, qlen);
1176		efree(qstr);
1177		return;
1178	}
1179	PDO_HANDLE_DBH_ERR();
1180	RETURN_FALSE;
1181}
1182/* }}} */
1183
1184/* {{{ proto PDO::__wakeup()
1185   Prevents use of a PDO instance that has been unserialized */
1186static PHP_METHOD(PDO, __wakeup)
1187{
1188	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDO instances");
1189}
1190/* }}} */
1191
1192/* {{{ proto int PDO::__sleep()
1193   Prevents serialization of a PDO instance */
1194static PHP_METHOD(PDO, __sleep)
1195{
1196	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDO instances");
1197}
1198/* }}} */
1199
1200/* {{{ proto array PDO::getAvailableDrivers()
1201   Return array of available PDO drivers */
1202static PHP_METHOD(PDO, getAvailableDrivers)
1203{
1204	pdo_driver_t *pdriver;
1205
1206	if (zend_parse_parameters_none() == FAILURE) {
1207		return;
1208	}
1209
1210	array_init(return_value);
1211
1212	ZEND_HASH_FOREACH_PTR(&pdo_driver_hash, pdriver) {
1213		add_next_index_stringl(return_value, (char*)pdriver->driver_name, pdriver->driver_name_len);
1214	} ZEND_HASH_FOREACH_END();
1215}
1216/* }}} */
1217
1218/* {{{ arginfo */
1219ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo___construct, 0, 0, 1)
1220	ZEND_ARG_INFO(0, dsn)
1221	ZEND_ARG_INFO(0, username)
1222	ZEND_ARG_INFO(0, passwd)
1223	ZEND_ARG_INFO(0, options) /* array */
1224ZEND_END_ARG_INFO()
1225
1226ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_prepare, 0, 0, 1)
1227	ZEND_ARG_INFO(0, statement)
1228	ZEND_ARG_INFO(0, options) /* array */
1229ZEND_END_ARG_INFO()
1230
1231ZEND_BEGIN_ARG_INFO(arginfo_pdo_setattribute, 0)
1232	ZEND_ARG_INFO(0, attribute)
1233	ZEND_ARG_INFO(0, value)
1234ZEND_END_ARG_INFO()
1235
1236ZEND_BEGIN_ARG_INFO(arginfo_pdo_getattribute, 0)
1237	ZEND_ARG_INFO(0, attribute)
1238ZEND_END_ARG_INFO()
1239
1240ZEND_BEGIN_ARG_INFO(arginfo_pdo_exec, 0)
1241	ZEND_ARG_INFO(0, query)
1242ZEND_END_ARG_INFO()
1243
1244ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_lastinsertid, 0, 0, 0)
1245	ZEND_ARG_INFO(0, seqname)
1246ZEND_END_ARG_INFO()
1247
1248ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_quote, 0, 0, 1)
1249	ZEND_ARG_INFO(0, string)
1250	ZEND_ARG_INFO(0, paramtype)
1251ZEND_END_ARG_INFO()
1252
1253ZEND_BEGIN_ARG_INFO(arginfo_pdo__void, 0)
1254ZEND_END_ARG_INFO()
1255/* }}} */
1256
1257const zend_function_entry pdo_dbh_functions[] = /* {{{ */ {
1258	ZEND_MALIAS(PDO, __construct, dbh_constructor,	arginfo_pdo___construct,	ZEND_ACC_PUBLIC)
1259	PHP_ME(PDO, prepare, 				arginfo_pdo_prepare,		ZEND_ACC_PUBLIC)
1260	PHP_ME(PDO, beginTransaction,       arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1261	PHP_ME(PDO, commit,                 arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1262	PHP_ME(PDO, rollBack,               arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1263	PHP_ME(PDO, inTransaction,          arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1264	PHP_ME(PDO, setAttribute,	arginfo_pdo_setattribute,	ZEND_ACC_PUBLIC)
1265	PHP_ME(PDO, exec,			arginfo_pdo_exec,		ZEND_ACC_PUBLIC)
1266	PHP_ME(PDO, query,			NULL,					ZEND_ACC_PUBLIC)
1267	PHP_ME(PDO, lastInsertId,	arginfo_pdo_lastinsertid,	ZEND_ACC_PUBLIC)
1268	PHP_ME(PDO, errorCode,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1269	PHP_ME(PDO, errorInfo,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1270	PHP_ME(PDO, getAttribute,	arginfo_pdo_getattribute,	ZEND_ACC_PUBLIC)
1271	PHP_ME(PDO, quote,			arginfo_pdo_quote,		ZEND_ACC_PUBLIC)
1272	PHP_ME(PDO, __wakeup,               arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1273	PHP_ME(PDO, __sleep,                arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1274	PHP_ME(PDO, getAvailableDrivers,    arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1275	PHP_FE_END
1276};
1277/* }}} */
1278
1279static void cls_method_dtor(zval *el) /* {{{ */ {
1280	zend_function *func = (zend_function*)Z_PTR_P(el);
1281	if (func->common.function_name) {
1282		zend_string_release_ex(func->common.function_name, 0);
1283	}
1284	efree(func);
1285}
1286/* }}} */
1287
1288static void cls_method_pdtor(zval *el) /* {{{ */ {
1289	zend_function *func = (zend_function*)Z_PTR_P(el);
1290	if (func->common.function_name) {
1291		zend_string_release_ex(func->common.function_name, 1);
1292	}
1293	pefree(func, 1);
1294}
1295/* }}} */
1296
1297/* {{{ overloaded object handlers for PDO class */
1298int pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
1299{
1300	const zend_function_entry *funcs;
1301	zend_internal_function func;
1302	size_t namelen;
1303	char *lc_name;
1304	pdo_dbh_t *dbh = dbh_obj->inner;
1305
1306	if (!dbh || !dbh->methods || !dbh->methods->get_driver_methods) {
1307		return 0;
1308	}
1309	funcs =	dbh->methods->get_driver_methods(dbh, kind);
1310	if (!funcs) {
1311		return 0;
1312	}
1313
1314	dbh->cls_methods[kind] = pemalloc(sizeof(HashTable), dbh->is_persistent);
1315	zend_hash_init_ex(dbh->cls_methods[kind], 8, NULL,
1316			dbh->is_persistent? cls_method_pdtor : cls_method_dtor, dbh->is_persistent, 0);
1317
1318	memset(&func, 0, sizeof(func));
1319
1320	while (funcs->fname) {
1321		func.type = ZEND_INTERNAL_FUNCTION;
1322		func.handler = funcs->handler;
1323		func.function_name = zend_string_init(funcs->fname, strlen(funcs->fname), dbh->is_persistent);
1324		func.scope = dbh_obj->std.ce;
1325		func.prototype = NULL;
1326		if (funcs->flags) {
1327			func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
1328		} else {
1329			func.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
1330		}
1331		if (funcs->arg_info) {
1332			zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
1333
1334			func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
1335			func.num_args = funcs->num_args;
1336			if (info->required_num_args == (uint32_t)-1) {
1337				func.required_num_args = funcs->num_args;
1338			} else {
1339				func.required_num_args = info->required_num_args;
1340			}
1341			if (info->return_reference) {
1342				func.fn_flags |= ZEND_ACC_RETURN_REFERENCE;
1343			}
1344			if (funcs->arg_info[funcs->num_args].is_variadic) {
1345				func.fn_flags |= ZEND_ACC_VARIADIC;
1346				/* Don't count the variadic argument */
1347				func.num_args--;
1348			}
1349		} else {
1350			func.arg_info = NULL;
1351			func.num_args = 0;
1352			func.required_num_args = 0;
1353		}
1354		zend_set_function_arg_flags((zend_function*)&func);
1355		namelen = strlen(funcs->fname);
1356		lc_name = emalloc(namelen+1);
1357		zend_str_tolower_copy(lc_name, funcs->fname, namelen);
1358		zend_hash_str_add_mem(dbh->cls_methods[kind], lc_name, namelen, &func, sizeof(func));
1359		efree(lc_name);
1360		funcs++;
1361	}
1362
1363	return 1;
1364}
1365
1366static union _zend_function *dbh_method_get(zend_object **object, zend_string *method_name, const zval *key)
1367{
1368	zend_function *fbc = NULL;
1369	pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object);
1370	zend_string *lc_method_name;
1371
1372	if ((fbc = zend_std_get_method(object, method_name, key)) == NULL) {
1373		/* not a pre-defined method, nor a user-defined method; check
1374		 * the driver specific methods */
1375		if (!dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1376			if (!pdo_hash_methods(dbh_obj,
1377				PDO_DBH_DRIVER_METHOD_KIND_DBH)
1378				|| !dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1379				goto out;
1380			}
1381		}
1382
1383		lc_method_name = zend_string_tolower(method_name);
1384		fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name);
1385		zend_string_release_ex(lc_method_name, 0);
1386	}
1387
1388out:
1389	return fbc;
1390}
1391
1392static int dbh_compare(zval *object1, zval *object2)
1393{
1394	return -1;
1395}
1396
1397static HashTable *dbh_get_gc(zval *object, zval **gc_data, int *gc_count)
1398{
1399	pdo_dbh_t *dbh = Z_PDO_DBH_P(object);
1400	*gc_data = &dbh->def_stmt_ctor_args;
1401	*gc_count = 1;
1402	return zend_std_get_properties(object);
1403}
1404
1405static zend_object_handlers pdo_dbh_object_handlers;
1406static void pdo_dbh_free_storage(zend_object *std);
1407
1408void pdo_dbh_init(void)
1409{
1410	zend_class_entry ce;
1411
1412	INIT_CLASS_ENTRY(ce, "PDO", pdo_dbh_functions);
1413	pdo_dbh_ce = zend_register_internal_class(&ce);
1414	pdo_dbh_ce->create_object = pdo_dbh_new;
1415
1416	memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1417	pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std);
1418	pdo_dbh_object_handlers.dtor_obj = zend_objects_destroy_object;
1419	pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage;
1420	pdo_dbh_object_handlers.clone_obj = NULL;
1421	pdo_dbh_object_handlers.get_method = dbh_method_get;
1422	pdo_dbh_object_handlers.compare_objects = dbh_compare;
1423	pdo_dbh_object_handlers.get_gc = dbh_get_gc;
1424
1425	REGISTER_PDO_CLASS_CONST_LONG("PARAM_BOOL", (zend_long)PDO_PARAM_BOOL);
1426	REGISTER_PDO_CLASS_CONST_LONG("PARAM_NULL", (zend_long)PDO_PARAM_NULL);
1427	REGISTER_PDO_CLASS_CONST_LONG("PARAM_INT",  (zend_long)PDO_PARAM_INT);
1428	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STR",  (zend_long)PDO_PARAM_STR);
1429	REGISTER_PDO_CLASS_CONST_LONG("PARAM_LOB",  (zend_long)PDO_PARAM_LOB);
1430	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STMT", (zend_long)PDO_PARAM_STMT);
1431	REGISTER_PDO_CLASS_CONST_LONG("PARAM_INPUT_OUTPUT", (zend_long)PDO_PARAM_INPUT_OUTPUT);
1432	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STR_NATL", (zend_long)PDO_PARAM_STR_NATL);
1433	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STR_CHAR", (zend_long)PDO_PARAM_STR_CHAR);
1434
1435
1436	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_ALLOC",		(zend_long)PDO_PARAM_EVT_ALLOC);
1437	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FREE",			(zend_long)PDO_PARAM_EVT_FREE);
1438	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_PRE",		(zend_long)PDO_PARAM_EVT_EXEC_PRE);
1439	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_POST",	(zend_long)PDO_PARAM_EVT_EXEC_POST);
1440	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_PRE",	(zend_long)PDO_PARAM_EVT_FETCH_PRE);
1441	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_POST",	(zend_long)PDO_PARAM_EVT_FETCH_POST);
1442	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_NORMALIZE",	(zend_long)PDO_PARAM_EVT_NORMALIZE);
1443
1444	REGISTER_PDO_CLASS_CONST_LONG("FETCH_LAZY", (zend_long)PDO_FETCH_LAZY);
1445	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ASSOC", (zend_long)PDO_FETCH_ASSOC);
1446	REGISTER_PDO_CLASS_CONST_LONG("FETCH_NUM",  (zend_long)PDO_FETCH_NUM);
1447	REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOTH", (zend_long)PDO_FETCH_BOTH);
1448	REGISTER_PDO_CLASS_CONST_LONG("FETCH_OBJ",  (zend_long)PDO_FETCH_OBJ);
1449	REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOUND", (zend_long)PDO_FETCH_BOUND);
1450	REGISTER_PDO_CLASS_CONST_LONG("FETCH_COLUMN", (zend_long)PDO_FETCH_COLUMN);
1451	REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASS", (zend_long)PDO_FETCH_CLASS);
1452	REGISTER_PDO_CLASS_CONST_LONG("FETCH_INTO", (zend_long)PDO_FETCH_INTO);
1453	REGISTER_PDO_CLASS_CONST_LONG("FETCH_FUNC", (zend_long)PDO_FETCH_FUNC);
1454	REGISTER_PDO_CLASS_CONST_LONG("FETCH_GROUP", (zend_long)PDO_FETCH_GROUP);
1455	REGISTER_PDO_CLASS_CONST_LONG("FETCH_UNIQUE", (zend_long)PDO_FETCH_UNIQUE);
1456	REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR", (zend_long)PDO_FETCH_KEY_PAIR);
1457	REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE", (zend_long)PDO_FETCH_CLASSTYPE);
1458
1459	REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(zend_long)PDO_FETCH_SERIALIZE);
1460	REGISTER_PDO_CLASS_CONST_LONG("FETCH_PROPS_LATE", (zend_long)PDO_FETCH_PROPS_LATE);
1461	REGISTER_PDO_CLASS_CONST_LONG("FETCH_NAMED", (zend_long)PDO_FETCH_NAMED);
1462
1463	REGISTER_PDO_CLASS_CONST_LONG("ATTR_AUTOCOMMIT",	(zend_long)PDO_ATTR_AUTOCOMMIT);
1464	REGISTER_PDO_CLASS_CONST_LONG("ATTR_PREFETCH",		(zend_long)PDO_ATTR_PREFETCH);
1465	REGISTER_PDO_CLASS_CONST_LONG("ATTR_TIMEOUT", 		(zend_long)PDO_ATTR_TIMEOUT);
1466	REGISTER_PDO_CLASS_CONST_LONG("ATTR_ERRMODE", 		(zend_long)PDO_ATTR_ERRMODE);
1467	REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_VERSION",	(zend_long)PDO_ATTR_SERVER_VERSION);
1468	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CLIENT_VERSION", 	(zend_long)PDO_ATTR_CLIENT_VERSION);
1469	REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_INFO",		(zend_long)PDO_ATTR_SERVER_INFO);
1470	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CONNECTION_STATUS", 	(zend_long)PDO_ATTR_CONNECTION_STATUS);
1471	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CASE",		 	(zend_long)PDO_ATTR_CASE);
1472	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR_NAME", 	(zend_long)PDO_ATTR_CURSOR_NAME);
1473	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR",	 	(zend_long)PDO_ATTR_CURSOR);
1474	REGISTER_PDO_CLASS_CONST_LONG("ATTR_ORACLE_NULLS",	(zend_long)PDO_ATTR_ORACLE_NULLS);
1475	REGISTER_PDO_CLASS_CONST_LONG("ATTR_PERSISTENT",	(zend_long)PDO_ATTR_PERSISTENT);
1476	REGISTER_PDO_CLASS_CONST_LONG("ATTR_STATEMENT_CLASS",		(zend_long)PDO_ATTR_STATEMENT_CLASS);
1477	REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_TABLE_NAMES",		(zend_long)PDO_ATTR_FETCH_TABLE_NAMES);
1478	REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_CATALOG_NAMES",		(zend_long)PDO_ATTR_FETCH_CATALOG_NAMES);
1479	REGISTER_PDO_CLASS_CONST_LONG("ATTR_DRIVER_NAME",		(zend_long)PDO_ATTR_DRIVER_NAME);
1480	REGISTER_PDO_CLASS_CONST_LONG("ATTR_STRINGIFY_FETCHES", (zend_long)PDO_ATTR_STRINGIFY_FETCHES);
1481	REGISTER_PDO_CLASS_CONST_LONG("ATTR_MAX_COLUMN_LEN", (zend_long)PDO_ATTR_MAX_COLUMN_LEN);
1482	REGISTER_PDO_CLASS_CONST_LONG("ATTR_EMULATE_PREPARES", (zend_long)PDO_ATTR_EMULATE_PREPARES);
1483	REGISTER_PDO_CLASS_CONST_LONG("ATTR_DEFAULT_FETCH_MODE", (zend_long)PDO_ATTR_DEFAULT_FETCH_MODE);
1484	REGISTER_PDO_CLASS_CONST_LONG("ATTR_DEFAULT_STR_PARAM", (zend_long)PDO_ATTR_DEFAULT_STR_PARAM);
1485
1486	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_SILENT",	(zend_long)PDO_ERRMODE_SILENT);
1487	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_WARNING",	(zend_long)PDO_ERRMODE_WARNING);
1488	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_EXCEPTION",	(zend_long)PDO_ERRMODE_EXCEPTION);
1489
1490	REGISTER_PDO_CLASS_CONST_LONG("CASE_NATURAL",	(zend_long)PDO_CASE_NATURAL);
1491	REGISTER_PDO_CLASS_CONST_LONG("CASE_LOWER",	(zend_long)PDO_CASE_LOWER);
1492	REGISTER_PDO_CLASS_CONST_LONG("CASE_UPPER",	(zend_long)PDO_CASE_UPPER);
1493
1494	REGISTER_PDO_CLASS_CONST_LONG("NULL_NATURAL",	(zend_long)PDO_NULL_NATURAL);
1495	REGISTER_PDO_CLASS_CONST_LONG("NULL_EMPTY_STRING",	(zend_long)PDO_NULL_EMPTY_STRING);
1496	REGISTER_PDO_CLASS_CONST_LONG("NULL_TO_STRING",	(zend_long)PDO_NULL_TO_STRING);
1497
1498	REGISTER_PDO_CLASS_CONST_STRING("ERR_NONE",	PDO_ERR_NONE);
1499
1500	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_NEXT", (zend_long)PDO_FETCH_ORI_NEXT);
1501	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_PRIOR", (zend_long)PDO_FETCH_ORI_PRIOR);
1502	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_FIRST", (zend_long)PDO_FETCH_ORI_FIRST);
1503	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_LAST", (zend_long)PDO_FETCH_ORI_LAST);
1504	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_ABS", (zend_long)PDO_FETCH_ORI_ABS);
1505	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_REL", (zend_long)PDO_FETCH_ORI_REL);
1506
1507	REGISTER_PDO_CLASS_CONST_LONG("CURSOR_FWDONLY", (zend_long)PDO_CURSOR_FWDONLY);
1508	REGISTER_PDO_CLASS_CONST_LONG("CURSOR_SCROLL", (zend_long)PDO_CURSOR_SCROLL);
1509}
1510
1511static void dbh_free(pdo_dbh_t *dbh, zend_bool free_persistent)
1512{
1513	int i;
1514
1515	if (dbh->query_stmt) {
1516		zval_ptr_dtor(&dbh->query_stmt_zval);
1517		dbh->query_stmt = NULL;
1518	}
1519
1520	if (dbh->is_persistent) {
1521#if ZEND_DEBUG
1522		ZEND_ASSERT(!free_persistent || (dbh->refcount == 1));
1523#endif
1524		if (!free_persistent && (--dbh->refcount)) {
1525			return;
1526		}
1527	}
1528
1529	if (dbh->methods) {
1530		dbh->methods->closer(dbh);
1531	}
1532
1533	if (dbh->data_source) {
1534		pefree((char *)dbh->data_source, dbh->is_persistent);
1535	}
1536	if (dbh->username) {
1537		pefree(dbh->username, dbh->is_persistent);
1538	}
1539	if (dbh->password) {
1540		pefree(dbh->password, dbh->is_persistent);
1541	}
1542
1543	if (dbh->persistent_id) {
1544		pefree((char *)dbh->persistent_id, dbh->is_persistent);
1545	}
1546
1547	if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
1548		zval_ptr_dtor(&dbh->def_stmt_ctor_args);
1549	}
1550
1551	for (i = 0; i < PDO_DBH_DRIVER_METHOD_KIND__MAX; i++) {
1552		if (dbh->cls_methods[i]) {
1553			zend_hash_destroy(dbh->cls_methods[i]);
1554			pefree(dbh->cls_methods[i], dbh->is_persistent);
1555		}
1556	}
1557
1558	pefree(dbh, dbh->is_persistent);
1559}
1560
1561static void pdo_dbh_free_storage(zend_object *std)
1562{
1563	pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std);
1564	if (dbh->in_txn && dbh->methods && dbh->methods->rollback) {
1565		dbh->methods->rollback(dbh);
1566		dbh->in_txn = 0;
1567	}
1568
1569	if (dbh->is_persistent && dbh->methods && dbh->methods->persistent_shutdown) {
1570		dbh->methods->persistent_shutdown(dbh);
1571	}
1572	zend_object_std_dtor(std);
1573	dbh_free(dbh, 0);
1574}
1575
1576zend_object *pdo_dbh_new(zend_class_entry *ce)
1577{
1578	pdo_dbh_object_t *dbh;
1579
1580	dbh = zend_object_alloc(sizeof(pdo_dbh_object_t), ce);
1581	zend_object_std_init(&dbh->std, ce);
1582	object_properties_init(&dbh->std, ce);
1583	rebuild_object_properties(&dbh->std);
1584	dbh->inner = ecalloc(1, sizeof(pdo_dbh_t));
1585	dbh->inner->def_stmt_ce = pdo_dbstmt_ce;
1586
1587	dbh->std.handlers = &pdo_dbh_object_handlers;
1588
1589	return &dbh->std;
1590}
1591
1592/* }}} */
1593
1594ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor) /* {{{ */
1595{
1596	if (res->ptr) {
1597		pdo_dbh_t *dbh = (pdo_dbh_t*)res->ptr;
1598		dbh_free(dbh, 1);
1599		res->ptr = NULL;
1600	}
1601}
1602/* }}} */
1603
1604/*
1605 * Local variables:
1606 * tab-width: 4
1607 * c-basic-offset: 4
1608 * End:
1609 * vim600: noet sw=4 ts=4 fdm=marker
1610 * vim<600: noet sw=4 ts=4
1611 */
1612