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_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();
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_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