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