1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | http://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Wez Furlong <wez@php.net> |
14 | Marcus Boerger <helly@php.net> |
15 | Sterling Hughes <sterling@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <ctype.h>
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/info.h"
27 #include "php_pdo.h"
28 #include "php_pdo_driver.h"
29 #include "php_pdo_int.h"
30 #include "zend_exceptions.h"
31 #include "ext/spl/spl_exceptions.h"
32 #include "pdo_arginfo.h"
33
34 zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce;
35
36 /* for exceptional circumstances */
37 zend_class_entry *pdo_exception_ce;
38
39 /* True global resources - no need for thread safety here */
40
41 /* the registry of PDO drivers */
42 HashTable pdo_driver_hash;
43
44 /* we use persistent resources for the driver connection stuff */
45 static int le_ppdo;
46
php_pdo_list_entry(void)47 int php_pdo_list_entry(void) /* {{{ */
48 {
49 return le_ppdo;
50 }
51 /* }}} */
52
php_pdo_get_dbh_ce(void)53 PDO_API zend_class_entry *php_pdo_get_dbh_ce(void) /* {{{ */
54 {
55 return pdo_dbh_ce;
56 }
57 /* }}} */
58
php_pdo_get_exception(void)59 PDO_API zend_class_entry *php_pdo_get_exception(void) /* {{{ */
60 {
61 return pdo_exception_ce;
62 }
63 /* }}} */
64
php_pdo_str_tolower_dup(const char * src,int len)65 PDO_API char *php_pdo_str_tolower_dup(const char *src, int len) /* {{{ */
66 {
67 char *dest = emalloc(len + 1);
68 zend_str_tolower_copy(dest, src, len);
69 return dest;
70 }
71 /* }}} */
72
73 /* {{{ Return array of available PDO drivers */
PHP_FUNCTION(pdo_drivers)74 PHP_FUNCTION(pdo_drivers)
75 {
76 pdo_driver_t *pdriver;
77
78 ZEND_PARSE_PARAMETERS_NONE();
79
80 array_init(return_value);
81
82 ZEND_HASH_FOREACH_PTR(&pdo_driver_hash, pdriver) {
83 add_next_index_stringl(return_value, (char*)pdriver->driver_name, pdriver->driver_name_len);
84 } ZEND_HASH_FOREACH_END();
85 }
86 /* }}} */
87
88 /* {{{ pdo_functions[] */
89 static const zend_module_dep pdo_deps[] = {
90 ZEND_MOD_REQUIRED("spl")
91 ZEND_MOD_END
92 };
93 /* }}} */
94
95 /* {{{ pdo_module_entry */
96 zend_module_entry pdo_module_entry = {
97 STANDARD_MODULE_HEADER_EX, NULL,
98 pdo_deps,
99 "PDO",
100 ext_functions,
101 PHP_MINIT(pdo),
102 PHP_MSHUTDOWN(pdo),
103 NULL,
104 NULL,
105 PHP_MINFO(pdo),
106 PHP_PDO_VERSION,
107 STANDARD_MODULE_PROPERTIES
108 };
109 /* }}} */
110
111 /* TODO: visit persistent handles: for each persistent statement handle,
112 * remove bound parameter associations */
113
114 #ifdef COMPILE_DL_PDO
ZEND_GET_MODULE(pdo)115 ZEND_GET_MODULE(pdo)
116 #endif
117
118 PDO_API int php_pdo_register_driver(const pdo_driver_t *driver) /* {{{ */
119 {
120 if (driver->api_version != PDO_DRIVER_API) {
121 zend_error(E_ERROR, "PDO: driver %s requires PDO API version " ZEND_ULONG_FMT "; this is PDO version %d",
122 driver->driver_name, driver->api_version, PDO_DRIVER_API);
123 return FAILURE;
124 }
125 if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
126 zend_error(E_ERROR, "You MUST load PDO before loading any PDO drivers");
127 return FAILURE; /* NOTREACHED */
128 }
129
130 return zend_hash_str_add_ptr(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len, (void*)driver) != NULL ? SUCCESS : FAILURE;
131 }
132 /* }}} */
133
php_pdo_unregister_driver(const pdo_driver_t * driver)134 PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver) /* {{{ */
135 {
136 if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
137 return;
138 }
139
140 zend_hash_str_del(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len);
141 }
142 /* }}} */
143
pdo_find_driver(const char * name,int namelen)144 pdo_driver_t *pdo_find_driver(const char *name, int namelen) /* {{{ */
145 {
146 return zend_hash_str_find_ptr(&pdo_driver_hash, (char*)name, namelen);
147 }
148 /* }}} */
149
php_pdo_parse_data_source(const char * data_source,zend_ulong data_source_len,struct pdo_data_src_parser * parsed,int nparams)150 PDO_API int php_pdo_parse_data_source(const char *data_source, zend_ulong data_source_len, struct pdo_data_src_parser *parsed, int nparams) /* {{{ */
151 {
152 zend_ulong i;
153 int j;
154 int valstart = -1;
155 int semi = -1;
156 int optstart = 0;
157 int nlen;
158 int n_matches = 0;
159 int n_semicolumns = 0;
160
161 i = 0;
162 while (i < data_source_len) {
163 /* looking for NAME= */
164
165 if (data_source[i] == '\0') {
166 break;
167 }
168
169 if (data_source[i] != '=') {
170 ++i;
171 continue;
172 }
173
174 valstart = ++i;
175
176 /* now we're looking for VALUE; or just VALUE<NUL> */
177 semi = -1;
178 n_semicolumns = 0;
179 while (i < data_source_len) {
180 if (data_source[i] == '\0') {
181 semi = i++;
182 break;
183 }
184 if (data_source[i] == ';') {
185 if ((i + 1 >= data_source_len) || data_source[i+1] != ';') {
186 semi = i++;
187 break;
188 } else {
189 n_semicolumns++;
190 i += 2;
191 continue;
192 }
193 }
194 ++i;
195 }
196
197 if (semi == -1) {
198 semi = i;
199 }
200
201 /* find the entry in the array */
202 nlen = valstart - optstart - 1;
203 for (j = 0; j < nparams; j++) {
204 if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) && parsed[j].optname[nlen] == '\0') {
205 /* got a match */
206 if (parsed[j].freeme) {
207 efree(parsed[j].optval);
208 }
209
210 if (n_semicolumns == 0) {
211 parsed[j].optval = estrndup(data_source + valstart, semi - valstart - n_semicolumns);
212 } else {
213 int vlen = semi - valstart;
214 const char *orig_val = data_source + valstart;
215 char *new_val = (char *) emalloc(vlen - n_semicolumns + 1);
216
217 parsed[j].optval = new_val;
218
219 while (vlen && *orig_val) {
220 *new_val = *orig_val;
221 new_val++;
222
223 if (*orig_val == ';') {
224 orig_val+=2;
225 vlen-=2;
226 } else {
227 orig_val++;
228 vlen--;
229 }
230 }
231 *new_val = '\0';
232 }
233
234 parsed[j].freeme = 1;
235 ++n_matches;
236 break;
237 }
238 }
239
240 while (i < data_source_len && isspace(data_source[i])) {
241 i++;
242 }
243
244 optstart = i;
245 }
246
247 return n_matches;
248 }
249 /* }}} */
250
251 static const char digit_vec[] = "0123456789";
php_pdo_int64_to_str(pdo_int64_t i64)252 PDO_API char *php_pdo_int64_to_str(pdo_int64_t i64) /* {{{ */
253 {
254 char buffer[65];
255 char outbuf[65] = "";
256 register char *p;
257 zend_long long_val;
258 char *dst = outbuf;
259
260 if (i64 < 0) {
261 i64 = -i64;
262 *dst++ = '-';
263 }
264
265 if (i64 == 0) {
266 *dst++ = '0';
267 *dst++ = '\0';
268 return estrdup(outbuf);
269 }
270
271 p = &buffer[sizeof(buffer)-1];
272 *p = '\0';
273
274 while ((pdo_uint64_t)i64 > (pdo_uint64_t)ZEND_LONG_MAX) {
275 pdo_uint64_t quo = (pdo_uint64_t)i64 / (unsigned int)10;
276 unsigned int rem = (unsigned int)(i64 - quo*10U);
277 *--p = digit_vec[rem];
278 i64 = (pdo_int64_t)quo;
279 }
280 long_val = (zend_long)i64;
281 while (long_val != 0) {
282 zend_long quo = long_val / 10;
283 *--p = digit_vec[(unsigned int)(long_val - quo * 10)];
284 long_val = quo;
285 }
286 while ((*dst++ = *p++) != 0)
287 ;
288 *dst = '\0';
289 return estrdup(outbuf);
290 }
291 /* }}} */
292
293 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo)294 PHP_MINIT_FUNCTION(pdo)
295 {
296 zend_class_entry ce;
297
298 if (FAILURE == pdo_sqlstate_init_error_table()) {
299 return FAILURE;
300 }
301
302 zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
303
304 le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
305 "PDO persistent database", module_number);
306
307 INIT_CLASS_ENTRY(ce, "PDOException", NULL);
308
309 pdo_exception_ce = zend_register_internal_class_ex(&ce, spl_ce_RuntimeException);
310
311 zend_declare_property_null(pdo_exception_ce, "errorInfo", sizeof("errorInfo")-1, ZEND_ACC_PUBLIC);
312
313 pdo_dbh_init();
314 pdo_stmt_init();
315
316 return SUCCESS;
317 }
318 /* }}} */
319
320 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(pdo)321 PHP_MSHUTDOWN_FUNCTION(pdo)
322 {
323 zend_hash_destroy(&pdo_driver_hash);
324 pdo_sqlstate_fini_error_table();
325 return SUCCESS;
326 }
327 /* }}} */
328
329 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pdo)330 PHP_MINFO_FUNCTION(pdo)
331 {
332 char *drivers = NULL, *ldrivers = estrdup("");
333 pdo_driver_t *pdriver;
334
335 php_info_print_table_start();
336 php_info_print_table_header(2, "PDO support", "enabled");
337
338 ZEND_HASH_FOREACH_PTR(&pdo_driver_hash, pdriver) {
339 spprintf(&drivers, 0, "%s, %s", ldrivers, pdriver->driver_name);
340 efree(ldrivers);
341 ldrivers = drivers;
342 } ZEND_HASH_FOREACH_END();
343
344 php_info_print_table_row(2, "PDO drivers", drivers ? drivers + 2 : "");
345
346 if (drivers) {
347 efree(drivers);
348 } else {
349 efree(ldrivers);
350 }
351
352 php_info_print_table_end();
353
354 }
355 /* }}} */
356