xref: /PHP-7.2/ext/pdo/pdo.c (revision 7a7ec01a)
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