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