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 /* The PDO Database Handle Class */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "php_pdo.h"
29 #include "php_pdo_driver.h"
30 #include "php_pdo_int.h"
31 #include "zend_attributes.h"
32 #include "zend_exceptions.h"
33 #include "zend_object_handlers.h"
34 #include "zend_hash.h"
35 #include "pdo_dbh_arginfo.h"
36 #include "zend_observer.h"
37 #include "zend_extensions.h"
38
39 static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value);
40
pdo_throw_exception(unsigned int driver_errcode,char * driver_errmsg,pdo_error_type * pdo_error)41 void pdo_throw_exception(unsigned int driver_errcode, char *driver_errmsg, pdo_error_type *pdo_error)
42 {
43 zval error_info,pdo_exception;
44 char *pdo_exception_message;
45
46 object_init_ex(&pdo_exception, php_pdo_get_exception());
47 array_init(&error_info);
48
49 add_next_index_string(&error_info, *pdo_error);
50 add_next_index_long(&error_info, driver_errcode);
51 add_next_index_string(&error_info, driver_errmsg);
52
53 spprintf(&pdo_exception_message, 0,"SQLSTATE[%s] [%d] %s",*pdo_error, driver_errcode, driver_errmsg);
54 zend_update_property(php_pdo_get_exception(), Z_OBJ(pdo_exception), "errorInfo", sizeof("errorInfo")-1, &error_info);
55 zend_update_property_long(php_pdo_get_exception(), Z_OBJ(pdo_exception), "code", sizeof("code")-1, driver_errcode);
56 zend_update_property_string(
57 php_pdo_get_exception(),
58 Z_OBJ(pdo_exception),
59 "message",
60 sizeof("message")-1,
61 pdo_exception_message
62 );
63 efree(pdo_exception_message);
64 zval_ptr_dtor(&error_info);
65 zend_throw_exception_object(&pdo_exception);
66 }
67
pdo_raise_impl_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt,pdo_error_type sqlstate,const char * supp)68 void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, pdo_error_type sqlstate, const char *supp) /* {{{ */
69 {
70 pdo_error_type *pdo_err = &dbh->error_code;
71 char *message = NULL;
72 const char *msg;
73
74 if (dbh && dbh->error_mode == PDO_ERRMODE_SILENT) {
75 #if 0
76 /* BUG: if user is running in silent mode and hits an error at the driver level
77 * when they use the PDO methods to call up the error information, they may
78 * get bogus information */
79 return;
80 #endif
81 }
82
83 if (stmt) {
84 pdo_err = &stmt->error_code;
85 }
86
87 memcpy(*pdo_err, sqlstate, sizeof(pdo_error_type));
88
89 /* hash sqlstate to error messages */
90 msg = pdo_sqlstate_state_to_description(*pdo_err);
91 if (!msg) {
92 msg = "<<Unknown error>>";
93 }
94
95 if (supp) {
96 spprintf(&message, 0, "SQLSTATE[%s]: %s: %s", *pdo_err, msg, supp);
97 } else {
98 spprintf(&message, 0, "SQLSTATE[%s]: %s", *pdo_err, msg);
99 }
100
101 if (dbh && dbh->error_mode != PDO_ERRMODE_EXCEPTION) {
102 php_error_docref(NULL, E_WARNING, "%s", message);
103 } else {
104 zval ex, info;
105 zend_class_entry *pdo_ex = php_pdo_get_exception();
106
107 object_init_ex(&ex, pdo_ex);
108
109 zend_update_property_string(zend_ce_exception, Z_OBJ(ex), "message", sizeof("message")-1, message);
110 zend_update_property_string(zend_ce_exception, Z_OBJ(ex), "code", sizeof("code")-1, *pdo_err);
111
112 array_init(&info);
113
114 add_next_index_string(&info, *pdo_err);
115 add_next_index_long(&info, 0);
116 zend_update_property(pdo_ex, Z_OBJ(ex), "errorInfo", sizeof("errorInfo")-1, &info);
117 zval_ptr_dtor(&info);
118
119 zend_throw_exception_object(&ex);
120 }
121
122 if (message) {
123 efree(message);
124 }
125 }
126 /* }}} */
127
pdo_handle_error(pdo_dbh_t * dbh,pdo_stmt_t * stmt)128 PDO_API void pdo_handle_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt) /* {{{ */
129 {
130 pdo_error_type *pdo_err = &dbh->error_code;
131 const char *msg = "<<Unknown>>";
132 char *supp = NULL;
133 zend_long native_code = 0;
134 zend_string *message = NULL;
135 zval info;
136
137 if (dbh == NULL || dbh->error_mode == PDO_ERRMODE_SILENT) {
138 return;
139 }
140
141 if (stmt) {
142 pdo_err = &stmt->error_code;
143 }
144
145 /* hash sqlstate to error messages */
146 msg = pdo_sqlstate_state_to_description(*pdo_err);
147 if (!msg) {
148 msg = "<<Unknown error>>";
149 }
150
151 ZVAL_UNDEF(&info);
152 if (dbh->methods->fetch_err) {
153 zval *item;
154 array_init(&info);
155
156 add_next_index_string(&info, *pdo_err);
157
158 dbh->methods->fetch_err(dbh, stmt, &info);
159
160 if ((item = zend_hash_index_find(Z_ARRVAL(info), 1)) != NULL
161 && Z_TYPE_P(item) == IS_LONG) {
162 native_code = Z_LVAL_P(item);
163 }
164
165 if ((item = zend_hash_index_find(Z_ARRVAL(info), 2)) != NULL) {
166 supp = estrndup(Z_STRVAL_P(item), Z_STRLEN_P(item));
167 }
168 }
169
170 if (native_code && supp) {
171 message = strpprintf(0, "SQLSTATE[%s]: %s: " ZEND_LONG_FMT " %s", *pdo_err, msg, native_code, supp);
172 } else if (supp) {
173 message = strpprintf(0, "SQLSTATE[%s]: %s: %s", *pdo_err, msg, supp);
174 } else {
175 message = strpprintf(0, "SQLSTATE[%s]: %s", *pdo_err, msg);
176 }
177
178 if (dbh->error_mode == PDO_ERRMODE_WARNING) {
179 php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(message));
180 } else if (EG(exception) == NULL) {
181 zval ex;
182 zend_class_entry *pdo_ex = php_pdo_get_exception();
183
184 object_init_ex(&ex, pdo_ex);
185
186 zend_update_property_str(zend_ce_exception, Z_OBJ(ex), "message", sizeof("message") - 1, message);
187 zend_update_property_string(zend_ce_exception, Z_OBJ(ex), "code", sizeof("code") - 1, *pdo_err);
188
189 if (!Z_ISUNDEF(info)) {
190 zend_update_property(pdo_ex, Z_OBJ(ex), "errorInfo", sizeof("errorInfo") - 1, &info);
191 }
192
193 zend_throw_exception_object(&ex);
194 }
195
196 if (!Z_ISUNDEF(info)) {
197 zval_ptr_dtor(&info);
198 }
199
200 if (message) {
201 zend_string_release_ex(message, 0);
202 }
203
204 if (supp) {
205 efree(supp);
206 }
207 }
208 /* }}} */
209
dsn_from_uri(char * uri,char * buf,size_t buflen)210 static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */
211 {
212 php_stream *stream;
213 char *dsn = NULL;
214
215 stream = php_stream_open_wrapper(uri, "rb", REPORT_ERRORS, NULL);
216 if (stream) {
217 dsn = php_stream_get_line(stream, buf, buflen, NULL);
218 php_stream_close(stream);
219 }
220 return dsn;
221 }
222 /* }}} */
223
224 /* {{{ */
PHP_METHOD(PDO,__construct)225 PHP_METHOD(PDO, __construct)
226 {
227 zval *object = ZEND_THIS;
228 pdo_dbh_t *dbh = NULL;
229 bool is_persistent = 0;
230 char *data_source;
231 size_t data_source_len;
232 char *colon;
233 char *username=NULL, *password=NULL;
234 size_t usernamelen, passwordlen;
235 pdo_driver_t *driver = NULL;
236 zval *options = NULL;
237 char alt_dsn[512];
238 int call_factory = 1;
239 zend_error_handling zeh;
240
241 ZEND_PARSE_PARAMETERS_START(1, 4)
242 Z_PARAM_STRING(data_source, data_source_len)
243 Z_PARAM_OPTIONAL
244 Z_PARAM_STRING_OR_NULL(username, usernamelen)
245 Z_PARAM_STRING_OR_NULL(password, passwordlen)
246 Z_PARAM_ARRAY_OR_NULL(options)
247 ZEND_PARSE_PARAMETERS_END();
248
249 /* parse the data source name */
250 colon = strchr(data_source, ':');
251
252 if (!colon) {
253 /* let's see if this string has a matching dsn in the php.ini */
254 char *ini_dsn = NULL;
255
256 snprintf(alt_dsn, sizeof(alt_dsn), "pdo.dsn.%s", data_source);
257 if (FAILURE == cfg_get_string(alt_dsn, &ini_dsn)) {
258 zend_argument_error(php_pdo_get_exception(), 1, "must be a valid data source name");
259 RETURN_THROWS();
260 }
261
262 data_source = ini_dsn;
263 colon = strchr(data_source, ':');
264
265 if (!colon) {
266 zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name (via INI: %s)", alt_dsn);
267 RETURN_THROWS();
268 }
269 }
270
271 if (!strncmp(data_source, "uri:", sizeof("uri:")-1)) {
272 /* the specified URI holds connection details */
273 data_source = dsn_from_uri(data_source + sizeof("uri:")-1, alt_dsn, sizeof(alt_dsn));
274 if (!data_source) {
275 zend_argument_error(php_pdo_get_exception(), 1, "must be a valid data source URI");
276 RETURN_THROWS();
277 }
278 colon = strchr(data_source, ':');
279 if (!colon) {
280 zend_argument_error(php_pdo_get_exception(), 1, "must be a valid data source name (via URI)");
281 RETURN_THROWS();
282 }
283 }
284
285 driver = pdo_find_driver(data_source, colon - data_source);
286
287 if (!driver) {
288 /* NB: don't want to include the data_source in the error message as
289 * it might contain a password */
290 zend_throw_exception_ex(php_pdo_get_exception(), 0, "could not find driver");
291 RETURN_THROWS();
292 }
293
294 dbh = Z_PDO_DBH_P(object);
295
296 /* is this supposed to be a persistent connection ? */
297 if (options) {
298 int plen = 0;
299 char *hashkey = NULL;
300 zend_resource *le;
301 pdo_dbh_t *pdbh = NULL;
302 zval *v;
303
304 if ((v = zend_hash_index_find_deref(Z_ARRVAL_P(options), PDO_ATTR_PERSISTENT)) != NULL) {
305 if (Z_TYPE_P(v) == IS_STRING &&
306 !is_numeric_string(Z_STRVAL_P(v), Z_STRLEN_P(v), NULL, NULL, 0) && Z_STRLEN_P(v) > 0) {
307 /* user specified key */
308 plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s:%s", data_source,
309 username ? username : "",
310 password ? password : "",
311 Z_STRVAL_P(v));
312 is_persistent = 1;
313 } else {
314 is_persistent = zval_get_long(v) ? 1 : 0;
315 plen = spprintf(&hashkey, 0, "PDO:DBH:DSN=%s:%s:%s", data_source,
316 username ? username : "",
317 password ? password : "");
318 }
319 }
320
321 if (is_persistent) {
322 /* let's see if we have one cached.... */
323 if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashkey, plen)) != NULL) {
324 if (le->type == php_pdo_list_entry()) {
325 pdbh = (pdo_dbh_t*)le->ptr;
326
327 /* is the connection still alive ? */
328 if (pdbh->methods->check_liveness && FAILURE == (pdbh->methods->check_liveness)(pdbh)) {
329 /* nope... need to kill it */
330 pdbh->refcount--;
331 zend_list_close(le);
332 pdbh = NULL;
333 }
334 }
335 }
336
337 if (pdbh) {
338 call_factory = 0;
339 } else {
340 /* need a brand new pdbh */
341 pdbh = pecalloc(1, sizeof(*pdbh), 1);
342
343 pdbh->refcount = 1;
344 pdbh->is_persistent = 1;
345 pdbh->persistent_id = pemalloc(plen + 1, 1);
346 memcpy((char *)pdbh->persistent_id, hashkey, plen+1);
347 pdbh->persistent_id_len = plen;
348 pdbh->def_stmt_ce = dbh->def_stmt_ce;
349 }
350 }
351
352 if (pdbh) {
353 efree(dbh);
354 /* switch over to the persistent one */
355 Z_PDO_OBJECT_P(object)->inner = pdbh;
356 pdbh->refcount++;
357 dbh = pdbh;
358 }
359
360 if (hashkey) {
361 efree(hashkey);
362 }
363 }
364
365 if (call_factory) {
366 dbh->data_source_len = strlen(colon + 1);
367 dbh->data_source = (const char*)pestrdup(colon + 1, is_persistent);
368 dbh->username = username ? pestrdup(username, is_persistent) : NULL;
369 dbh->password = password ? pestrdup(password, is_persistent) : NULL;
370 dbh->default_fetch_type = PDO_FETCH_BOTH;
371 }
372
373 dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1);
374 dbh->error_mode = pdo_attr_lval(options, PDO_ATTR_ERRMODE, PDO_ERRMODE_EXCEPTION);
375
376 if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
377 php_error_docref(NULL, E_ERROR, "Out of memory");
378 }
379
380 /* pdo_dbh_attribute_set() can emit a Warning if the ERR_MODE is set to warning
381 * As we are in a constructor we override the behaviour by replacing the error handler */
382 zend_replace_error_handling(EH_THROW, pdo_exception_ce, &zeh);
383
384 if (!call_factory) {
385 /* we got a persistent guy from our cache */
386 goto options;
387 }
388
389 if (driver->db_handle_factory(dbh, options)) {
390 /* all set */
391
392 if (is_persistent) {
393 /* register in the persistent list etc. */
394 /* we should also need to replace the object store entry,
395 since it was created with emalloc */
396 if ((zend_register_persistent_resource(
397 (char*)dbh->persistent_id, dbh->persistent_id_len, dbh, php_pdo_list_entry())) == NULL) {
398 php_error_docref(NULL, E_ERROR, "Failed to register persistent entry");
399 }
400 }
401
402 dbh->driver = driver;
403 options:
404 if (options) {
405 zval *attr_value;
406 zend_ulong long_key;
407 zend_string *str_key = NULL;
408
409 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(options), long_key, str_key, attr_value) {
410 if (str_key) {
411 continue;
412 }
413 ZVAL_DEREF(attr_value);
414
415 /* TODO: Should the constructor fail when the attribute cannot be set? */
416 pdo_dbh_attribute_set(dbh, long_key, attr_value);
417 } ZEND_HASH_FOREACH_END();
418 }
419
420 zend_restore_error_handling(&zeh);
421 return;
422 }
423
424 /* the connection failed; things will tidy up in free_storage */
425 if (is_persistent) {
426 dbh->refcount--;
427 }
428
429 /* XXX raise exception */
430 zend_restore_error_handling(&zeh);
431 if (!EG(exception)) {
432 zend_throw_exception(pdo_exception_ce, "Constructor failed", 0);
433 }
434 }
435 /* }}} */
436
pdo_stmt_instantiate(pdo_dbh_t * dbh,zval * object,zend_class_entry * dbstmt_ce,zval * ctor_args)437 static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
438 {
439 if (!Z_ISUNDEF_P(ctor_args)) {
440 /* This implies an error within PDO if this does not hold */
441 ZEND_ASSERT(Z_TYPE_P(ctor_args) == IS_ARRAY);
442 if (!dbstmt_ce->constructor) {
443 zend_throw_error(NULL, "User-supplied statement does not accept constructor arguments");
444 return NULL;
445 }
446 }
447
448 if (UNEXPECTED(object_init_ex(object, dbstmt_ce) != SUCCESS)) {
449 if (EXPECTED(!EG(exception))) {
450 zend_throw_error(NULL, "Cannot instantiate user-supplied statement class");
451 }
452 return NULL;
453 }
454
455 return object;
456 } /* }}} */
457
pdo_stmt_construct(zend_execute_data * execute_data,pdo_stmt_t * stmt,zval * object,zend_class_entry * dbstmt_ce,zval * ctor_args)458 static void pdo_stmt_construct(zend_execute_data *execute_data, pdo_stmt_t *stmt, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
459 {
460 zval query_string;
461 zend_string *key;
462
463 ZVAL_STR(&query_string, stmt->query_string);
464 key = zend_string_init("queryString", sizeof("queryString") - 1, 0);
465 zend_std_write_property(Z_OBJ_P(object), key, &query_string, NULL);
466 zend_string_release_ex(key, 0);
467
468 if (dbstmt_ce->constructor) {
469 zend_fcall_info fci;
470 zend_fcall_info_cache fcc;
471 zval retval;
472
473 fci.size = sizeof(zend_fcall_info);
474 ZVAL_UNDEF(&fci.function_name);
475 fci.object = Z_OBJ_P(object);
476 fci.retval = &retval;
477 fci.param_count = 0;
478 fci.params = NULL;
479 fci.named_params = NULL;
480
481 zend_fcall_info_args(&fci, ctor_args);
482
483 fcc.function_handler = dbstmt_ce->constructor;
484 fcc.called_scope = Z_OBJCE_P(object);
485 fcc.object = Z_OBJ_P(object);
486
487 if (zend_call_function(&fci, &fcc) != FAILURE) {
488 zval_ptr_dtor(&retval);
489 }
490
491 zend_fcall_info_args_clear(&fci, 1);
492 }
493 }
494 /* }}} */
495
496 /* {{{ Prepares a statement for execution and returns a statement object */
PHP_METHOD(PDO,prepare)497 PHP_METHOD(PDO, prepare)
498 {
499 pdo_stmt_t *stmt;
500 zend_string *statement;
501 zval *options = NULL, *value, *item, ctor_args;
502 zend_class_entry *dbstmt_ce, *pce;
503 pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(ZEND_THIS);
504 pdo_dbh_t *dbh = dbh_obj->inner;
505
506 ZEND_PARSE_PARAMETERS_START(1, 2)
507 Z_PARAM_STR(statement)
508 Z_PARAM_OPTIONAL
509 Z_PARAM_ARRAY(options)
510 ZEND_PARSE_PARAMETERS_END();
511
512 PDO_CONSTRUCT_CHECK;
513
514 if (ZSTR_LEN(statement) == 0) {
515 zend_argument_value_error(1, "cannot be empty");
516 RETURN_THROWS();
517 }
518
519 PDO_DBH_CLEAR_ERR();
520
521 if (options && (value = zend_hash_index_find(Z_ARRVAL_P(options), PDO_ATTR_STATEMENT_CLASS)) != NULL) {
522 if (Z_TYPE_P(value) != IS_ARRAY) {
523 zend_type_error("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given",
524 zend_zval_type_name(value));
525 RETURN_THROWS();
526 }
527 if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL) {
528 zend_value_error("PDO::ATTR_STATEMENT_CLASS value must be an array with the format "
529 "array(classname, constructor_args)");
530 RETURN_THROWS();
531 }
532 if (Z_TYPE_P(item) != IS_STRING || (pce = zend_lookup_class(Z_STR_P(item))) == NULL) {
533 zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be a valid class");
534 RETURN_THROWS();
535 }
536 dbstmt_ce = pce;
537 if (!instanceof_function(dbstmt_ce, pdo_dbstmt_ce)) {
538 zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement");
539 RETURN_THROWS();
540 }
541 if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
542 zend_type_error("User-supplied statement class cannot have a public constructor");
543 RETURN_THROWS();
544 }
545 if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
546 if (Z_TYPE_P(item) != IS_ARRAY) {
547 zend_type_error("PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given",
548 zend_zval_type_name(value));
549 RETURN_THROWS();
550 }
551 ZVAL_COPY_VALUE(&ctor_args, item);
552 } else {
553 ZVAL_UNDEF(&ctor_args);
554 }
555 } else {
556 dbstmt_ce = dbh->def_stmt_ce;
557 ZVAL_COPY_VALUE(&ctor_args, &dbh->def_stmt_ctor_args);
558 }
559
560 if (!pdo_stmt_instantiate(dbh, return_value, dbstmt_ce, &ctor_args)) {
561 RETURN_THROWS();
562 }
563 stmt = Z_PDO_STMT_P(return_value);
564
565 /* unconditionally keep this for later reference */
566 stmt->query_string = zend_string_copy(statement);
567 stmt->default_fetch_type = dbh->default_fetch_type;
568 stmt->dbh = dbh;
569 /* give it a reference to me */
570 ZVAL_OBJ_COPY(&stmt->database_object_handle, &dbh_obj->std);
571 /* we haven't created a lazy object yet */
572 ZVAL_UNDEF(&stmt->lazy_object_ref);
573
574 if (dbh->methods->preparer(dbh, statement, stmt, options)) {
575 pdo_stmt_construct(execute_data, stmt, return_value, dbstmt_ce, &ctor_args);
576 return;
577 }
578
579 PDO_HANDLE_DBH_ERR();
580
581 /* kill the object handle for the stmt here */
582 zval_ptr_dtor(return_value);
583
584 RETURN_FALSE;
585 }
586 /* }}} */
587
588
pdo_is_in_transaction(pdo_dbh_t * dbh)589 static bool pdo_is_in_transaction(pdo_dbh_t *dbh) {
590 if (dbh->methods->in_transaction) {
591 return dbh->methods->in_transaction(dbh);
592 }
593 return dbh->in_txn;
594 }
595
596 /* {{{ Initiates a transaction */
PHP_METHOD(PDO,beginTransaction)597 PHP_METHOD(PDO, beginTransaction)
598 {
599 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
600
601 ZEND_PARSE_PARAMETERS_NONE();
602
603 PDO_CONSTRUCT_CHECK;
604
605 if (pdo_is_in_transaction(dbh)) {
606 zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is already an active transaction");
607 RETURN_THROWS();
608 }
609
610 if (!dbh->methods->begin) {
611 /* Throw an exception when the driver does not support transactions */
612 zend_throw_exception_ex(php_pdo_get_exception(), 0, "This driver doesn't support transactions");
613 RETURN_THROWS();
614 }
615
616 if (dbh->methods->begin(dbh)) {
617 dbh->in_txn = true;
618 RETURN_TRUE;
619 }
620
621 PDO_HANDLE_DBH_ERR();
622 RETURN_FALSE;
623 }
624 /* }}} */
625
626 /* {{{ Commit a transaction */
PHP_METHOD(PDO,commit)627 PHP_METHOD(PDO, commit)
628 {
629 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
630
631 ZEND_PARSE_PARAMETERS_NONE();
632
633 PDO_CONSTRUCT_CHECK;
634
635 if (!pdo_is_in_transaction(dbh)) {
636 zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
637 RETURN_THROWS();
638 }
639
640 if (dbh->methods->commit(dbh)) {
641 dbh->in_txn = false;
642 RETURN_TRUE;
643 }
644
645 PDO_HANDLE_DBH_ERR();
646 RETURN_FALSE;
647 }
648 /* }}} */
649
650 /* {{{ roll back a transaction */
PHP_METHOD(PDO,rollBack)651 PHP_METHOD(PDO, rollBack)
652 {
653 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
654
655 ZEND_PARSE_PARAMETERS_NONE();
656
657 PDO_CONSTRUCT_CHECK;
658
659 if (!pdo_is_in_transaction(dbh)) {
660 zend_throw_exception_ex(php_pdo_get_exception(), 0, "There is no active transaction");
661 RETURN_THROWS();
662 }
663
664 if (dbh->methods->rollback(dbh)) {
665 dbh->in_txn = false;
666 RETURN_TRUE;
667 }
668
669 PDO_HANDLE_DBH_ERR();
670 RETURN_FALSE;
671 }
672 /* }}} */
673
674 /* {{{ determine if inside a transaction */
PHP_METHOD(PDO,inTransaction)675 PHP_METHOD(PDO, inTransaction)
676 {
677 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
678
679 ZEND_PARSE_PARAMETERS_NONE();
680
681 PDO_CONSTRUCT_CHECK;
682
683 RETURN_BOOL(pdo_is_in_transaction(dbh));
684 }
685 /* }}} */
686
pdo_get_long_param(zend_long * lval,zval * value)687 PDO_API bool pdo_get_long_param(zend_long *lval, zval *value)
688 {
689 switch (Z_TYPE_P(value)) {
690 case IS_LONG:
691 case IS_TRUE:
692 case IS_FALSE:
693 *lval = zval_get_long(value);
694 return true;
695 case IS_STRING:
696 if (IS_LONG == is_numeric_str_function(Z_STR_P(value), lval, NULL)) {
697 return true;
698 }
699 ZEND_FALLTHROUGH;
700 default:
701 zend_type_error("Attribute value must be of type int for selected attribute, %s given", zend_zval_type_name(value));
702 return false;
703 }
704 }
pdo_get_bool_param(bool * bval,zval * value)705 PDO_API bool pdo_get_bool_param(bool *bval, zval *value)
706 {
707 switch (Z_TYPE_P(value)) {
708 case IS_TRUE:
709 *bval = true;
710 return true;
711 case IS_FALSE:
712 *bval = false;
713 return true;
714 case IS_LONG:
715 *bval = zval_is_true(value);
716 return true;
717 case IS_STRING: /* TODO Should string be allowed? */
718 default:
719 zend_type_error("Attribute value must be of type bool for selected attribute, %s given", zend_zval_type_name(value));
720 return false;
721 }
722 }
723
724 /* Return false on failure, true otherwise */
pdo_dbh_attribute_set(pdo_dbh_t * dbh,zend_long attr,zval * value)725 static bool pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value) /* {{{ */
726 {
727 zend_long lval;
728 bool bval;
729
730 switch (attr) {
731 case PDO_ATTR_ERRMODE:
732 if (!pdo_get_long_param(&lval, value)) {
733 return false;
734 }
735 switch (lval) {
736 case PDO_ERRMODE_SILENT:
737 case PDO_ERRMODE_WARNING:
738 case PDO_ERRMODE_EXCEPTION:
739 dbh->error_mode = lval;
740 return true;
741 default:
742 zend_value_error("Error mode must be one of the PDO::ERRMODE_* constants");
743 return false;
744 }
745 return false;
746
747 case PDO_ATTR_CASE:
748 if (!pdo_get_long_param(&lval, value)) {
749 return false;
750 }
751 switch (lval) {
752 case PDO_CASE_NATURAL:
753 case PDO_CASE_UPPER:
754 case PDO_CASE_LOWER:
755 dbh->desired_case = lval;
756 return true;
757 default:
758 zend_value_error("Case folding mode must be one of the PDO::CASE_* constants");
759 return false;
760 }
761 return false;
762
763 case PDO_ATTR_ORACLE_NULLS:
764 if (!pdo_get_long_param(&lval, value)) {
765 return false;
766 }
767 /* TODO Check for valid value (NULL_NATURAL, NULL_EMPTY_STRING, NULL_TO_STRING)? */
768 dbh->oracle_nulls = lval;
769 return true;
770
771 case PDO_ATTR_DEFAULT_FETCH_MODE:
772 if (Z_TYPE_P(value) == IS_ARRAY) {
773 zval *tmp;
774 if ((tmp = zend_hash_index_find(Z_ARRVAL_P(value), 0)) != NULL && Z_TYPE_P(tmp) == IS_LONG) {
775 if (Z_LVAL_P(tmp) == PDO_FETCH_INTO || Z_LVAL_P(tmp) == PDO_FETCH_CLASS) {
776 zend_value_error("PDO::FETCH_INTO and PDO::FETCH_CLASS cannot be set as the default fetch mode");
777 return false;
778 }
779 }
780 lval = zval_get_long(value);
781 } else {
782 if (!pdo_get_long_param(&lval, value)) {
783 return false;
784 }
785 }
786 if (lval == PDO_FETCH_USE_DEFAULT) {
787 zend_value_error("Fetch mode must be a bitmask of PDO::FETCH_* constants");
788 return false;
789 }
790 dbh->default_fetch_type = lval;
791 return true;
792
793 case PDO_ATTR_STRINGIFY_FETCHES:
794 if (!pdo_get_bool_param(&bval, value)) {
795 return false;
796 }
797 dbh->stringify = bval;
798 if (dbh->methods->set_attribute) {
799 dbh->methods->set_attribute(dbh, attr, value);
800 }
801 return true;
802
803 case PDO_ATTR_STATEMENT_CLASS: {
804 /* array(string classname, array(mixed ctor_args)) */
805 zend_class_entry *pce;
806 zval *item;
807
808 if (dbh->is_persistent) {
809 /* TODO: ValueError/ PDOException? */
810 pdo_raise_impl_error(dbh, NULL, "HY000",
811 "PDO::ATTR_STATEMENT_CLASS cannot be used with persistent PDO instances"
812 );
813 PDO_HANDLE_DBH_ERR();
814 return false;
815 }
816 if (Z_TYPE_P(value) != IS_ARRAY) {
817 zend_type_error("PDO::ATTR_STATEMENT_CLASS value must be of type array, %s given",
818 zend_zval_type_name(value));
819 return false;
820 }
821 if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 0)) == NULL) {
822 zend_value_error("PDO::ATTR_STATEMENT_CLASS value must be an array with the format "
823 "array(classname, constructor_args)");
824 return false;
825 }
826 if (Z_TYPE_P(item) != IS_STRING || (pce = zend_lookup_class(Z_STR_P(item))) == NULL) {
827 zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be a valid class");
828 return false;
829 }
830 if (!instanceof_function(pce, pdo_dbstmt_ce)) {
831 zend_type_error("PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement");
832 return false;
833 }
834 if (pce->constructor && !(pce->constructor->common.fn_flags & (ZEND_ACC_PRIVATE|ZEND_ACC_PROTECTED))) {
835 zend_type_error("User-supplied statement class cannot have a public constructor");
836 return false;
837 }
838 dbh->def_stmt_ce = pce;
839 if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
840 zval_ptr_dtor(&dbh->def_stmt_ctor_args);
841 ZVAL_UNDEF(&dbh->def_stmt_ctor_args);
842 }
843 if ((item = zend_hash_index_find(Z_ARRVAL_P(value), 1)) != NULL) {
844 if (Z_TYPE_P(item) != IS_ARRAY) {
845 zend_type_error("PDO::ATTR_STATEMENT_CLASS constructor_args must be of type ?array, %s given",
846 zend_zval_type_name(value));
847 return false;
848 }
849 ZVAL_COPY(&dbh->def_stmt_ctor_args, item);
850 }
851 return true;
852 }
853 /* Don't throw a ValueError as the attribute might be a driver specific one */
854 default:;
855 }
856
857 if (!dbh->methods->set_attribute) {
858 goto fail;
859 }
860
861 PDO_DBH_CLEAR_ERR();
862 if (dbh->methods->set_attribute(dbh, attr, value)) {
863 return true;
864 }
865
866 fail:
867 if (!dbh->methods->set_attribute) {
868 pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support setting attributes");
869 } else {
870 PDO_HANDLE_DBH_ERR();
871 }
872 return false;
873 }
874 /* }}} */
875
876 /* {{{ Set an attribute */
PHP_METHOD(PDO,setAttribute)877 PHP_METHOD(PDO, setAttribute)
878 {
879 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
880 zend_long attr;
881 zval *value;
882
883 ZEND_PARSE_PARAMETERS_START(2, 2)
884 Z_PARAM_LONG(attr)
885 Z_PARAM_ZVAL(value)
886 ZEND_PARSE_PARAMETERS_END();
887
888 PDO_DBH_CLEAR_ERR();
889 PDO_CONSTRUCT_CHECK;
890
891 RETURN_BOOL(pdo_dbh_attribute_set(dbh, attr, value));
892 }
893 /* }}} */
894
895 /* {{{ Get an attribute */
PHP_METHOD(PDO,getAttribute)896 PHP_METHOD(PDO, getAttribute)
897 {
898 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
899 zend_long attr;
900
901 ZEND_PARSE_PARAMETERS_START(1, 1)
902 Z_PARAM_LONG(attr)
903 ZEND_PARSE_PARAMETERS_END();
904
905 PDO_DBH_CLEAR_ERR();
906 PDO_CONSTRUCT_CHECK;
907
908 /* handle generic PDO-level attributes */
909 switch (attr) {
910 case PDO_ATTR_PERSISTENT:
911 RETURN_BOOL(dbh->is_persistent);
912
913 case PDO_ATTR_CASE:
914 RETURN_LONG(dbh->desired_case);
915
916 case PDO_ATTR_ORACLE_NULLS:
917 RETURN_LONG(dbh->oracle_nulls);
918
919 case PDO_ATTR_ERRMODE:
920 RETURN_LONG(dbh->error_mode);
921
922 case PDO_ATTR_DRIVER_NAME:
923 RETURN_STRINGL((char*)dbh->driver->driver_name, dbh->driver->driver_name_len);
924
925 case PDO_ATTR_STATEMENT_CLASS:
926 array_init(return_value);
927 add_next_index_str(return_value, zend_string_copy(dbh->def_stmt_ce->name));
928 if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
929 Z_TRY_ADDREF(dbh->def_stmt_ctor_args);
930 add_next_index_zval(return_value, &dbh->def_stmt_ctor_args);
931 }
932 return;
933
934 case PDO_ATTR_DEFAULT_FETCH_MODE:
935 RETURN_LONG(dbh->default_fetch_type);
936
937 case PDO_ATTR_STRINGIFY_FETCHES:
938 RETURN_BOOL(dbh->stringify);
939
940 default:
941 break;
942 }
943
944 if (!dbh->methods->get_attribute) {
945 pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support getting attributes");
946 RETURN_FALSE;
947 }
948
949 switch (dbh->methods->get_attribute(dbh, attr, return_value)) {
950 case -1:
951 PDO_HANDLE_DBH_ERR();
952 RETURN_FALSE;
953
954 case 0:
955 pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support that attribute");
956 RETURN_FALSE;
957
958 default:
959 /* No error state, just return as the return_value has been assigned
960 * by the get_attribute handler */
961 return;
962 }
963 }
964 /* }}} */
965
966 /* {{{ Execute a statement that does not return a row set, returning the number of affected rows */
PHP_METHOD(PDO,exec)967 PHP_METHOD(PDO, exec)
968 {
969 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
970 zend_string *statement;
971 zend_long ret;
972
973 ZEND_PARSE_PARAMETERS_START(1, 1)
974 Z_PARAM_STR(statement)
975 ZEND_PARSE_PARAMETERS_END();
976
977 if (ZSTR_LEN(statement) == 0) {
978 zend_argument_value_error(1, "cannot be empty");
979 RETURN_THROWS();
980 }
981
982 PDO_DBH_CLEAR_ERR();
983 PDO_CONSTRUCT_CHECK;
984 ret = dbh->methods->doer(dbh, statement);
985 if (ret == -1) {
986 PDO_HANDLE_DBH_ERR();
987 RETURN_FALSE;
988 } else {
989 RETURN_LONG(ret);
990 }
991 }
992 /* }}} */
993
994 /* {{{ Returns the id of the last row that we affected on this connection. Some databases require a sequence or table name to be passed in. Not always meaningful. */
PHP_METHOD(PDO,lastInsertId)995 PHP_METHOD(PDO, lastInsertId)
996 {
997 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
998 zend_string *name = NULL;
999 zend_string *last_id = NULL;
1000
1001 ZEND_PARSE_PARAMETERS_START(0, 1)
1002 Z_PARAM_OPTIONAL
1003 Z_PARAM_STR_OR_NULL(name)
1004 ZEND_PARSE_PARAMETERS_END();
1005
1006 PDO_CONSTRUCT_CHECK;
1007
1008 PDO_DBH_CLEAR_ERR();
1009
1010 if (!dbh->methods->last_id) {
1011 pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support lastInsertId()");
1012 RETURN_FALSE;
1013 }
1014 last_id = dbh->methods->last_id(dbh, name);
1015 if (!last_id) {
1016 PDO_HANDLE_DBH_ERR();
1017 RETURN_FALSE;
1018 }
1019 RETURN_STR(last_id);
1020 }
1021 /* }}} */
1022
1023 /* {{{ Fetch the error code associated with the last operation on the database handle */
PHP_METHOD(PDO,errorCode)1024 PHP_METHOD(PDO, errorCode)
1025 {
1026 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
1027
1028 ZEND_PARSE_PARAMETERS_NONE();
1029
1030 PDO_CONSTRUCT_CHECK;
1031
1032 if (dbh->query_stmt) {
1033 RETURN_STRING(dbh->query_stmt->error_code);
1034 }
1035
1036 if (dbh->error_code[0] == '\0') {
1037 RETURN_NULL();
1038 }
1039
1040 /**
1041 * Making sure that we fallback to the default implementation
1042 * if the dbh->error_code is not null.
1043 */
1044 RETURN_STRING(dbh->error_code);
1045 }
1046 /* }}} */
1047
1048 /* {{{ Fetch extended error information associated with the last operation on the database handle */
PHP_METHOD(PDO,errorInfo)1049 PHP_METHOD(PDO, errorInfo)
1050 {
1051 int error_count;
1052 int error_count_diff = 0;
1053 int error_expected_count = 3;
1054
1055 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
1056
1057 ZEND_PARSE_PARAMETERS_NONE();
1058
1059 PDO_CONSTRUCT_CHECK;
1060
1061 array_init(return_value);
1062
1063 if (dbh->query_stmt) {
1064 add_next_index_string(return_value, dbh->query_stmt->error_code);
1065 if(!strncmp(dbh->query_stmt->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1066 } else {
1067 add_next_index_string(return_value, dbh->error_code);
1068 if(!strncmp(dbh->error_code, PDO_ERR_NONE, sizeof(PDO_ERR_NONE))) goto fill_array;
1069 }
1070
1071 if (dbh->methods->fetch_err) {
1072 dbh->methods->fetch_err(dbh, dbh->query_stmt, return_value);
1073 }
1074
1075 fill_array:
1076 /**
1077 * In order to be consistent, we have to make sure we add the good amount
1078 * of nulls depending on the current number of elements. We make a simple
1079 * difference and add the needed elements
1080 */
1081 error_count = zend_hash_num_elements(Z_ARRVAL_P(return_value));
1082
1083 if (error_expected_count > error_count) {
1084 int current_index;
1085
1086 error_count_diff = error_expected_count - error_count;
1087 for (current_index = 0; current_index < error_count_diff; current_index++) {
1088 add_next_index_null(return_value);
1089 }
1090 }
1091 }
1092 /* }}} */
1093
1094 /* {{{ Prepare and execute $sql; returns the statement object for iteration */
PHP_METHOD(PDO,query)1095 PHP_METHOD(PDO, query)
1096 {
1097 pdo_stmt_t *stmt;
1098 zend_string *statement;
1099 zend_long fetch_mode;
1100 bool fetch_mode_is_null = 1;
1101 zval *args = NULL;
1102 uint32_t num_args = 0;
1103 pdo_dbh_object_t *dbh_obj = Z_PDO_OBJECT_P(ZEND_THIS);
1104 pdo_dbh_t *dbh = dbh_obj->inner;
1105
1106 if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "S|l!*", &statement,
1107 &fetch_mode, &fetch_mode_is_null, &args, &num_args)) {
1108 RETURN_THROWS();
1109 }
1110
1111 PDO_CONSTRUCT_CHECK;
1112
1113 if (ZSTR_LEN(statement) == 0) {
1114 zend_argument_value_error(1, "cannot be empty");
1115 RETURN_THROWS();
1116 }
1117
1118 PDO_DBH_CLEAR_ERR();
1119
1120 if (!pdo_stmt_instantiate(dbh, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args)) {
1121 RETURN_THROWS();
1122 }
1123 stmt = Z_PDO_STMT_P(return_value);
1124
1125 /* unconditionally keep this for later reference */
1126 stmt->query_string = zend_string_copy(statement);
1127 stmt->active_query_string = zend_string_copy(stmt->query_string);
1128 stmt->default_fetch_type = dbh->default_fetch_type;
1129 stmt->dbh = dbh;
1130 /* give it a reference to me */
1131 ZVAL_OBJ_COPY(&stmt->database_object_handle, &dbh_obj->std);
1132 /* we haven't created a lazy object yet */
1133 ZVAL_UNDEF(&stmt->lazy_object_ref);
1134
1135 if (dbh->methods->preparer(dbh, statement, stmt, NULL)) {
1136 PDO_STMT_CLEAR_ERR();
1137 if (fetch_mode_is_null || pdo_stmt_setup_fetch_mode(stmt, fetch_mode, 2, args, num_args)) {
1138 /* now execute the statement */
1139 PDO_STMT_CLEAR_ERR();
1140 if (stmt->methods->executer(stmt)) {
1141 bool ret = true;
1142 if (!stmt->executed) {
1143 if (stmt->dbh->alloc_own_columns) {
1144 ret = pdo_stmt_describe_columns(stmt);
1145 }
1146 stmt->executed = 1;
1147 }
1148 if (ret) {
1149 pdo_stmt_construct(execute_data, stmt, return_value, dbh->def_stmt_ce, &dbh->def_stmt_ctor_args);
1150 return;
1151 }
1152 }
1153 }
1154 /* something broke */
1155 dbh->query_stmt = stmt;
1156 ZVAL_OBJ(&dbh->query_stmt_zval, Z_OBJ_P(return_value));
1157 Z_DELREF(stmt->database_object_handle);
1158 ZVAL_UNDEF(&stmt->database_object_handle);
1159 PDO_HANDLE_STMT_ERR();
1160 } else {
1161 PDO_HANDLE_DBH_ERR();
1162 zval_ptr_dtor(return_value);
1163 }
1164
1165 RETURN_FALSE;
1166 }
1167 /* }}} */
1168
1169 /* {{{ quotes string for use in a query.
1170 * The optional paramtype acts as a hint for drivers that have alternate quoting styles.
1171 * The default value is PDO_PARAM_STR */
PHP_METHOD(PDO,quote)1172 PHP_METHOD(PDO, quote)
1173 {
1174 pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
1175 zend_string *str, *quoted;
1176 zend_long paramtype = PDO_PARAM_STR;
1177
1178 ZEND_PARSE_PARAMETERS_START(1, 2)
1179 Z_PARAM_STR(str)
1180 Z_PARAM_OPTIONAL
1181 Z_PARAM_LONG(paramtype)
1182 ZEND_PARSE_PARAMETERS_END();
1183
1184 PDO_CONSTRUCT_CHECK;
1185
1186 PDO_DBH_CLEAR_ERR();
1187 if (!dbh->methods->quoter) {
1188 pdo_raise_impl_error(dbh, NULL, "IM001", "driver does not support quoting");
1189 RETURN_FALSE;
1190 }
1191 quoted = dbh->methods->quoter(dbh, str, paramtype);
1192
1193 if (quoted == NULL) {
1194 PDO_HANDLE_DBH_ERR();
1195 RETURN_FALSE;
1196 }
1197
1198 RETURN_STR(quoted);
1199 }
1200 /* }}} */
1201
1202 /* {{{ Return array of available PDO drivers */
PHP_METHOD(PDO,getAvailableDrivers)1203 PHP_METHOD(PDO, getAvailableDrivers)
1204 {
1205 pdo_driver_t *pdriver;
1206
1207 ZEND_PARSE_PARAMETERS_NONE();
1208
1209 array_init(return_value);
1210
1211 ZEND_HASH_MAP_FOREACH_PTR(&pdo_driver_hash, pdriver) {
1212 add_next_index_stringl(return_value, (char*)pdriver->driver_name, pdriver->driver_name_len);
1213 } ZEND_HASH_FOREACH_END();
1214 }
1215 /* }}} */
1216
cls_method_dtor(zval * el)1217 static void cls_method_dtor(zval *el) /* {{{ */ {
1218 zend_function *func = (zend_function*)Z_PTR_P(el);
1219 if (func->common.function_name) {
1220 zend_string_release_ex(func->common.function_name, 0);
1221 }
1222 if (ZEND_MAP_PTR(func->common.run_time_cache)) {
1223 efree(ZEND_MAP_PTR(func->common.run_time_cache));
1224 }
1225 efree(func);
1226 }
1227 /* }}} */
1228
cls_method_pdtor(zval * el)1229 static void cls_method_pdtor(zval *el) /* {{{ */ {
1230 zend_function *func = (zend_function*)Z_PTR_P(el);
1231 if (func->common.function_name) {
1232 zend_string_release_ex(func->common.function_name, 1);
1233 }
1234 if (ZEND_MAP_PTR(func->common.run_time_cache)) {
1235 pefree(ZEND_MAP_PTR(func->common.run_time_cache), 1);
1236 }
1237 pefree(func, 1);
1238 }
1239 /* }}} */
1240
1241 /* {{{ overloaded object handlers for PDO class */
pdo_hash_methods(pdo_dbh_object_t * dbh_obj,int kind)1242 bool pdo_hash_methods(pdo_dbh_object_t *dbh_obj, int kind)
1243 {
1244 const zend_function_entry *funcs;
1245 zend_internal_function func;
1246 size_t namelen;
1247 char *lc_name;
1248 pdo_dbh_t *dbh = dbh_obj->inner;
1249
1250 if (!dbh || !dbh->methods || !dbh->methods->get_driver_methods) {
1251 return false;
1252 }
1253 funcs = dbh->methods->get_driver_methods(dbh, kind);
1254 if (!funcs) {
1255 return false;
1256 }
1257
1258 dbh->cls_methods[kind] = pemalloc(sizeof(HashTable), dbh->is_persistent);
1259 zend_hash_init(dbh->cls_methods[kind], 8, NULL,
1260 dbh->is_persistent? cls_method_pdtor : cls_method_dtor, dbh->is_persistent);
1261
1262 memset(&func, 0, sizeof(func));
1263
1264 size_t rt_cache_size = zend_internal_run_time_cache_reserved_size();
1265 while (funcs->fname) {
1266 func.type = ZEND_INTERNAL_FUNCTION;
1267 func.handler = funcs->handler;
1268 func.function_name = zend_string_init(funcs->fname, strlen(funcs->fname), dbh->is_persistent);
1269 func.scope = dbh_obj->std.ce;
1270 func.prototype = NULL;
1271 ZEND_MAP_PTR(func.run_time_cache) = rt_cache_size ? pecalloc(rt_cache_size, 1, dbh->is_persistent) : NULL;
1272 func.T = ZEND_OBSERVER_ENABLED;
1273 if (funcs->flags) {
1274 func.fn_flags = funcs->flags | ZEND_ACC_NEVER_CACHE;
1275 } else {
1276 func.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_NEVER_CACHE;
1277 }
1278 if (funcs->arg_info) {
1279 zend_internal_function_info *info = (zend_internal_function_info*)funcs->arg_info;
1280
1281 func.arg_info = (zend_internal_arg_info*)funcs->arg_info + 1;
1282 func.num_args = funcs->num_args;
1283 if (info->required_num_args == (uint32_t)-1) {
1284 func.required_num_args = funcs->num_args;
1285 } else {
1286 func.required_num_args = info->required_num_args;
1287 }
1288 if (ZEND_ARG_SEND_MODE(info)) {
1289 func.fn_flags |= ZEND_ACC_RETURN_REFERENCE;
1290 }
1291 if (ZEND_ARG_IS_VARIADIC(&funcs->arg_info[funcs->num_args])) {
1292 func.fn_flags |= ZEND_ACC_VARIADIC;
1293 /* Don't count the variadic argument */
1294 func.num_args--;
1295 }
1296 } else {
1297 func.arg_info = NULL;
1298 func.num_args = 0;
1299 func.required_num_args = 0;
1300 }
1301 zend_set_function_arg_flags((zend_function*)&func);
1302 namelen = strlen(funcs->fname);
1303 lc_name = emalloc(namelen+1);
1304 zend_str_tolower_copy(lc_name, funcs->fname, namelen);
1305 zend_hash_str_add_mem(dbh->cls_methods[kind], lc_name, namelen, &func, sizeof(func));
1306 efree(lc_name);
1307 funcs++;
1308 }
1309
1310 return true;
1311 }
1312
dbh_method_get(zend_object ** object,zend_string * method_name,const zval * key)1313 static zend_function *dbh_method_get(zend_object **object, zend_string *method_name, const zval *key)
1314 {
1315 zend_function *fbc = NULL;
1316 pdo_dbh_object_t *dbh_obj = php_pdo_dbh_fetch_object(*object);
1317 zend_string *lc_method_name;
1318
1319 if ((fbc = zend_std_get_method(object, method_name, key)) == NULL) {
1320 /* not a pre-defined method, nor a user-defined method; check
1321 * the driver specific methods */
1322 if (!dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1323 if (!pdo_hash_methods(dbh_obj,
1324 PDO_DBH_DRIVER_METHOD_KIND_DBH)
1325 || !dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH]) {
1326 goto out;
1327 }
1328 }
1329
1330 lc_method_name = zend_string_tolower(method_name);
1331 fbc = zend_hash_find_ptr(dbh_obj->inner->cls_methods[PDO_DBH_DRIVER_METHOD_KIND_DBH], lc_method_name);
1332 zend_string_release_ex(lc_method_name, 0);
1333 }
1334
1335 out:
1336 return fbc;
1337 }
1338
dbh_get_gc(zend_object * object,zval ** gc_data,int * gc_count)1339 static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count)
1340 {
1341 pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(object);
1342 zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
1343 zend_get_gc_buffer_add_zval(gc_buffer, &dbh->def_stmt_ctor_args);
1344 if (dbh->methods && dbh->methods->get_gc) {
1345 dbh->methods->get_gc(dbh, gc_buffer);
1346 }
1347 zend_get_gc_buffer_use(gc_buffer, gc_data, gc_count);
1348 return zend_std_get_properties(object);
1349 }
1350
1351 static zend_object_handlers pdo_dbh_object_handlers;
1352 static void pdo_dbh_free_storage(zend_object *std);
1353
pdo_dbh_init(int module_number)1354 void pdo_dbh_init(int module_number)
1355 {
1356 pdo_dbh_ce = register_class_PDO();
1357 pdo_dbh_ce->create_object = pdo_dbh_new;
1358
1359 memcpy(&pdo_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1360 pdo_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std);
1361 pdo_dbh_object_handlers.free_obj = pdo_dbh_free_storage;
1362 pdo_dbh_object_handlers.clone_obj = NULL;
1363 pdo_dbh_object_handlers.get_method = dbh_method_get;
1364 pdo_dbh_object_handlers.compare = zend_objects_not_comparable;
1365 pdo_dbh_object_handlers.get_gc = dbh_get_gc;
1366 }
1367
dbh_free(pdo_dbh_t * dbh,bool free_persistent)1368 static void dbh_free(pdo_dbh_t *dbh, bool free_persistent)
1369 {
1370 int i;
1371
1372 if (dbh->query_stmt) {
1373 zval_ptr_dtor(&dbh->query_stmt_zval);
1374 dbh->query_stmt = NULL;
1375 }
1376
1377 if (dbh->is_persistent) {
1378 #if ZEND_DEBUG
1379 ZEND_ASSERT(!free_persistent || (dbh->refcount == 1));
1380 #endif
1381 if (!free_persistent && (--dbh->refcount)) {
1382 return;
1383 }
1384 }
1385
1386 if (dbh->methods) {
1387 dbh->methods->closer(dbh);
1388 }
1389
1390 if (dbh->data_source) {
1391 pefree((char *)dbh->data_source, dbh->is_persistent);
1392 }
1393 if (dbh->username) {
1394 pefree(dbh->username, dbh->is_persistent);
1395 }
1396 if (dbh->password) {
1397 pefree(dbh->password, dbh->is_persistent);
1398 }
1399
1400 if (dbh->persistent_id) {
1401 pefree((char *)dbh->persistent_id, dbh->is_persistent);
1402 }
1403
1404 if (!Z_ISUNDEF(dbh->def_stmt_ctor_args)) {
1405 zval_ptr_dtor(&dbh->def_stmt_ctor_args);
1406 }
1407
1408 for (i = 0; i < PDO_DBH_DRIVER_METHOD_KIND__MAX; i++) {
1409 if (dbh->cls_methods[i]) {
1410 zend_hash_destroy(dbh->cls_methods[i]);
1411 pefree(dbh->cls_methods[i], dbh->is_persistent);
1412 }
1413 }
1414
1415 pefree(dbh, dbh->is_persistent);
1416 }
1417
pdo_dbh_free_storage(zend_object * std)1418 static void pdo_dbh_free_storage(zend_object *std)
1419 {
1420 pdo_dbh_t *dbh = php_pdo_dbh_fetch_inner(std);
1421 if (dbh->driver_data && dbh->methods && dbh->methods->rollback && pdo_is_in_transaction(dbh)) {
1422 dbh->methods->rollback(dbh);
1423 dbh->in_txn = false;
1424 }
1425
1426 if (dbh->is_persistent && dbh->methods && dbh->methods->persistent_shutdown) {
1427 dbh->methods->persistent_shutdown(dbh);
1428 }
1429 zend_object_std_dtor(std);
1430 dbh_free(dbh, 0);
1431 }
1432
pdo_dbh_new(zend_class_entry * ce)1433 zend_object *pdo_dbh_new(zend_class_entry *ce)
1434 {
1435 pdo_dbh_object_t *dbh;
1436
1437 dbh = zend_object_alloc(sizeof(pdo_dbh_object_t), ce);
1438 zend_object_std_init(&dbh->std, ce);
1439 object_properties_init(&dbh->std, ce);
1440 rebuild_object_properties(&dbh->std);
1441 dbh->inner = ecalloc(1, sizeof(pdo_dbh_t));
1442 dbh->inner->def_stmt_ce = pdo_dbstmt_ce;
1443
1444 dbh->std.handlers = &pdo_dbh_object_handlers;
1445
1446 return &dbh->std;
1447 }
1448
1449 /* }}} */
1450
ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor)1451 ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor) /* {{{ */
1452 {
1453 if (res->ptr) {
1454 pdo_dbh_t *dbh = (pdo_dbh_t*)res->ptr;
1455 dbh_free(dbh, 1);
1456 res->ptr = NULL;
1457 }
1458 }
1459 /* }}} */
1460