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		ZVAL_UNDEF(&fci.function_name);
440		fci.object = Z_OBJ_P(object);
441		fci.retval = &retval;
442		fci.param_count = 0;
443		fci.params = NULL;
444		fci.no_separation = 1;
445
446		zend_fcall_info_args(&fci, ctor_args);
447
448		fcc.initialized = 1;
449		fcc.function_handler = dbstmt_ce->constructor;
450		fcc.calling_scope = zend_get_executed_scope();
451		fcc.called_scope = Z_OBJCE_P(object);
452		fcc.object = Z_OBJ_P(object);
453
454		if (zend_call_function(&fci, &fcc) != FAILURE) {
455			zval_ptr_dtor(&retval);
456		}
457
458		zend_fcall_info_args_clear(&fci, 1);
459	}
460}
461/* }}} */
462
463/* {{{ proto object PDO::prepare(string statement [, array options])
464   Prepares a statement for execution and returns a statement object */
465static PHP_METHOD(PDO, prepare)
466{
467	pdo_stmt_t *stmt;
468	char *statement;
469	size_t statement_len;
470	zval *options = NULL, *opt, *item, ctor_args;
471	zend_class_entry *dbstmt_ce, *pce;
472	pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
473	pdo_dbh_t *dbh = dbh_obj->inner;
474
475	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &statement,
476			&statement_len, &options)) {
477		RETURN_FALSE;
478	}
479
480	PDO_DBH_CLEAR_ERR();
481	PDO_CONSTRUCT_CHECK;
482
483	if (ZEND_NUM_ARGS() > 1 && (opt = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
484		if (Z_TYPE_P(opt) != IS_ARRAY || (item = zend_hash_index_find(Z_ARRVAL_P(opt), 0)) == NULL
485			|| Z_TYPE_P(item) != IS_STRING
486			|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
487		) {
488			pdo_raise_impl_error(dbh, NULL, "HY000",
489				"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
490				"the classname must be a string specifying an existing class"
491				);
492			PDO_HANDLE_DBH_ERR();
493			RETURN_FALSE;
494		}
495		dbstmt_ce = pce;
496		if (!instanceof_function(dbstmt_ce, pdo_dbstmt_ce)) {
497			pdo_raise_impl_error(dbh, NULL, "HY000",
498				"user-supplied statement class must be derived from PDOStatement");
499			PDO_HANDLE_DBH_ERR();
500			RETURN_FALSE;
501		}
502		if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
503			pdo_raise_impl_error(dbh, NULL, "HY000",
504				"user-supplied statement class cannot have a public constructor");
505			PDO_HANDLE_DBH_ERR();
506			RETURN_FALSE;
507		}
508		if ((item = zend_hash_index_find(Z_ARRVAL_P(opt), 1)) != NULL) {
509			if (Z_TYPE_P(item) != IS_ARRAY) {
510				pdo_raise_impl_error(dbh, NULL, "HY000",
511					"PDO::ATTR_STATEMENT_CLASS requires format array(classname, ctor_args); "
512					"ctor_args must be an array"
513				);
514				PDO_HANDLE_DBH_ERR();
515				RETURN_FALSE;
516			}
517			ZVAL_COPY_VALUE(&ctor_args, item);
518		} else {
519			ZVAL_UNDEF(&ctor_args);
520		}
521	} else {
522		dbstmt_ce = dbh->def_stmt_ce;
523		ZVAL_COPY_VALUE(&ctor_args, &dbh->def_stmt_ctor_args);
524	}
525
526	if (!pdo_stmt_instantiate(dbh, return_value, dbstmt_ce, &ctor_args)) {
527		if (EXPECTED(!EG(exception))) {
528			pdo_raise_impl_error(dbh, NULL, "HY000",
529				"failed to instantiate user-supplied statement class"
530				);
531		}
532		PDO_HANDLE_DBH_ERR();
533		RETURN_FALSE;
534	}
535	stmt = Z_PDO_STMT_P(return_value);
536
537	/* unconditionally keep this for later reference */
538	stmt->query_string = estrndup(statement, statement_len);
539	stmt->query_stringlen = statement_len;
540	stmt->default_fetch_type = dbh->default_fetch_type;
541	stmt->dbh = dbh;
542	/* give it a reference to me */
543	ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
544	Z_ADDREF(stmt->database_object_handle);
545	/* we haven't created a lazy object yet */
546	ZVAL_UNDEF(&stmt->lazy_object_ref);
547
548	if (dbh->methods->preparer(dbh, statement, statement_len, stmt, options)) {
549		pdo_stmt_construct(execute_data, stmt, return_value, dbstmt_ce, &ctor_args);
550		return;
551	}
552
553	PDO_HANDLE_DBH_ERR();
554
555	/* kill the object handle for the stmt here */
556	zval_dtor(return_value);
557
558	RETURN_FALSE;
559}
560/* }}} */
561
562/* {{{ proto bool PDO::beginTransaction()
563   Initiates a transaction */
564static PHP_METHOD(PDO, beginTransaction)
565{
566	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
567
568	if (zend_parse_parameters_none() == FAILURE) {
569		return;
570	}
571	PDO_CONSTRUCT_CHECK;
572
573	if (dbh->in_txn) {
574		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is already an active transaction");
575		RETURN_FALSE;
576	}
577
578	if (!dbh->methods->begin) {
579		/* TODO: this should be an exception; see the auto-commit mode
580		 * comments below */
581		zend_throw_exception_ex(php_pdo_get_exception(), 0, "This driver doesn't support transactions");
582		RETURN_FALSE;
583	}
584
585	if (dbh->methods->begin(dbh)) {
586		dbh->in_txn = 1;
587		RETURN_TRUE;
588	}
589
590	PDO_HANDLE_DBH_ERR();
591	RETURN_FALSE;
592}
593/* }}} */
594
595/* {{{ proto bool PDO::commit()
596   Commit a transaction */
597static PHP_METHOD(PDO, commit)
598{
599	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
600
601	if (zend_parse_parameters_none() == FAILURE) {
602		return;
603	}
604	PDO_CONSTRUCT_CHECK;
605
606	if (!dbh->in_txn) {
607		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
608		RETURN_FALSE;
609	}
610
611	if (dbh->methods->commit(dbh)) {
612		dbh->in_txn = 0;
613		RETURN_TRUE;
614	}
615
616	PDO_HANDLE_DBH_ERR();
617	RETURN_FALSE;
618}
619/* }}} */
620
621/* {{{ proto bool PDO::rollBack()
622   roll back a transaction */
623static PHP_METHOD(PDO, rollBack)
624{
625	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
626
627	if (zend_parse_parameters_none() == FAILURE) {
628		return;
629	}
630	PDO_CONSTRUCT_CHECK;
631
632	if (!dbh->in_txn) {
633		zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
634		RETURN_FALSE;
635	}
636
637	if (dbh->methods->rollback(dbh)) {
638		dbh->in_txn = 0;
639		RETURN_TRUE;
640	}
641
642	PDO_HANDLE_DBH_ERR();
643	RETURN_FALSE;
644}
645/* }}} */
646
647/* {{{ proto bool PDO::inTransaction()
648   determine if inside a transaction */
649static PHP_METHOD(PDO, inTransaction)
650{
651	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
652
653	if (zend_parse_parameters_none() == FAILURE) {
654		return;
655	}
656	PDO_CONSTRUCT_CHECK;
657
658	if (!dbh->methods->in_transaction) {
659		RETURN_BOOL(dbh->in_txn);
660	}
661
662	RETURN_BOOL(dbh->methods->in_transaction(dbh));
663}
664/* }}} */
665
666static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /* {{{ */
667{
668	zend_long lval;
669
670#define PDO_LONG_PARAM_CHECK \
671	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) { \
672		pdo_raise_impl_error(dbh, NULL, "HY000", "attribute value must be an integer"); \
673		PDO_HANDLE_DBH_ERR(); \
674		return FAILURE; \
675	} \
676
677	switch (attr) {
678		case PDO_ATTR_ERRMODE:
679			PDO_LONG_PARAM_CHECK;
680			lval = zval_get_long(value);
681			switch (lval) {
682				case PDO_ERRMODE_SILENT:
683				case PDO_ERRMODE_WARNING:
684				case PDO_ERRMODE_EXCEPTION:
685					dbh->error_mode = lval;
686					return SUCCESS;
687				default:
688					pdo_raise_impl_error(dbh, NULL, "HY000", "invalid error mode");
689					PDO_HANDLE_DBH_ERR();
690					return FAILURE;
691			}
692			return FAILURE;
693
694		case PDO_ATTR_CASE:
695			PDO_LONG_PARAM_CHECK;
696			lval = zval_get_long(value);
697			switch (lval) {
698				case PDO_CASE_NATURAL:
699				case PDO_CASE_UPPER:
700				case PDO_CASE_LOWER:
701					dbh->desired_case = lval;
702					return SUCCESS;
703				default:
704					pdo_raise_impl_error(dbh, NULL, "HY000", "invalid case folding mode");
705					PDO_HANDLE_DBH_ERR();
706					return FAILURE;
707			}
708			return FAILURE;
709
710		case PDO_ATTR_ORACLE_NULLS:
711			PDO_LONG_PARAM_CHECK;
712			dbh->oracle_nulls = zval_get_long(value);
713			return SUCCESS;
714
715		case PDO_ATTR_DEFAULT_FETCH_MODE:
716			if (Z_TYPE_P(value) == IS_ARRAY) {
717				zval *tmp;
718				if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
719					if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) {
720						pdo_raise_impl_error(dbh, NULL, "HY000", "FETCH_INTO and FETCH_CLASS are not yet supported as default fetch modes");
721						return FAILURE;
722					}
723				}
724			} else {
725				PDO_LONG_PARAM_CHECK;
726			}
727			lval = zval_get_long(value);
728			if (lval == PDO_FETCH_USE_DEFAULT) {
729				pdo_raise_impl_error(dbh, NULL, "HY000", "invalid fetch mode type");
730				return FAILURE;
731			}
732			dbh->default_fetch_type = lval;
733			return SUCCESS;
734
735		case PDO_ATTR_STRINGIFY_FETCHES:
736			PDO_LONG_PARAM_CHECK;
737			dbh->stringify = zval_get_long(value) ? 1 : 0;
738			return SUCCESS;
739
740		case PDO_ATTR_STATEMENT_CLASS: {
741			/* array(string classname, array(mixed ctor_args)) */
742			zend_class_entry *pce;
743			zval *item;
744
745			if (dbh->is_persistent) {
746				pdo_raise_impl_error(dbh, NULL, "HY000",
747					"PDO::ATTR_STATEMENT_CLASS cannot be used with persistent PDO instances"
748					);
749				PDO_HANDLE_DBH_ERR();
750				return FAILURE;
751			}
752			if (Z_TYPE_P(value) != IS_ARRAY
753				|| (item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL
754				|| Z_TYPE_P(item) != IS_STRING
755				|| (pce = zend_lookup_class(Z_STR_P(item))) == NULL
756			) {
757				pdo_raise_impl_error(dbh, NULL, "HY000",
758					"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
759					"the classname must be a string specifying an existing class"
760					);
761				PDO_HANDLE_DBH_ERR();
762				return FAILURE;
763			}
764			if (!instanceof_function(pce, pdo_dbstmt_ce)) {
765				pdo_raise_impl_error(dbh, NULL, "HY000",
766					"user-supplied statement class must be derived from PDOStatement");
767				PDO_HANDLE_DBH_ERR();
768				return FAILURE;
769			}
770			if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
771				pdo_raise_impl_error(dbh, NULL, "HY000",
772					"user-supplied statement class cannot have a public constructor");
773				PDO_HANDLE_DBH_ERR();
774				return FAILURE;
775			}
776			dbh->def_stmt_ce = pce;
777			if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
778				zval_ptr_dtor(&dbh->def_stmt_ctor_args);
779				ZVAL_UNDEF(&dbh->def_stmt_ctor_args);
780			}
781			if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
782				if (Z_TYPE_P(item) != IS_ARRAY) {
783					pdo_raise_impl_error(dbh, NULL, "HY000",
784						"PDO::ATTR_STATEMENT_CLASS requires format array(classname, array(ctor_args)); "
785						"ctor_args must be an array"
786					);
787					PDO_HANDLE_DBH_ERR();
788					return FAILURE;
789				}
790				ZVAL_COPY(&dbh->def_stmt_ctor_args, item);
791			}
792			return SUCCESS;
793		}
794
795		default:
796			;
797	}
798
799	if (!dbh->methods->set_attribute) {
800		goto fail;
801	}
802
803	PDO_DBH_CLEAR_ERR();
804	if (dbh->methods->set_attribute(dbh, attr, value)) {
805		return SUCCESS;
806	}
807
808fail:
809	if (attr == PDO_ATTR_AUTOCOMMIT) {
810		zend_throw_exception_ex(php_pdo_get_exception(), 0, "The auto-commit mode cannot be changed for this driver");
811	} else if (!dbh->methods->set_attribute) {
812		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support setting attributes");
813	} else {
814		PDO_HANDLE_DBH_ERR();
815	}
816	return FAILURE;
817}
818/* }}} */
819
820/* {{{ proto bool PDO::setAttribute(long attribute, mixed value)
821   Set an attribute */
822static PHP_METHOD(PDO, setAttribute)
823{
824	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
825	zend_long attr;
826	zval *value;
827
828	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "lz", &attr, &value)) {
829		RETURN_FALSE;
830	}
831
832	PDO_DBH_CLEAR_ERR();
833	PDO_CONSTRUCT_CHECK;
834
835	if (pdo_dbh_attribute_set(dbh, attr, value) != FAILURE) {
836 		RETURN_TRUE;
837 	}
838 	RETURN_FALSE;
839}
840/* }}} */
841
842/* {{{ proto mixed PDO::getAttribute(long attribute)
843   Get an attribute */
844static PHP_METHOD(PDO, getAttribute)
845{
846	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
847	zend_long attr;
848
849	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "l", &attr)) {
850		RETURN_FALSE;
851	}
852
853	PDO_DBH_CLEAR_ERR();
854	PDO_CONSTRUCT_CHECK;
855
856	/* handle generic PDO-level attributes */
857	switch (attr) {
858		case PDO_ATTR_PERSISTENT:
859			RETURN_BOOL(dbh->is_persistent);
860
861		case PDO_ATTR_CASE:
862			RETURN_LONG(dbh->desired_case);
863
864		case PDO_ATTR_ORACLE_NULLS:
865			RETURN_LONG(dbh->oracle_nulls);
866
867		case PDO_ATTR_ERRMODE:
868			RETURN_LONG(dbh->error_mode);
869
870		case PDO_ATTR_DRIVER_NAME:
871			RETURN_STRINGL((char*)dbh->driver->driver_name, dbh->driver->driver_name_len);
872
873		case PDO_ATTR_STATEMENT_CLASS:
874			array_init(return_value);
875			add_next_index_str(return_value, zend_string_copy(dbh->def_stmt_ce->name));
876			if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
877				if (Z_REFCOUNTED(dbh->def_stmt_ctor_args)) Z_ADDREF(dbh->def_stmt_ctor_args);
878				add_next_index_zval(return_value, &dbh->def_stmt_ctor_args);
879			}
880			return;
881		case PDO_ATTR_DEFAULT_FETCH_MODE:
882			RETURN_LONG(dbh->default_fetch_type);
883		default:
884			break;
885	}
886
887	if (!dbh->methods->get_attribute) {
888		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support getting attributes");
889		RETURN_FALSE;
890	}
891
892	switch (dbh->methods->get_attribute(dbh, attr, return_value)) {
893		case -1:
894			PDO_HANDLE_DBH_ERR();
895			RETURN_FALSE;
896
897		case 0:
898			pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support that attribute");
899			RETURN_FALSE;
900
901		default:
902			return;
903	}
904}
905/* }}} */
906
907/* {{{ proto long PDO::exec(string query)
908   Execute a query that does not return a row set, returning the number of affected rows */
909static PHP_METHOD(PDO, exec)
910{
911	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
912	char *statement;
913	size_t statement_len;
914	zend_long ret;
915
916	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &statement, &statement_len)) {
917		RETURN_FALSE;
918	}
919
920	if (!statement_len) {
921		pdo_raise_impl_error(dbh, NULL, "HY000",  "trying to execute an empty query");
922		RETURN_FALSE;
923	}
924	PDO_DBH_CLEAR_ERR();
925	PDO_CONSTRUCT_CHECK;
926	ret = dbh->methods->doer(dbh, statement, statement_len);
927	if(ret == -1) {
928		PDO_HANDLE_DBH_ERR();
929		RETURN_FALSE;
930	} else {
931		RETURN_LONG(ret);
932	}
933}
934/* }}} */
935
936/* {{{ proto string PDO::lastInsertId([string seqname])
937   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. */
938static PHP_METHOD(PDO, lastInsertId)
939{
940	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
941	char *name = NULL;
942	size_t namelen;
943
944	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &name, &namelen)) {
945		RETURN_FALSE;
946	}
947
948	PDO_DBH_CLEAR_ERR();
949	PDO_CONSTRUCT_CHECK;
950	if (!dbh->methods->last_id) {
951		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support lastInsertId()");
952		RETURN_FALSE;
953	} else {
954		size_t id_len;
955		char *id;
956		id = dbh->methods->last_id(dbh, name, &id_len);
957		if (!id) {
958			PDO_HANDLE_DBH_ERR();
959			RETURN_FALSE;
960		} else {
961			//??? use zend_string ?
962			RETVAL_STRINGL(id, id_len);
963			efree(id);
964		}
965	}
966}
967/* }}} */
968
969/* {{{ proto string PDO::errorCode()
970   Fetch the error code associated with the last operation on the database handle */
971static PHP_METHOD(PDO, errorCode)
972{
973	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
974
975	if (zend_parse_parameters_none() == FAILURE) {
976		return;
977	}
978	PDO_CONSTRUCT_CHECK;
979
980	if (dbh->query_stmt) {
981		RETURN_STRING(dbh->query_stmt->error_code);
982	}
983
984	if (dbh->error_code[0] == '\0') {
985		RETURN_NULL();
986	}
987
988	/**
989	 * Making sure that we fallback to the default implementation
990	 * if the dbh->error_code is not null.
991	 */
992	RETURN_STRING(dbh->error_code);
993}
994/* }}} */
995
996/* {{{ proto int PDO::errorInfo()
997   Fetch extended error information associated with the last operation on the database handle */
998static PHP_METHOD(PDO, errorInfo)
999{
1000	int error_count;
1001	int error_count_diff 	 = 0;
1002	int error_expected_count = 3;
1003
1004	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1005
1006	if (zend_parse_parameters_none() == FAILURE) {
1007		return;
1008	}
1009
1010	PDO_CONSTRUCT_CHECK;
1011
1012	array_init(return_value);
1013
1014	if (dbh->query_stmt) {
1015		add_next_index_string(return_value, dbh->query_stmt->error_code);
1016		if(!strncmp(dbh->query_stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1017	} else {
1018		add_next_index_string(return_value, dbh->error_code);
1019		if(!strncmp(dbh->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1020	}
1021
1022	if (dbh->methods->fetch_err) {
1023		dbh->methods->fetch_err(dbh, dbh->query_stmt, return_value);
1024	}
1025
1026fill_array:
1027	/**
1028	 * In order to be consistent, we have to make sure we add the good amount
1029	 * of nulls depending on the current number of elements. We make a simple
1030	 * difference and add the needed elements
1031	 */
1032	error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1033
1034	if (error_expected_count > error_count) {
1035		int current_index;
1036
1037		error_count_diff = error_expected_count - error_count;
1038		for (current_index = 0; current_index < error_count_diff; current_index++) {
1039			add_next_index_null(return_value);
1040		}
1041	}
1042}
1043/* }}} */
1044
1045/* {{{ proto object PDO::query(string sql [, PDOStatement::setFetchMode() args])
1046   Prepare and execute $sql; returns the statement object for iteration */
1047static PHP_METHOD(PDO, query)
1048{
1049	pdo_stmt_t *stmt;
1050	char *statement;
1051	size_t statement_len;
1052	pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(getThis());
1053	pdo_dbh_t *dbh = dbh_obj->inner;
1054
1055	/* Return a meaningful error when no parameters were passed */
1056	if (!ZEND_NUM_ARGS()) {
1057		zend_parse_parameters(0, "z|z", NULL, NULL);
1058		RETURN_FALSE;
1059	}
1060
1061	if (FAILURE == zend_parse_parameters(1, "s", &statement,
1062			&statement_len)) {
1063		RETURN_FALSE;
1064	}
1065
1066	PDO_DBH_CLEAR_ERR();
1067	PDO_CONSTRUCT_CHECK;
1068
1069	if (!pdo_stmt_instantiate(dbh, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args)) {
1070		if (EXPECTED(!EG(exception))) {
1071			pdo_raise_impl_error(dbh, NULL, "HY000", "failed to instantiate user supplied statement class");
1072		}
1073		return;
1074	}
1075	stmt = Z_PDO_STMT_P(return_value);
1076
1077	/* unconditionally keep this for later reference */
1078	stmt->query_string = estrndup(statement, statement_len);
1079	stmt->query_stringlen = statement_len;
1080
1081	stmt->default_fetch_type = dbh->default_fetch_type;
1082	stmt->active_query_string = stmt->query_string;
1083	stmt->active_query_stringlen = statement_len;
1084	stmt->dbh = dbh;
1085	/* give it a reference to me */
1086	ZVAL_OBJ(&stmt->database_object_handle, &dbh_obj->std);
1087	Z_ADDREF(stmt->database_object_handle);
1088	/* we haven't created a lazy object yet */
1089	ZVAL_UNDEF(&stmt->lazy_object_ref);
1090
1091	if (dbh->methods->preparer(dbh, statement, statement_len, stmt, NULL)) {
1092		PDO_STMT_CLEAR_ERR();
1093		if (ZEND_NUM_ARGS() == 1 || SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, 1)) {
1094
1095			/* now execute the statement */
1096			PDO_STMT_CLEAR_ERR();
1097			if (stmt->methods->executer(stmt)) {
1098				int ret = 1;
1099				if (!stmt->executed) {
1100					if (stmt->dbh->alloc_own_columns) {
1101						ret = pdo_stmt_describe_columns(stmt);
1102					}
1103					stmt->executed = 1;
1104				}
1105				if (ret) {
1106					pdo_stmt_construct(execute_data, stmt, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args);
1107					return;
1108				}
1109			}
1110		}
1111		/* something broke */
1112		dbh->query_stmt = stmt;
1113		ZVAL_COPY_VALUE(&dbh->query_stmt_zval, return_value);
1114		Z_DELREF(stmt->database_object_handle);
1115		ZVAL_UNDEF(&stmt->database_object_handle);
1116		PDO_HANDLE_STMT_ERR();
1117	} else {
1118		PDO_HANDLE_DBH_ERR();
1119		zval_ptr_dtor(return_value);
1120	}
1121
1122	RETURN_FALSE;
1123}
1124/* }}} */
1125
1126/* {{{ proto string PDO::quote(string string [, int paramtype])
1127   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 */
1128static PHP_METHOD(PDO, quote)
1129{
1130	pdo_dbh_t *dbh = Z_PDO_DBH_P(getThis());
1131	char *str;
1132	size_t str_len;
1133	zend_long paramtype = PDO_PARAM_STR;
1134	char *qstr;
1135	size_t qlen;
1136
1137	if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s|l", &str, &str_len, &paramtype)) {
1138		RETURN_FALSE;
1139	}
1140
1141	PDO_DBH_CLEAR_ERR();
1142	PDO_CONSTRUCT_CHECK;
1143	if (!dbh->methods->quoter) {
1144		pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting");
1145		RETURN_FALSE;
1146	}
1147
1148	if (dbh->methods->quoter(dbh, str, str_len, &qstr, &qlen, paramtype)) {
1149		RETVAL_STRINGL(qstr, qlen);
1150		efree(qstr);
1151		return;
1152	}
1153	PDO_HANDLE_DBH_ERR();
1154	RETURN_FALSE;
1155}
1156/* }}} */
1157
1158/* {{{ proto int PDO::__wakeup()
1159   Prevents use of a PDO instance that has been unserialized */
1160static PHP_METHOD(PDO, __wakeup)
1161{
1162	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDO instances");
1163}
1164/* }}} */
1165
1166/* {{{ proto int PDO::__sleep()
1167   Prevents serialization of a PDO instance */
1168static PHP_METHOD(PDO, __sleep)
1169{
1170	zend_throw_exception_ex(php_pdo_get_exception(), 0, "You cannot serialize or unserialize PDO instances");
1171}
1172/* }}} */
1173
1174/* {{{ proto array PDO::getAvailableDrivers()
1175   Return array of available PDO drivers */
1176static PHP_METHOD(PDO, getAvailableDrivers)
1177{
1178	pdo_driver_t *pdriver;
1179
1180	if (zend_parse_parameters_none() == FAILURE) {
1181		return;
1182	}
1183
1184	array_init(return_value);
1185
1186	ZEND_HASH_FOREACH_PTR(&pdo_driver_hash, pdriver) {
1187		add_next_index_stringl(return_value, (char*)pdriver->driver_name, pdriver->driver_name_len);
1188	} ZEND_HASH_FOREACH_END();
1189}
1190/* }}} */
1191
1192/* {{{ arginfo */
1193ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo___construct, 0, 0, 1)
1194	ZEND_ARG_INFO(0, dsn)
1195	ZEND_ARG_INFO(0, username)
1196	ZEND_ARG_INFO(0, passwd)
1197	ZEND_ARG_INFO(0, options) /* array */
1198ZEND_END_ARG_INFO()
1199
1200ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_prepare, 0, 0, 1)
1201	ZEND_ARG_INFO(0, statement)
1202	ZEND_ARG_INFO(0, options) /* array */
1203ZEND_END_ARG_INFO()
1204
1205ZEND_BEGIN_ARG_INFO(arginfo_pdo_setattribute, 0)
1206	ZEND_ARG_INFO(0, attribute)
1207	ZEND_ARG_INFO(0, value)
1208ZEND_END_ARG_INFO()
1209
1210ZEND_BEGIN_ARG_INFO(arginfo_pdo_getattribute, 0)
1211	ZEND_ARG_INFO(0, attribute)
1212ZEND_END_ARG_INFO()
1213
1214ZEND_BEGIN_ARG_INFO(arginfo_pdo_exec, 0)
1215	ZEND_ARG_INFO(0, query)
1216ZEND_END_ARG_INFO()
1217
1218ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_lastinsertid, 0, 0, 0)
1219	ZEND_ARG_INFO(0, seqname)
1220ZEND_END_ARG_INFO()
1221
1222ZEND_BEGIN_ARG_INFO_EX(arginfo_pdo_quote, 0, 0, 1)
1223	ZEND_ARG_INFO(0, string)
1224	ZEND_ARG_INFO(0, paramtype)
1225ZEND_END_ARG_INFO()
1226
1227ZEND_BEGIN_ARG_INFO(arginfo_pdo__void, 0)
1228ZEND_END_ARG_INFO()
1229/* }}} */
1230
1231const zend_function_entry pdo_dbh_functions[] = /* {{{ */ {
1232	ZEND_MALIAS(PDO, __construct, dbh_constructor,	arginfo_pdo___construct,	ZEND_ACC_PUBLIC)
1233	PHP_ME(PDO, prepare, 				arginfo_pdo_prepare,		ZEND_ACC_PUBLIC)
1234	PHP_ME(PDO, beginTransaction,       arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1235	PHP_ME(PDO, commit,                 arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1236	PHP_ME(PDO, rollBack,               arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1237	PHP_ME(PDO, inTransaction,          arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1238	PHP_ME(PDO, setAttribute,	arginfo_pdo_setattribute,	ZEND_ACC_PUBLIC)
1239	PHP_ME(PDO, exec,			arginfo_pdo_exec,		ZEND_ACC_PUBLIC)
1240	PHP_ME(PDO, query,			NULL,					ZEND_ACC_PUBLIC)
1241	PHP_ME(PDO, lastInsertId,	arginfo_pdo_lastinsertid,	ZEND_ACC_PUBLIC)
1242	PHP_ME(PDO, errorCode,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1243	PHP_ME(PDO, errorInfo,              arginfo_pdo__void,         ZEND_ACC_PUBLIC)
1244	PHP_ME(PDO, getAttribute,	arginfo_pdo_getattribute,	ZEND_ACC_PUBLIC)
1245	PHP_ME(PDO, quote,			arginfo_pdo_quote,		ZEND_ACC_PUBLIC)
1246	PHP_ME(PDO, __wakeup,               arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1247	PHP_ME(PDO, __sleep,                arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
1248	PHP_ME(PDO, getAvailableDrivers,    arginfo_pdo__void,         ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
1249	{NULL, NULL, NULL}
1250};
1251/* }}} */
1252
1253static void cls_method_dtor(zval *el) /* {{{ */ {
1254	zend_function *func = (zend_function*)Z_PTR_P(el);
1255	if (func->common.function_name) {
1256		zend_string_release(func->common.function_name);
1257	}
1258	efree(func);
1259}
1260/* }}} */
1261
1262static void cls_method_pdtor(zval *el) /* {{{ */ {
1263	zend_function *func = (zend_function*)Z_PTR_P(el);
1264	if (func->common.function_name) {
1265		zend_string_release(func->common.function_name);
1266	}
1267	pefree(func, 1);
1268}
1269/* }}} */
1270
1271/* {{{ overloaded object handlers for PDO class */
1272int pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
1273{
1274	const zend_function_entry *funcs;
1275	zend_internal_function func;
1276	size_t namelen;
1277	char *lc_name;
1278	pdo_dbh_t *dbh = dbh_obj->inner;
1279
1280	if (!dbh || !dbh->methods || !dbh->methods->get_driver_methods) {
1281		return 0;
1282	}
1283	funcs =	dbh->methods->get_driver_methods(dbh, kind);
1284	if (!funcs) {
1285		return 0;
1286	}
1287
1288	if (!(dbh->cls_methods[kind] = pemalloc(sizeof(HashTable), dbh->is_persistent))) {
1289		php_error_docref(NULL, E_ERROR, "out of memory while allocating PDO methods.");
1290	}
1291	zend_hash_init_ex(dbh->cls_methods[kind], 8, NULL,
1292			dbh->is_persistent? cls_method_pdtor : cls_method_dtor, dbh->is_persistent, 0);
1293
1294	memset(&func, 0, sizeof(func));
1295
1296	while (funcs->fname) {
1297		func.type = ZEND_INTERNAL_FUNCTION;
1298		func.handler = funcs->handler;
1299		func.function_name = zend_string_init(funcs->fname, strlen(funcs->fname), dbh->is_persistent);
1300		func.scope = dbh_obj->std.ce;
1301		func.prototype = NULL;
1302		if (funcs->flags) {
1303			func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
1304		} else {
1305			func.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
1306		}
1307		if (funcs->arg_info) {
1308			zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
1309
1310			func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
1311			func.num_args = funcs->num_args;
1312			if (info->required_num_args == -1) {
1313				func.required_num_args = funcs->num_args;
1314			} else {
1315				func.required_num_args = info->required_num_args;
1316			}
1317			if (info->return_reference) {
1318				func.fn_flags |= ZEND_ACC_RETURN_REFERENCE;
1319			}
1320			if (funcs->arg_info[funcs->num_args].is_variadic) {
1321				func.fn_flags |= ZEND_ACC_VARIADIC;
1322				/* Don't count the variadic argument */
1323				func.num_args--;
1324			}
1325		} else {
1326			func.arg_info = NULL;
1327			func.num_args = 0;
1328			func.required_num_args = 0;
1329		}
1330		zend_set_function_arg_flags((zend_function*)&func);
1331		namelen = strlen(funcs->fname);
1332		lc_name = emalloc(namelen+1);
1333		zend_str_tolower_copy(lc_name, funcs->fname, namelen);
1334		zend_hash_str_add_mem(dbh->cls_methods[kind], lc_name, namelen, &func, sizeof(func));
1335		efree(lc_name);
1336		funcs++;
1337	}
1338
1339	return 1;
1340}
1341
1342static union _zend_function *dbh_method_get(zend_object **object, zend_string *method_name, const zval *key)
1343{
1344	zend_function *fbc = NULL;
1345	pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object);
1346	zend_string *lc_method_name;
1347
1348	lc_method_name = zend_string_init(ZSTR_VAL(method_name), ZSTR_LEN(method_name), 0);
1349	zend_str_tolower_copy(ZSTR_VAL(lc_method_name), ZSTR_VAL(method_name), ZSTR_LEN(method_name));
1350
1351	if ((fbc = std_object_handlers.get_method(object, method_name, key)) == NULL) {
1352		/* not a pre-defined method, nor a user-defined method; check
1353		 * the driver specific methods */
1354		if (!dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1355			if (!pdo_hash_methods(dbh_obj,
1356				PDO_DBH_DRIVER_METHOD_KIND_DBH)
1357				|| !dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1358				goto out;
1359			}
1360		}
1361
1362		fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name);
1363	}
1364
1365out:
1366	zend_string_release(lc_method_name);
1367	return fbc;
1368}
1369
1370static int dbh_compare(zval *object1, zval *object2)
1371{
1372	return -1;
1373}
1374
1375static HashTable *dbh_get_gc(zval *object, zval **gc_data, int *gc_count)
1376{
1377	pdo_dbh_t *dbh = Z_PDO_DBH_P(object);
1378	*gc_data = &dbh->def_stmt_ctor_args;
1379	*gc_count = 1;
1380	return zend_std_get_properties(object);
1381}
1382
1383static zend_object_handlers pdo_dbh_object_handlers;
1384static void pdo_dbh_free_storage(zend_object *std);
1385
1386void pdo_dbh_init(void)
1387{
1388	zend_class_entry ce;
1389
1390	INIT_CLASS_ENTRY(ce, "PDO", pdo_dbh_functions);
1391	pdo_dbh_ce = zend_register_internal_class(&ce);
1392	pdo_dbh_ce->create_object = pdo_dbh_new;
1393
1394	memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1395	pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std);
1396	pdo_dbh_object_handlers.dtor_obj = zend_objects_destroy_object;
1397	pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage;
1398	pdo_dbh_object_handlers.get_method = dbh_method_get;
1399	pdo_dbh_object_handlers.compare_objects = dbh_compare;
1400	pdo_dbh_object_handlers.get_gc = dbh_get_gc;
1401
1402	REGISTER_PDO_CLASS_CONST_LONG("PARAM_BOOL", (zend_long)PDO_PARAM_BOOL);
1403	REGISTER_PDO_CLASS_CONST_LONG("PARAM_NULL", (zend_long)PDO_PARAM_NULL);
1404	REGISTER_PDO_CLASS_CONST_LONG("PARAM_INT",  (zend_long)PDO_PARAM_INT);
1405	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STR",  (zend_long)PDO_PARAM_STR);
1406	REGISTER_PDO_CLASS_CONST_LONG("PARAM_LOB",  (zend_long)PDO_PARAM_LOB);
1407	REGISTER_PDO_CLASS_CONST_LONG("PARAM_STMT", (zend_long)PDO_PARAM_STMT);
1408	REGISTER_PDO_CLASS_CONST_LONG("PARAM_INPUT_OUTPUT", (zend_long)PDO_PARAM_INPUT_OUTPUT);
1409
1410	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_ALLOC",		(zend_long)PDO_PARAM_EVT_ALLOC);
1411	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FREE",			(zend_long)PDO_PARAM_EVT_FREE);
1412	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_PRE",		(zend_long)PDO_PARAM_EVT_EXEC_PRE);
1413	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_EXEC_POST",	(zend_long)PDO_PARAM_EVT_EXEC_POST);
1414	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_PRE",	(zend_long)PDO_PARAM_EVT_FETCH_PRE);
1415	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_FETCH_POST",	(zend_long)PDO_PARAM_EVT_FETCH_POST);
1416	REGISTER_PDO_CLASS_CONST_LONG("PARAM_EVT_NORMALIZE",	(zend_long)PDO_PARAM_EVT_NORMALIZE);
1417
1418	REGISTER_PDO_CLASS_CONST_LONG("FETCH_LAZY", (zend_long)PDO_FETCH_LAZY);
1419	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ASSOC", (zend_long)PDO_FETCH_ASSOC);
1420	REGISTER_PDO_CLASS_CONST_LONG("FETCH_NUM",  (zend_long)PDO_FETCH_NUM);
1421	REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOTH", (zend_long)PDO_FETCH_BOTH);
1422	REGISTER_PDO_CLASS_CONST_LONG("FETCH_OBJ",  (zend_long)PDO_FETCH_OBJ);
1423	REGISTER_PDO_CLASS_CONST_LONG("FETCH_BOUND", (zend_long)PDO_FETCH_BOUND);
1424	REGISTER_PDO_CLASS_CONST_LONG("FETCH_COLUMN", (zend_long)PDO_FETCH_COLUMN);
1425	REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASS", (zend_long)PDO_FETCH_CLASS);
1426	REGISTER_PDO_CLASS_CONST_LONG("FETCH_INTO", (zend_long)PDO_FETCH_INTO);
1427	REGISTER_PDO_CLASS_CONST_LONG("FETCH_FUNC", (zend_long)PDO_FETCH_FUNC);
1428	REGISTER_PDO_CLASS_CONST_LONG("FETCH_GROUP", (zend_long)PDO_FETCH_GROUP);
1429	REGISTER_PDO_CLASS_CONST_LONG("FETCH_UNIQUE", (zend_long)PDO_FETCH_UNIQUE);
1430	REGISTER_PDO_CLASS_CONST_LONG("FETCH_KEY_PAIR", (zend_long)PDO_FETCH_KEY_PAIR);
1431	REGISTER_PDO_CLASS_CONST_LONG("FETCH_CLASSTYPE", (zend_long)PDO_FETCH_CLASSTYPE);
1432
1433	REGISTER_PDO_CLASS_CONST_LONG("FETCH_SERIALIZE",(zend_long)PDO_FETCH_SERIALIZE);
1434	REGISTER_PDO_CLASS_CONST_LONG("FETCH_PROPS_LATE", (zend_long)PDO_FETCH_PROPS_LATE);
1435	REGISTER_PDO_CLASS_CONST_LONG("FETCH_NAMED", (zend_long)PDO_FETCH_NAMED);
1436
1437	REGISTER_PDO_CLASS_CONST_LONG("ATTR_AUTOCOMMIT",	(zend_long)PDO_ATTR_AUTOCOMMIT);
1438	REGISTER_PDO_CLASS_CONST_LONG("ATTR_PREFETCH",		(zend_long)PDO_ATTR_PREFETCH);
1439	REGISTER_PDO_CLASS_CONST_LONG("ATTR_TIMEOUT", 		(zend_long)PDO_ATTR_TIMEOUT);
1440	REGISTER_PDO_CLASS_CONST_LONG("ATTR_ERRMODE", 		(zend_long)PDO_ATTR_ERRMODE);
1441	REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_VERSION",	(zend_long)PDO_ATTR_SERVER_VERSION);
1442	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CLIENT_VERSION", 	(zend_long)PDO_ATTR_CLIENT_VERSION);
1443	REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_INFO",		(zend_long)PDO_ATTR_SERVER_INFO);
1444	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CONNECTION_STATUS", 	(zend_long)PDO_ATTR_CONNECTION_STATUS);
1445	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CASE",		 	(zend_long)PDO_ATTR_CASE);
1446	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR_NAME", 	(zend_long)PDO_ATTR_CURSOR_NAME);
1447	REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR",	 	(zend_long)PDO_ATTR_CURSOR);
1448	REGISTER_PDO_CLASS_CONST_LONG("ATTR_ORACLE_NULLS",	(zend_long)PDO_ATTR_ORACLE_NULLS);
1449	REGISTER_PDO_CLASS_CONST_LONG("ATTR_PERSISTENT",	(zend_long)PDO_ATTR_PERSISTENT);
1450	REGISTER_PDO_CLASS_CONST_LONG("ATTR_STATEMENT_CLASS",		(zend_long)PDO_ATTR_STATEMENT_CLASS);
1451	REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_TABLE_NAMES",		(zend_long)PDO_ATTR_FETCH_TABLE_NAMES);
1452	REGISTER_PDO_CLASS_CONST_LONG("ATTR_FETCH_CATALOG_NAMES",		(zend_long)PDO_ATTR_FETCH_CATALOG_NAMES);
1453	REGISTER_PDO_CLASS_CONST_LONG("ATTR_DRIVER_NAME",		(zend_long)PDO_ATTR_DRIVER_NAME);
1454	REGISTER_PDO_CLASS_CONST_LONG("ATTR_STRINGIFY_FETCHES", (zend_long)PDO_ATTR_STRINGIFY_FETCHES);
1455	REGISTER_PDO_CLASS_CONST_LONG("ATTR_MAX_COLUMN_LEN", (zend_long)PDO_ATTR_MAX_COLUMN_LEN);
1456	REGISTER_PDO_CLASS_CONST_LONG("ATTR_EMULATE_PREPARES", (zend_long)PDO_ATTR_EMULATE_PREPARES);
1457	REGISTER_PDO_CLASS_CONST_LONG("ATTR_DEFAULT_FETCH_MODE", (zend_long)PDO_ATTR_DEFAULT_FETCH_MODE);
1458
1459	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_SILENT",	(zend_long)PDO_ERRMODE_SILENT);
1460	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_WARNING",	(zend_long)PDO_ERRMODE_WARNING);
1461	REGISTER_PDO_CLASS_CONST_LONG("ERRMODE_EXCEPTION",	(zend_long)PDO_ERRMODE_EXCEPTION);
1462
1463	REGISTER_PDO_CLASS_CONST_LONG("CASE_NATURAL",	(zend_long)PDO_CASE_NATURAL);
1464	REGISTER_PDO_CLASS_CONST_LONG("CASE_LOWER",	(zend_long)PDO_CASE_LOWER);
1465	REGISTER_PDO_CLASS_CONST_LONG("CASE_UPPER",	(zend_long)PDO_CASE_UPPER);
1466
1467	REGISTER_PDO_CLASS_CONST_LONG("NULL_NATURAL",	(zend_long)PDO_NULL_NATURAL);
1468	REGISTER_PDO_CLASS_CONST_LONG("NULL_EMPTY_STRING",	(zend_long)PDO_NULL_EMPTY_STRING);
1469	REGISTER_PDO_CLASS_CONST_LONG("NULL_TO_STRING",	(zend_long)PDO_NULL_TO_STRING);
1470
1471	REGISTER_PDO_CLASS_CONST_STRING("ERR_NONE",	PDO_ERR_NONE);
1472
1473	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_NEXT", (zend_long)PDO_FETCH_ORI_NEXT);
1474	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_PRIOR", (zend_long)PDO_FETCH_ORI_PRIOR);
1475	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_FIRST", (zend_long)PDO_FETCH_ORI_FIRST);
1476	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_LAST", (zend_long)PDO_FETCH_ORI_LAST);
1477	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_ABS", (zend_long)PDO_FETCH_ORI_ABS);
1478	REGISTER_PDO_CLASS_CONST_LONG("FETCH_ORI_REL", (zend_long)PDO_FETCH_ORI_REL);
1479
1480	REGISTER_PDO_CLASS_CONST_LONG("CURSOR_FWDONLY", (zend_long)PDO_CURSOR_FWDONLY);
1481	REGISTER_PDO_CLASS_CONST_LONG("CURSOR_SCROLL", (zend_long)PDO_CURSOR_SCROLL);
1482
1483#if 0
1484	REGISTER_PDO_CLASS_CONST_LONG("ERR_CANT_MAP", 		(zend_long)PDO_ERR_CANT_MAP);
1485	REGISTER_PDO_CLASS_CONST_LONG("ERR_SYNTAX", 		(zend_long)PDO_ERR_SYNTAX);
1486	REGISTER_PDO_CLASS_CONST_LONG("ERR_CONSTRAINT", 	(zend_long)PDO_ERR_CONSTRAINT);
1487	REGISTER_PDO_CLASS_CONST_LONG("ERR_NOT_FOUND", 		(zend_long)PDO_ERR_NOT_FOUND);
1488	REGISTER_PDO_CLASS_CONST_LONG("ERR_ALREADY_EXISTS", 	(zend_long)PDO_ERR_ALREADY_EXISTS);
1489	REGISTER_PDO_CLASS_CONST_LONG("ERR_NOT_IMPLEMENTED", 	(zend_long)PDO_ERR_NOT_IMPLEMENTED);
1490	REGISTER_PDO_CLASS_CONST_LONG("ERR_MISMATCH", 		(zend_long)PDO_ERR_MISMATCH);
1491	REGISTER_PDO_CLASS_CONST_LONG("ERR_TRUNCATED", 		(zend_long)PDO_ERR_TRUNCATED);
1492	REGISTER_PDO_CLASS_CONST_LONG("ERR_DISCONNECTED", 	(zend_long)PDO_ERR_DISCONNECTED);
1493	REGISTER_PDO_CLASS_CONST_LONG("ERR_NO_PERM",		(zend_long)PDO_ERR_NO_PERM);
1494#endif
1495
1496}
1497
1498static void dbh_free(pdo_dbh_t *dbh, zend_bool free_persistent)
1499{
1500	int i;
1501
1502	if (dbh->is_persistent && !free_persistent) {
1503		return;
1504	}
1505
1506	if (dbh->query_stmt) {
1507		zval_ptr_dtor(&dbh->query_stmt_zval);
1508		dbh->query_stmt = NULL;
1509	}
1510
1511	if (dbh->methods) {
1512		dbh->methods->closer(dbh);
1513	}
1514
1515	if (dbh->data_source) {
1516		pefree((char *)dbh->data_source, dbh->is_persistent);
1517	}
1518	if (dbh->username) {
1519		pefree(dbh->username, dbh->is_persistent);
1520	}
1521	if (dbh->password) {
1522		pefree(dbh->password, dbh->is_persistent);
1523	}
1524
1525	if (dbh->persistent_id) {
1526		pefree((char *)dbh->persistent_id, dbh->is_persistent);
1527	}
1528
1529	if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
1530		zval_ptr_dtor(&dbh->def_stmt_ctor_args);
1531	}
1532
1533	for (i = 0; i < PDO_DBH_DRIVER_METHOD_KIND__MAX; i++) {
1534		if (dbh->cls_methods[i]) {
1535			zend_hash_destroy(dbh->cls_methods[i]);
1536			pefree(dbh->cls_methods[i], dbh->is_persistent);
1537		}
1538	}
1539
1540	pefree(dbh, dbh->is_persistent);
1541}
1542
1543static void pdo_dbh_free_storage(zend_object *std)
1544{
1545	pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std);
1546	if (dbh->in_txn && dbh->methods && dbh->methods->rollback) {
1547		dbh->methods->rollback(dbh);
1548		dbh->in_txn = 0;
1549	}
1550
1551	if (dbh->is_persistent && dbh->methods && dbh->methods->persistent_shutdown) {
1552		dbh->methods->persistent_shutdown(dbh);
1553	}
1554	zend_object_std_dtor(std);
1555	dbh_free(dbh, 0);
1556}
1557
1558zend_object *pdo_dbh_new(zend_class_entry *ce)
1559{
1560	pdo_dbh_object_t *dbh;
1561
1562	dbh = ecalloc(1, sizeof(pdo_dbh_object_t) + zend_object_properties_size(ce));
1563	zend_object_std_init(&dbh->std, ce);
1564	object_properties_init(&dbh->std, ce);
1565	rebuild_object_properties(&dbh->std);
1566	dbh->inner = ecalloc(1, sizeof(pdo_dbh_t));
1567	dbh->inner->def_stmt_ce = pdo_dbstmt_ce;
1568
1569	dbh->std.handlers = &pdo_dbh_object_handlers;
1570
1571	return &dbh->std;
1572}
1573
1574/* }}} */
1575
1576ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor) /* {{{ */
1577{
1578	if (res->ptr) {
1579		pdo_dbh_t *dbh = (pdo_dbh_t*)res->ptr;
1580		dbh_free(dbh, 1);
1581		res->ptr = NULL;
1582	}
1583}
1584/* }}} */
1585
1586/*
1587 * Local variables:
1588 * tab-width: 4
1589 * c-basic-offset: 4
1590 * End:
1591 * vim600: noet sw=4 ts=4 fdm=marker
1592 * vim<600: noet sw=4 ts=4
1593 */
1594