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