Commit d8d55b75c5d for php
commit d8d55b75c5dd3342ae6762113847c3f6b986f172
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Tue Oct 13 08:32:11 2026 -0400
ext/pdo: Report $options as the argument for constructor option errors
An attribute in the $options array of PDO::__construct() or PDO::connect()
that fails validation was reported against argument #3 ($password) instead
of argument #4 ($options), because php_pdo_internal_construct_driver()
passed a value_arg_num of 3 to pdo_dbh_attribute_set().
Closes GH-24297
diff --git a/NEWS b/NEWS
index d589da31754..07e265d2193 100644
--- a/NEWS
+++ b/NEWS
@@ -51,6 +51,8 @@ PHP NEWS
- PDO:
. Fixed a duplicate warning when ATTR_STATEMENT_CLASS is rejected on a
persistent connection. (Ilia Alshanetsky)
+ . Fixed constructor option errors naming argument #3 ($password) instead
+ of argument #4 ($options). (Ilia Alshanetsky)
- Phpdbg:
. Fix wrong command names. (ndossche, Weilin Du)
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index f5a16f20aef..fcb1ff4498a 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -516,7 +516,7 @@ PDO_API void php_pdo_internal_construct_driver(INTERNAL_FUNCTION_PARAMETERS, zen
ZVAL_DEREF(attr_value);
/* TODO: Should the constructor fail when the attribute cannot be set? */
- pdo_dbh_attribute_set(dbh, long_key, attr_value, 3);
+ pdo_dbh_attribute_set(dbh, long_key, attr_value, 4);
} ZEND_HASH_FOREACH_END();
}
diff --git a/ext/pdo/tests/pdo_constructor_option_argnum.phpt b/ext/pdo/tests/pdo_constructor_option_argnum.phpt
new file mode 100644
index 00000000000..5aebe0b4a2a
--- /dev/null
+++ b/ext/pdo/tests/pdo_constructor_option_argnum.phpt
@@ -0,0 +1,38 @@
+--TEST--
+PDO Common: Constructor option errors report $options as argument #4
+--EXTENSIONS--
+pdo
+--SKIPIF--
+<?php
+$dir = getenv('REDIR_TEST_DIR');
+if (false == $dir) die('skip no driver');
+require_once $dir . 'pdo_test.inc';
+PDOTest::skip();
+?>
+--FILE--
+<?php
+if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
+require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
+
+foreach ([false, true] as $useConnectMethod) {
+ foreach ([[PDO::ATTR_ERRMODE => 999], [PDO::ATTR_STATEMENT_CLASS => 'PDOStatement']] as $options) {
+ putenv("PDOTEST_ATTR=" . serialize($options));
+ try {
+ PDOTest::factory(PDO::class, $useConnectMethod);
+ } catch (Error $e) {
+ echo $e::class, ": ", $e->getMessage(), "\n";
+ }
+ }
+
+ putenv("PDOTEST_ATTR=" . serialize([PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM]));
+ $db = PDOTest::factory(PDO::class, $useConnectMethod);
+ var_dump($db->getAttribute(PDO::ATTR_DEFAULT_FETCH_MODE) === PDO::FETCH_NUM);
+}
+?>
+--EXPECT--
+ValueError: PDO::__construct(): Argument #4 ($options) Error mode must be one of the PDO::ERRMODE_* constants
+TypeError: PDO::__construct(): Argument #4 ($options) PDO::ATTR_STATEMENT_CLASS value must be of type array, string given
+bool(true)
+ValueError: PDO::connect(): Argument #4 ($options) Error mode must be one of the PDO::ERRMODE_* constants
+TypeError: PDO::connect(): Argument #4 ($options) PDO::ATTR_STATEMENT_CLASS value must be of type array, string given
+bool(true)