1 | /* |
2 | +----------------------------------------------------------------------+ |
3 | | PHP Version 5 | |
4 | +----------------------------------------------------------------------+ |
5 | | Copyright (c) 1997-2015 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 | |
36 | static zend_class_entry *spl_ce_RuntimeException; |
37 | |
38 | ZEND_DECLARE_MODULE_GLOBALS(pdo) |
39 | static PHP_GINIT_FUNCTION(pdo); |
40 | |
41 | /* True global resources - no need for thread safety here */ |
42 | |
43 | /* the registry of PDO drivers */ |
44 | HashTable pdo_driver_hash; |
45 | |
46 | /* we use persistent resources for the driver connection stuff */ |
47 | static int le_ppdo; |
48 | |
49 | int php_pdo_list_entry(void) |
50 | { |
51 | return le_ppdo; |
52 | } |
53 | |
54 | /* for exceptional circumstances */ |
55 | zend_class_entry *pdo_exception_ce; |
56 | |
57 | PDO_API zend_class_entry *php_pdo_get_dbh_ce(void) |
58 | { |
59 | return pdo_dbh_ce; |
60 | } |
61 | |
62 | PDO_API zend_class_entry *php_pdo_get_exception(void) |
63 | { |
64 | return pdo_exception_ce; |
65 | } |
66 | |
67 | PDO_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 | |
74 | PDO_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 | |
97 | zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce; |
98 | |
99 | /* {{{ proto array pdo_drivers() |
100 | Return array of available PDO drivers */ |
101 | PHP_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 */ |
121 | ZEND_BEGIN_ARG_INFO(arginfo_pdo_drivers, 0) |
122 | ZEND_END_ARG_INFO() |
123 | /* }}} */ |
124 | |
125 | /* {{{ pdo_functions[] */ |
126 | const 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 |
134 | static 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 */ |
144 | zend_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 |
171 | ZEND_GET_MODULE(pdo) |
172 | #endif |
173 | |
174 | /* {{{ PHP_GINIT_FUNCTION */ |
175 | static PHP_GINIT_FUNCTION(pdo) |
176 | { |
177 | pdo_globals->global_value = 0; |
178 | } |
179 | /* }}} */ |
180 | |
181 | PDO_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 | |
197 | PDO_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 | |
206 | pdo_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 | |
215 | PDO_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 | const 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 | |
316 | static const char digit_vec[] = "0123456789" ; |
317 | PDO_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 */ |
358 | PHP_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 */ |
387 | PHP_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 */ |
396 | PHP_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 | |