1/*
2  +----------------------------------------------------------------------+
3  | PHP Version 5                                                        |
4  +----------------------------------------------------------------------+
5  | Copyright (c) 1997-2013 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#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include <ctype.h>
28#include "php.h"
29#include "php_ini.h"
30#include "ext/standard/info.h"
31#include "php_pdo.h"
32#include "php_pdo_driver.h"
33#include "php_pdo_int.h"
34#include "zend_exceptions.h"
35
36static zend_class_entry *spl_ce_RuntimeException;
37
38ZEND_DECLARE_MODULE_GLOBALS(pdo)
39static PHP_GINIT_FUNCTION(pdo);
40
41/* True global resources - no need for thread safety here */
42
43/* the registry of PDO drivers */
44HashTable pdo_driver_hash;
45
46/* we use persistent resources for the driver connection stuff */
47static int le_ppdo;
48
49int php_pdo_list_entry(void)
50{
51    return le_ppdo;
52}
53
54/* for exceptional circumstances */
55zend_class_entry *pdo_exception_ce;
56
57PDO_API zend_class_entry *php_pdo_get_dbh_ce(void)
58{
59    return pdo_dbh_ce;
60}
61
62PDO_API zend_class_entry *php_pdo_get_exception(void)
63{
64    return pdo_exception_ce;
65}
66
67PDO_API char *php_pdo_str_tolower_dup(const char *src, int len)
68{
69    char *dest = emalloc(len + 1);
70    zend_str_tolower_copy(dest, src, len);
71    return dest;
72}
73
74PDO_API zend_class_entry *php_pdo_get_exception_base(int root TSRMLS_DC)
75{
76#if can_handle_soft_dependency_on_SPL && defined(HAVE_SPL) && ((PHP_MAJOR_VERSION > 5) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 1))
77    if (!root) {
78        if (!spl_ce_RuntimeException) {
79            zend_class_entry **pce;
80
81            if (zend_hash_find(CG(class_table), "runtimeexception", sizeof("RuntimeException"), (void **) &pce) == SUCCESS) {
82                spl_ce_RuntimeException = *pce;
83                return *pce;
84            }
85        } else {
86            return spl_ce_RuntimeException;
87        }
88    }
89#endif
90#if (PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION < 2)
91    return zend_exception_get_default();
92#else
93    return zend_exception_get_default(TSRMLS_C);
94#endif
95}
96
97zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce;
98
99/* {{{ proto array pdo_drivers()
100 Return array of available PDO drivers */
101PHP_FUNCTION(pdo_drivers)
102{
103    HashPosition pos;
104    pdo_driver_t **pdriver;
105
106    if (zend_parse_parameters_none() == FAILURE) {
107        return;
108    }
109
110    array_init(return_value);
111
112    zend_hash_internal_pointer_reset_ex(&pdo_driver_hash, &pos);
113    while (SUCCESS == zend_hash_get_current_data_ex(&pdo_driver_hash, (void**)&pdriver, &pos)) {
114        add_next_index_stringl(return_value, (char*)(*pdriver)->driver_name, (*pdriver)->driver_name_len, 1);
115        zend_hash_move_forward_ex(&pdo_driver_hash, &pos);
116    }
117}
118/* }}} */
119
120/* {{{ arginfo */
121ZEND_BEGIN_ARG_INFO(arginfo_pdo_drivers, 0)
122ZEND_END_ARG_INFO()
123/* }}} */
124
125/* {{{ pdo_functions[] */
126const zend_function_entry pdo_functions[] = {
127    PHP_FE(pdo_drivers,             arginfo_pdo_drivers)
128    PHP_FE_END
129};
130/* }}} */
131
132/* {{{ pdo_functions[] */
133#if ZEND_MODULE_API_NO >= 20050922
134static const zend_module_dep pdo_deps[] = {
135#ifdef HAVE_SPL
136    ZEND_MOD_REQUIRED("spl")
137#endif
138    ZEND_MOD_END
139};
140#endif
141/* }}} */
142
143/* {{{ pdo_module_entry */
144zend_module_entry pdo_module_entry = {
145#if ZEND_MODULE_API_NO >= 20050922
146    STANDARD_MODULE_HEADER_EX, NULL,
147    pdo_deps,
148#else
149    STANDARD_MODULE_HEADER,
150#endif
151    "PDO",
152    pdo_functions,
153    PHP_MINIT(pdo),
154    PHP_MSHUTDOWN(pdo),
155    NULL,
156    NULL,
157    PHP_MINFO(pdo),
158    "1.0.4dev",
159    PHP_MODULE_GLOBALS(pdo),
160    PHP_GINIT(pdo),
161    NULL,
162    NULL,
163    STANDARD_MODULE_PROPERTIES_EX
164};
165/* }}} */
166
167/* TODO: visit persistent handles: for each persistent statement handle,
168 * remove bound parameter associations */
169
170#ifdef COMPILE_DL_PDO
171ZEND_GET_MODULE(pdo)
172#endif
173
174/* {{{ PHP_GINIT_FUNCTION */
175static PHP_GINIT_FUNCTION(pdo)
176{
177    pdo_globals->global_value = 0;
178}
179/* }}} */
180
181PDO_API int php_pdo_register_driver(pdo_driver_t *driver)
182{
183    if (driver->api_version != PDO_DRIVER_API) {
184        zend_error(E_ERROR, "PDO: driver %s requires PDO API version %ld; this is PDO version %d",
185            driver->driver_name, driver->api_version, PDO_DRIVER_API);
186        return FAILURE;
187    }
188    if (!zend_hash_exists(&module_registry, "pdo", sizeof("pdo"))) {
189        zend_error(E_ERROR, "You MUST load PDO before loading any PDO drivers");
190        return FAILURE; /* NOTREACHED */
191    }
192
193    return zend_hash_add(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len,
194            (void**)&driver, sizeof(pdo_driver_t *), NULL);
195}
196
197PDO_API void php_pdo_unregister_driver(pdo_driver_t *driver)
198{
199    if (!zend_hash_exists(&module_registry, "pdo", sizeof("pdo"))) {
200        return;
201    }
202
203    zend_hash_del(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len);
204}
205
206pdo_driver_t *pdo_find_driver(const char *name, int namelen)
207{
208    pdo_driver_t **driver = NULL;
209
210    zend_hash_find(&pdo_driver_hash, (char*)name, namelen, (void**)&driver);
211
212    return driver ? *driver : NULL;
213}
214
215PDO_API int php_pdo_parse_data_source(const char *data_source,
216        unsigned long data_source_len, struct pdo_data_src_parser *parsed,
217        int nparams)
218{
219    int i, j;
220    int valstart = -1;
221    int semi = -1;
222    int optstart = 0;
223    int nlen;
224    int n_matches = 0;
225    int n_semicolumns = 0;
226
227    i = 0;
228    while (i < data_source_len) {
229        /* looking for NAME= */
230
231        if (data_source[i] == '\0') {
232            break;
233        }
234
235        if (data_source[i] != '=') {
236            ++i;
237            continue;
238        }
239
240        valstart = ++i;
241
242        /* now we're looking for VALUE; or just VALUE<NUL> */
243        semi = -1;
244        n_semicolumns = 0;
245        while (i < data_source_len) {
246            if (data_source[i] == '\0') {
247                semi = i++;
248                break;
249            }
250            if (data_source[i] == ';') {
251                if ((i + 1 >= data_source_len) || data_source[i+1] != ';') {
252                    semi = i++;
253                    break;
254                } else {
255                    n_semicolumns++;
256                    i += 2;
257                    continue;
258                }
259            }
260            ++i;
261        }
262
263        if (semi == -1) {
264            semi = i;
265        }
266
267        /* find the entry in the array */
268        nlen = valstart - optstart - 1;
269        for (j = 0; j < nparams; j++) {
270            if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) && parsed[j].optname[nlen] == '\0') {
271                /* got a match */
272                if (parsed[j].freeme) {
273                    efree(parsed[j].optval);
274                }
275
276                if (n_semicolumns == 0) {
277                    parsed[j].optval = estrndup(data_source + valstart, semi - valstart - n_semicolumns);
278                } else {
279                    int vlen = semi - valstart;
280                    char *orig_val = data_source + valstart;
281                    char *new_val  = (char *) emalloc(vlen - n_semicolumns + 1);
282
283                    parsed[j].optval = new_val;
284
285                    while (vlen && *orig_val) {
286                        *new_val = *orig_val;
287                        new_val++;
288
289                        if (*orig_val == ';') {
290                            orig_val+=2;
291                            vlen-=2;
292                        } else {
293                            orig_val++;
294                            vlen--;
295                        }
296                    }
297                    *new_val = '\0';
298                }
299
300                parsed[j].freeme = 1;
301                ++n_matches;
302                break;
303            }
304        }
305
306        while (i < data_source_len && isspace(data_source[i])) {
307            i++;
308        }
309
310        optstart = i;
311    }
312
313    return n_matches;
314}
315
316static const char digit_vec[] = "0123456789";
317PDO_API char *php_pdo_int64_to_str(pdo_int64_t i64 TSRMLS_DC)
318{
319    char buffer[65];
320    char outbuf[65] = "";
321    register char *p;
322    long long_val;
323    char *dst = outbuf;
324
325    if (i64 < 0) {
326        i64 = -i64;
327        *dst++ = '-';
328    }
329
330    if (i64 == 0) {
331        *dst++ = '0';
332        *dst++ = '\0';
333        return estrdup(outbuf);
334    }
335
336    p = &buffer[sizeof(buffer)-1];
337    *p = '\0';
338
339    while ((pdo_uint64_t)i64 > (pdo_uint64_t)LONG_MAX) {
340        pdo_uint64_t quo = (pdo_uint64_t)i64 / (unsigned int)10;
341        unsigned int rem = (unsigned int)(i64 - quo*10U);
342        *--p = digit_vec[rem];
343        i64 = (pdo_int64_t)quo;
344    }
345    long_val = (long)i64;
346    while (long_val != 0) {
347        long quo = long_val / 10;
348        *--p = digit_vec[(unsigned int)(long_val - quo * 10)];
349        long_val = quo;
350    }
351    while ((*dst++ = *p++) != 0)
352        ;
353    *dst = '\0';
354    return estrdup(outbuf);
355}
356
357/* {{{ PHP_MINIT_FUNCTION */
358PHP_MINIT_FUNCTION(pdo)
359{
360    zend_class_entry ce;
361
362    spl_ce_RuntimeException = NULL;
363
364    if (FAILURE == pdo_sqlstate_init_error_table()) {
365        return FAILURE;
366    }
367
368    zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
369
370    le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
371        "PDO persistent database", module_number);
372
373    INIT_CLASS_ENTRY(ce, "PDOException", NULL);
374
375    pdo_exception_ce = zend_register_internal_class_ex(&ce, php_pdo_get_exception_base(0 TSRMLS_CC), NULL TSRMLS_CC);
376
377    zend_declare_property_null(pdo_exception_ce, "errorInfo", sizeof("errorInfo")-1, ZEND_ACC_PUBLIC TSRMLS_CC);
378
379    pdo_dbh_init(TSRMLS_C);
380    pdo_stmt_init(TSRMLS_C);
381
382    return SUCCESS;
383}
384/* }}} */
385
386/* {{{ PHP_MSHUTDOWN_FUNCTION */
387PHP_MSHUTDOWN_FUNCTION(pdo)
388{
389    zend_hash_destroy(&pdo_driver_hash);
390    pdo_sqlstate_fini_error_table();
391    return SUCCESS;
392}
393/* }}} */
394
395/* {{{ PHP_MINFO_FUNCTION */
396PHP_MINFO_FUNCTION(pdo)
397{
398    HashPosition pos;
399    char *drivers = NULL, *ldrivers = estrdup("");
400    pdo_driver_t **pdriver;
401
402    php_info_print_table_start();
403    php_info_print_table_header(2, "PDO support", "enabled");
404
405    zend_hash_internal_pointer_reset_ex(&pdo_driver_hash, &pos);
406    while (SUCCESS == zend_hash_get_current_data_ex(&pdo_driver_hash, (void**)&pdriver, &pos)) {
407        spprintf(&drivers, 0, "%s, %s", ldrivers, (*pdriver)->driver_name);
408        zend_hash_move_forward_ex(&pdo_driver_hash, &pos);
409        efree(ldrivers);
410        ldrivers = drivers;
411    }
412
413    php_info_print_table_row(2, "PDO drivers", drivers ? drivers+2 : "");
414
415    if (drivers) {
416        efree(drivers);
417    } else {
418        efree(ldrivers);
419    }
420
421    php_info_print_table_end();
422
423}
424/* }}} */
425
426/*
427 * Local variables:
428 * tab-width: 4
429 * c-basic-offset: 4
430 * End:
431 * vim600: noet sw=4 ts=4 fdm=marker
432 * vim<600: noet sw=4 ts=4
433 */
434