xref: /PHP-7.4/ext/pdo/pdo.c (revision 5aa11762)
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