diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index 2918129c4b647..3ab6e9040bd1b 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -625,7 +625,6 @@ static bool do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */ if (ce->constructor) { ZVAL_UNDEF(&fci->function_name); - fci->retval = &stmt->fetch.cls.retval; fci->param_count = 0; fci->params = NULL; @@ -643,11 +642,11 @@ static bool do_fetch_class_prepare(pdo_stmt_t *stmt) /* {{{ */ } /* }}} */ -static bool make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args) /* {{{ */ +static bool pdo_stmt_init_fci_fcc_for_function_fetch_mode(pdo_stmt_t *stmt, zval *callable) { char *is_callable_error = NULL; - if (zend_fcall_info_init(callable, 0, fci, fcc, NULL, &is_callable_error) == FAILURE) { + if (zend_fcall_info_init(callable, 0, &stmt->fetch.func.fci, &stmt->fetch.func.fcc, NULL, &is_callable_error) == FAILURE) { if (is_callable_error) { zend_type_error("%s", is_callable_error); efree(is_callable_error); @@ -656,33 +655,16 @@ static bool make_callable_ex(pdo_stmt_t *stmt, zval *callable, zend_fcall_info * } return false; } - if (is_callable_error) { - /* Possible error message */ - efree(is_callable_error); - } + ZEND_ASSERT(is_callable_error == NULL); - fci->param_count = num_args; /* probably less */ - fci->params = safe_emalloc(sizeof(zval), num_args, 0); + uint32_t num_args = stmt->column_count; + stmt->fetch.func.fci.param_count = num_args; /* probably less */ + stmt->fetch.func.fci.params = safe_emalloc(sizeof(zval), num_args, 0); return true; } -/* }}} */ -static bool do_fetch_func_prepare(pdo_stmt_t *stmt) /* {{{ */ -{ - zend_fcall_info *fci = &stmt->fetch.cls.fci; - zend_fcall_info_cache *fcc = &stmt->fetch.cls.fcc; - - if (!make_callable_ex(stmt, &stmt->fetch.func.function, fci, fcc, stmt->column_count)) { - return false; - } else { - stmt->fetch.func.values = safe_emalloc(sizeof(zval), stmt->column_count, 0); - return true; - } -} -/* }}} */ - -static void do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs) /* {{{ */ +static void do_fetch_opt_finish(pdo_stmt_t *stmt, bool free_ctor_agrs) /* {{{ */ { /* fci.size is used to check if it is valid */ if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) { @@ -701,10 +683,6 @@ static void do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs) /* {{{ */ ZVAL_UNDEF(&stmt->fetch.cls.ctor_args); stmt->fetch.cls.fci.param_count = 0; } - if (stmt->fetch.func.values) { - efree(stmt->fetch.func.values); - stmt->fetch.func.values = NULL; - } } /* }}} */ @@ -860,18 +838,18 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h } } if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) { + zval retval_constructor_call; + stmt->fetch.cls.fci.retval = &retval_constructor_call; stmt->fetch.cls.fci.object = Z_OBJ_P(return_value); stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value); - if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) { - /* TODO Error? */ - pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor"); - return 0; - } else { - if (!Z_ISUNDEF(stmt->fetch.cls.retval)) { - zval_ptr_dtor(&stmt->fetch.cls.retval); - ZVAL_UNDEF(&stmt->fetch.cls.retval); - } + zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc); + if (Z_TYPE(retval_constructor_call) == IS_UNDEF) { + /* Exception has happened */ + zval_ptr_dtor(return_value); + return false; } + zval_ptr_dtor(&retval_constructor_call); + ZVAL_UNDEF(stmt->fetch.cls.fci.retval); } } break; @@ -894,16 +872,10 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h case PDO_FETCH_FUNC: /* TODO: Make this an assertion and ensure this is true higher up? */ - if (Z_ISUNDEF(stmt->fetch.func.function)) { + if (!ZEND_FCI_INITIALIZED(stmt->fetch.func.fci)) { /* TODO ArgumentCountError? */ pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "No fetch function specified"); - return 0; - } - if (!stmt->fetch.func.fci.size) { - if (!do_fetch_func_prepare(stmt)) - { - return 0; - } + return false; } break; EMPTY_SWITCH_DEFAULT_CASE(); @@ -1002,8 +974,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h break; case PDO_FETCH_FUNC: - ZVAL_COPY_VALUE(&stmt->fetch.func.values[idx], &val); - ZVAL_COPY_VALUE(&stmt->fetch.cls.fci.params[idx], &stmt->fetch.func.values[idx]); + ZVAL_COPY_VALUE(&stmt->fetch.func.fci.params[idx], &val); break; default: @@ -1016,17 +987,18 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h switch (how) { case PDO_FETCH_CLASS: if (ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) { + zval retval_constructor_call; + stmt->fetch.cls.fci.retval = &retval_constructor_call; stmt->fetch.cls.fci.object = Z_OBJ_P(return_value); stmt->fetch.cls.fcc.object = Z_OBJ_P(return_value); - if (zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc) == FAILURE) { - /* TODO Error? */ - pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "could not call class constructor"); - return 0; - } else { - if (!Z_ISUNDEF(stmt->fetch.cls.retval)) { - zval_ptr_dtor(&stmt->fetch.cls.retval); - } + zend_call_function(&stmt->fetch.cls.fci, &stmt->fetch.cls.fcc); + if (Z_TYPE(retval_constructor_call) == IS_UNDEF) { + /* Exception has happened */ + zval_ptr_dtor(return_value); + return false; } + zval_ptr_dtor(&retval_constructor_call); + ZVAL_UNDEF(stmt->fetch.cls.fci.retval); } if (flags & PDO_FETCH_CLASSTYPE) { do_fetch_opt_finish(stmt, 0); @@ -1048,8 +1020,9 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h ZVAL_COPY_VALUE(return_value, &retval); } } - while (idx--) { - zval_ptr_dtor(&stmt->fetch.func.values[idx]); + /* Free FCI parameters that were allocated in the previous loop */ + for (uint32_t param_num = 0; param_num < stmt->fetch.func.fci.param_count; param_num++) { + zval_ptr_dtor(&stmt->fetch.func.fci.params[param_num]); } break; @@ -1298,9 +1271,7 @@ PHP_METHOD(PDOStatement, fetchAll) zend_argument_type_error(2, "must be a callable, null given"); RETURN_THROWS(); } - /* TODO Check it is a callable? */ - ZVAL_COPY_VALUE(&stmt->fetch.func.function, arg2); - if (do_fetch_func_prepare(stmt) == false) { + if (pdo_stmt_init_fci_fcc_for_function_fetch_mode(stmt, arg2) == false) { RETURN_THROWS(); } break; diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index d09a4c0fbc353..cd496a74f9ed3 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -615,16 +615,12 @@ struct _pdo_stmt_t { zval ctor_args; /* freed */ zend_fcall_info fci; zend_fcall_info_cache fcc; - zval retval; zend_class_entry *ce; } cls; struct { - zval fetch_args; /* freed */ + zval dummy; /* This exists due to alignment reasons with fetch.into and fetch.cls.ctor_args */ zend_fcall_info fci; zend_fcall_info_cache fcc; - zval object; - zval function; - zval *values; /* freed */ } func; zval into; } fetch; diff --git a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject_ctor_args.phpt b/ext/pdo/tests/pdo_stmt_fetchobject_ctor_args.phpt similarity index 60% rename from ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject_ctor_args.phpt rename to ext/pdo/tests/pdo_stmt_fetchobject_ctor_args.phpt index ed5a67ed05b00..cb98ebca6c3e5 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_stmt_fetchobject_ctor_args.phpt +++ b/ext/pdo/tests/pdo_stmt_fetchobject_ctor_args.phpt @@ -1,33 +1,27 @@ --TEST-- -MySQL PDO: PDOStatement->fetchObject() with $constructorArgs +PDO Common: PDOStatement->fetchObject() with $constructorArgs --EXTENSIONS-- -pdo_mysql +pdo --SKIPIF-- prepare($query); - $ok = $stmt->execute(); -} catch (PDOException $e) { - die("skip: Test cannot be run with SQL mode ANSI"); -} -if (!$ok) - die("skip: Test cannot be run with SQL mode ANSI"); +$dir = getenv('REDIR_TEST_DIR'); +if (false == $dir) die('skip no driver'); +require_once $dir . 'pdo_test.inc'; +PDOTest::skip(); ?> --FILE-- exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))"); +$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')"); +$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')"); +$db->exec("INSERT INTO {$table} (id, label) VALUES (3, 'c')"); -$query = "SELECT id FROM {$table} ORDER BY id ASC LIMIT 1"; +$query = "SELECT id FROM {$table} ORDER BY id ASC"; $stmt = $db->prepare($query); class Foo { @@ -79,9 +73,9 @@ try { ?> --CLEAN-- exec('DROP TABLE IF EXISTS pdo_mysql_stmt_fetchobject_ctor_args'); +require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; +$db = PDOTest::factory(); +PDOTest::dropTableIfExists($db, "pdo_stmt_fetchobject_ctor_args"); ?> --EXPECTF-- Too few arguments to function Foo::__construct(), 0 passed and exactly 1 expected