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