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