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