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