xref: /PHP-8.2/ext/pdo/pdo.c (revision 6906d1fc)
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 /* we use persistent resources for the driver connection stuff */
45 static int le_ppdo;
46 
php_pdo_list_entry(void)47 int php_pdo_list_entry(void) /* {{{ */
48 {
49 	return le_ppdo;
50 }
51 /* }}} */
52 
php_pdo_get_dbh_ce(void)53 PDO_API zend_class_entry *php_pdo_get_dbh_ce(void) /* {{{ */
54 {
55 	return pdo_dbh_ce;
56 }
57 /* }}} */
58 
php_pdo_get_exception(void)59 PDO_API zend_class_entry *php_pdo_get_exception(void) /* {{{ */
60 {
61 	return pdo_exception_ce;
62 }
63 /* }}} */
64 
65 /* {{{ Return array of available PDO drivers */
PHP_FUNCTION(pdo_drivers)66 PHP_FUNCTION(pdo_drivers)
67 {
68 	pdo_driver_t *pdriver;
69 
70 	ZEND_PARSE_PARAMETERS_NONE();
71 
72 	array_init(return_value);
73 
74 	ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_hash, pdriver) {
75 		add_next_index_stringl(return_value, pdriver->driver_name, pdriver->driver_name_len);
76 	} ZEND_HASH_FOREACH_END();
77 }
78 /* }}} */
79 
80 /* {{{ pdo_functions[] */
81 static const zend_module_dep pdo_deps[] = {
82 	ZEND_MOD_REQUIRED("spl")
83 	ZEND_MOD_END
84 };
85 /* }}} */
86 
87 /* {{{ pdo_module_entry */
88 zend_module_entry pdo_module_entry = {
89 	STANDARD_MODULE_HEADER_EX, NULL,
90 	pdo_deps,
91 	"PDO",
92 	ext_functions,
93 	PHP_MINIT(pdo),
94 	PHP_MSHUTDOWN(pdo),
95 	NULL,
96 	NULL,
97 	PHP_MINFO(pdo),
98 	PHP_PDO_VERSION,
99 	STANDARD_MODULE_PROPERTIES
100 };
101 /* }}} */
102 
103 /* TODO: visit persistent handles: for each persistent statement handle,
104  * remove bound parameter associations */
105 
106 #ifdef COMPILE_DL_PDO
ZEND_GET_MODULE(pdo)107 ZEND_GET_MODULE(pdo)
108 #endif
109 
110 PDO_API zend_result php_pdo_register_driver(const pdo_driver_t *driver) /* {{{ */
111 {
112 	if (driver->api_version != PDO_DRIVER_API) {
113 		zend_error(E_ERROR, "PDO: driver %s requires PDO API version " ZEND_ULONG_FMT "; this is PDO version %d",
114 			driver->driver_name, driver->api_version, PDO_DRIVER_API);
115 		return FAILURE;
116 	}
117 	if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
118 		zend_error(E_ERROR, "You MUST load PDO before loading any PDO drivers");
119 		return FAILURE;	/* NOTREACHED */
120 	}
121 
122 	return zend_hash_str_add_ptr(&pdo_driver_hash, driver->driver_name, driver->driver_name_len, (void*)driver) != NULL ? SUCCESS : FAILURE;
123 }
124 /* }}} */
125 
php_pdo_unregister_driver(const pdo_driver_t * driver)126 PDO_API void php_pdo_unregister_driver(const pdo_driver_t *driver) /* {{{ */
127 {
128 	if (!zend_hash_str_exists(&module_registry, "pdo", sizeof("pdo") - 1)) {
129 		return;
130 	}
131 
132 	zend_hash_str_del(&pdo_driver_hash, driver->driver_name, driver->driver_name_len);
133 }
134 /* }}} */
135 
pdo_find_driver(const char * name,int namelen)136 pdo_driver_t *pdo_find_driver(const char *name, int namelen) /* {{{ */
137 {
138 	return zend_hash_str_find_ptr(&pdo_driver_hash, name, namelen);
139 }
140 /* }}} */
141 
php_pdo_parse_data_source(const char * data_source,zend_ulong data_source_len,struct pdo_data_src_parser * parsed,int nparams)142 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) /* {{{ */
143 {
144 	zend_ulong i;
145 	int j;
146 	int valstart = -1;
147 	int semi = -1;
148 	int optstart = 0;
149 	int nlen;
150 	int n_matches = 0;
151 	int n_semicolumns = 0;
152 
153 	i = 0;
154 	while (i < data_source_len) {
155 		/* looking for NAME= */
156 
157 		if (data_source[i] == '\0') {
158 			break;
159 		}
160 
161 		if (data_source[i] != '=') {
162 			++i;
163 			continue;
164 		}
165 
166 		valstart = ++i;
167 
168 		/* now we're looking for VALUE; or just VALUE<NUL> */
169 		semi = -1;
170 		n_semicolumns = 0;
171 		while (i < data_source_len) {
172 			if (data_source[i] == '\0') {
173 				semi = i++;
174 				break;
175 			}
176 			if (data_source[i] == ';') {
177 				if ((i + 1 >= data_source_len) || data_source[i+1] != ';') {
178 					semi = i++;
179 					break;
180 				} else {
181 					n_semicolumns++;
182 					i += 2;
183 					continue;
184 				}
185 			}
186 			++i;
187 		}
188 
189 		if (semi == -1) {
190 			semi = i;
191 		}
192 
193 		/* find the entry in the array */
194 		nlen = valstart - optstart - 1;
195 		for (j = 0; j < nparams; j++) {
196 			if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) && parsed[j].optname[nlen] == '\0') {
197 				/* got a match */
198 				if (parsed[j].freeme) {
199 					efree(parsed[j].optval);
200 				}
201 
202 				if (n_semicolumns == 0) {
203 					parsed[j].optval = estrndup(data_source + valstart, semi - valstart - n_semicolumns);
204 				} else {
205 					int vlen = semi - valstart;
206 					const char *orig_val = data_source + valstart;
207 					char *new_val  = (char *) emalloc(vlen - n_semicolumns + 1);
208 
209 					parsed[j].optval = new_val;
210 
211 					while (vlen && *orig_val) {
212 						*new_val = *orig_val;
213 						new_val++;
214 
215 						if (*orig_val == ';') {
216 							orig_val+=2;
217 							vlen-=2;
218 						} else {
219 							orig_val++;
220 							vlen--;
221 						}
222 					}
223 					*new_val = '\0';
224 				}
225 
226 				parsed[j].freeme = 1;
227 				++n_matches;
228 				break;
229 			}
230 		}
231 
232 		while (i < data_source_len && isspace(data_source[i])) {
233 			i++;
234 		}
235 
236 		optstart = i;
237 	}
238 
239 	return n_matches;
240 }
241 /* }}} */
242 
243 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo)244 PHP_MINIT_FUNCTION(pdo)
245 {
246 	pdo_sqlstate_init_error_table();
247 
248 	zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
249 
250 	le_ppdo = zend_register_list_destructors_ex(NULL, php_pdo_pdbh_dtor,
251 		"PDO persistent database", module_number);
252 
253 	pdo_exception_ce = register_class_PDOException(spl_ce_RuntimeException);
254 
255 	pdo_dbh_init(module_number);
256 	pdo_stmt_init();
257 
258 	return SUCCESS;
259 }
260 /* }}} */
261 
262 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(pdo)263 PHP_MSHUTDOWN_FUNCTION(pdo)
264 {
265 	zend_hash_destroy(&pdo_driver_hash);
266 	pdo_sqlstate_fini_error_table();
267 	return SUCCESS;
268 }
269 /* }}} */
270 
271 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pdo)272 PHP_MINFO_FUNCTION(pdo)
273 {
274 	char *drivers = NULL, *ldrivers = estrdup("");
275 	pdo_driver_t *pdriver;
276 
277 	php_info_print_table_start();
278 	php_info_print_table_header(2, "PDO support", "enabled");
279 
280 	ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_hash, pdriver) {
281 		spprintf(&drivers, 0, "%s, %s", ldrivers, pdriver->driver_name);
282 		efree(ldrivers);
283 		ldrivers = drivers;
284 	} ZEND_HASH_FOREACH_END();
285 
286 	php_info_print_table_row(2, "PDO drivers", drivers ? drivers + 2 : "");
287 
288 	if (drivers) {
289 		efree(drivers);
290 	} else {
291 		efree(ldrivers);
292 	}
293 
294 	php_info_print_table_end();
295 
296 }
297 /* }}} */
298