xref: /PHP-8.4/ext/pdo/pdo.c (revision 11accb5c)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Author: Wez Furlong <wez@php.net>                                    |
14   |         Marcus Boerger <helly@php.net>                               |
15   |         Sterling Hughes <sterling@php.net>                           |
16   +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22 
23 #include <ctype.h>
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/info.h"
27 #include "php_pdo.h"
28 #include "php_pdo_driver.h"
29 #include "php_pdo_int.h"
30 #include "zend_exceptions.h"
31 #include "ext/spl/spl_exceptions.h"
32 #include "pdo_arginfo.h"
33 
34 zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce;
35 
36 /* for exceptional circumstances */
37 zend_class_entry *pdo_exception_ce;
38 
39 /* True global resources - no need for thread safety here */
40 
41 /* the registry of PDO drivers */
42 HashTable pdo_driver_hash;
43 
44 /* the registry of PDO driver specific class entries */
45 HashTable pdo_driver_specific_ce_hash;
46 
47 /* we use persistent resources for the driver connection stuff */
48 static int le_ppdo;
49 
php_pdo_list_entry(void)50 int php_pdo_list_entry(void) /* {{{ */
51 {
52 	return le_ppdo;
53 }
54 /* }}} */
55 
php_pdo_get_dbh_ce(void)56 PDO_API zend_class_entry *php_pdo_get_dbh_ce(void) /* {{{ */
57 {
58 	return pdo_dbh_ce;
59 }
60 /* }}} */
61 
php_pdo_get_exception(void)62 PDO_API zend_class_entry *php_pdo_get_exception(void) /* {{{ */
63 {
64 	return pdo_exception_ce;
65 }
66 /* }}} */
67 
68 /* {{{ Return array of available PDO drivers */
PHP_FUNCTION(pdo_drivers)69 PHP_FUNCTION(pdo_drivers)
70 {
71 	pdo_driver_t *pdriver;
72 
73 	ZEND_PARSE_PARAMETERS_NONE();
74 
75 	array_init(return_value);
76 
77 	ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_hash, pdriver) {
78 		add_next_index_stringl(return_value, pdriver->driver_name, pdriver->driver_name_len);
79 	} ZEND_HASH_FOREACH_END();
80 }
81 /* }}} */
82 
83 /* {{{ pdo_functions[] */
84 static const zend_module_dep pdo_deps[] = {
85 	ZEND_MOD_REQUIRED("spl")
86 	ZEND_MOD_END
87 };
88 /* }}} */
89 
90 /* {{{ pdo_module_entry */
91 zend_module_entry pdo_module_entry = {
92 	STANDARD_MODULE_HEADER_EX, NULL,
93 	pdo_deps,
94 	"PDO",
95 	ext_functions,
96 	PHP_MINIT(pdo),
97 	PHP_MSHUTDOWN(pdo),
98 	NULL,
99 	NULL,
100 	PHP_MINFO(pdo),
101 	PHP_PDO_VERSION,
102 	STANDARD_MODULE_PROPERTIES
103 };
104 /* }}} */
105 
106 /* TODO: visit persistent handles: for each persistent statement handle,
107  * remove bound parameter associations */
108 
109 #ifdef COMPILE_DL_PDO
ZEND_GET_MODULE(pdo)110 ZEND_GET_MODULE(pdo)
111 #endif
112 
113 PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver) /* {{{ */
114 {
115 	if (driver->api_version != PDO_DRIVER_API) {
116 		zend_error_noreturn(E_ERROR, "PDO: driver %s requires PDO API version " ZEND_ULONG_FMT "; this is PDO version %d",
117 			driver->driver_name, driver->api_version, PDO_DRIVER_API);
118 		return FAILURE;
119 	}
120 	if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
121 		zend_error_noreturn(E_ERROR, "The PDO extension must be loaded first in order to load PDO drivers");
122 		return FAILURE;	/* NOTREACHED */
123 	}
124 
125 	return zend_hash_str_add_ptr(&pdo_driver_hash, driver->driver_name, driver->driver_name_len, (void*)driver) != NULL ? SUCCESS : FAILURE;
126 }
127 /* }}} */
128 
php_pdo_unregister_driver(const pdo_driver_t * driver)129 PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver) /* {{{ */
130 {
131 	if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
132 		return;
133 	}
134 
135 	zend_hash_str_del(&pdo_driver_specific_ce_hash, driver->driver_name, driver->driver_name_len);
136 	zend_hash_str_del(&pdo_driver_hash, driver->driver_name, driver->driver_name_len);
137 }
138 /* }}} */
139 
php_pdo_register_driver_specific_ce(const pdo_driver_t * driver,zend_class_entry * ce)140 PDO_API zend_result php_pdo_register_driver_specific_ce(const pdo_driver_t *driver, zend_class_entry *ce) /* {{{ */
141 {
142 	if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
143 		zend_error_noreturn(E_ERROR, "The PDO extension must be loaded first in order to load PDO drivers");
144 		return FAILURE;	/* NOTREACHED */
145 	}
146 
147 	return zend_hash_str_add_ptr(&pdo_driver_specific_ce_hash, driver->driver_name,
148 		driver->driver_name_len, (void*)ce) != NULL ? SUCCESS : FAILURE;
149 }
150 
pdo_find_driver(const char * name,int namelen)151 pdo_driver_t *pdo_find_driver(const char *name, int namelen) /* {{{ */
152 {
153 	return zend_hash_str_find_ptr(&pdo_driver_hash, name, namelen);
154 }
155 /* }}} */
156 
php_pdo_parse_data_source(const char * data_source,zend_ulong data_source_len,struct pdo_data_src_parser * parsed,int nparams)157 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) /* {{{ */
158 {
159 	zend_ulong i;
160 	int j;
161 	int valstart = -1;
162 	int semi = -1;
163 	int optstart = 0;
164 	int nlen;
165 	int n_matches = 0;
166 	int n_semicolumns = 0;
167 
168 	i = 0;
169 	while (i < data_source_len) {
170 		/* looking for NAME= */
171 
172 		if (data_source[i] == '\0') {
173 			break;
174 		}
175 
176 		if (data_source[i] != '=') {
177 			++i;
178 			continue;
179 		}
180 
181 		valstart = ++i;
182 
183 		/* now we're looking for VALUE; or just VALUE<NUL> */
184 		semi = -1;
185 		n_semicolumns = 0;
186 		while (i < data_source_len) {
187 			if (data_source[i] == '\0') {
188 				semi = i++;
189 				break;
190 			}
191 			if (data_source[i] == ';') {
192 				if ((i + 1 >= data_source_len) || data_source[i+1] != ';') {
193 					semi = i++;
194 					break;
195 				} else {
196 					n_semicolumns++;
197 					i += 2;
198 					continue;
199 				}
200 			}
201 			++i;
202 		}
203 
204 		if (semi == -1) {
205 			semi = i;
206 		}
207 
208 		/* find the entry in the array */
209 		nlen = valstart - optstart - 1;
210 		for (j = 0; j < nparams; j++) {
211 			if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) && parsed[j].optname[nlen] == '\0') {
212 				/* got a match */
213 				if (parsed[j].freeme) {
214 					efree(parsed[j].optval);
215 				}
216 
217 				if (n_semicolumns == 0) {
218 					parsed[j].optval = estrndup(data_source + valstart, semi - valstart - n_semicolumns);
219 				} else {
220 					int vlen = semi - valstart;
221 					const char *orig_val = data_source + valstart;
222 					char *new_val  = (char *) emalloc(vlen - n_semicolumns + 1);
223 
224 					parsed[j].optval = new_val;
225 
226 					while (vlen && *orig_val) {
227 						*new_val = *orig_val;
228 						new_val++;
229 
230 						if (*orig_val == ';') {
231 							orig_val+=2;
232 							vlen-=2;
233 						} else {
234 							orig_val++;
235 							vlen--;
236 						}
237 					}
238 					*new_val = '\0';
239 				}
240 
241 				parsed[j].freeme = 1;
242 				++n_matches;
243 				break;
244 			}
245 		}
246 
247 		while (i < data_source_len && isspace(data_source[i])) {
248 			i++;
249 		}
250 
251 		optstart = i;
252 	}
253 
254 	return n_matches;
255 }
256 /* }}} */
257 
258 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo)259 PHP_MINIT_FUNCTION(pdo)
260 {
261 	pdo_sqlstate_init_error_table();
262 
263 	zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
264 	zend_hash_init(&pdo_driver_specific_ce_hash, 0, NULL, NULL, 1);
265 
266 	le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
267 		"PDO persistent database", module_number);
268 
269 	pdo_exception_ce = register_class_PDOException(spl_ce_RuntimeException);
270 
271 	pdo_dbh_init(module_number);
272 	pdo_stmt_init();
273 
274 	return SUCCESS;
275 }
276 /* }}} */
277 
278 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(pdo)279 PHP_MSHUTDOWN_FUNCTION(pdo)
280 {
281 	zend_hash_destroy(&pdo_driver_hash);
282 	zend_hash_destroy(&pdo_driver_specific_ce_hash);
283 	pdo_sqlstate_fini_error_table();
284 	return SUCCESS;
285 }
286 /* }}} */
287 
288 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pdo)289 PHP_MINFO_FUNCTION(pdo)
290 {
291 	char *drivers = NULL, *ldrivers = estrdup("");
292 	pdo_driver_t *pdriver;
293 
294 	php_info_print_table_start();
295 	php_info_print_table_row(2, "PDO support", "enabled");
296 
297 	ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_hash, pdriver) {
298 		spprintf(&drivers, 0, "%s, %s", ldrivers, pdriver->driver_name);
299 		efree(ldrivers);
300 		ldrivers = drivers;
301 	} ZEND_HASH_FOREACH_END();
302 
303 	php_info_print_table_row(2, "PDO drivers", drivers ? drivers + 2 : "");
304 
305 	if (drivers) {
306 		efree(drivers);
307 	} else {
308 		efree(ldrivers);
309 	}
310 
311 	php_info_print_table_end();
312 
313 }
314 /* }}} */
315