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