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