From 2a66f72a0fb8f6931c045340c0cad2e485030d0b Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 13 Jan 2025 12:48:28 +0000 Subject: [PATCH] ext/pdo: Rename and add tests for PDO::FETCH_CLASS fetch mode --- ...do_005.phpt => pdo_fetch_class_basic.phpt} | 0 .../pdo_fetch_class_by_ref_constructor.phpt | 81 +++++++++++++++++ ...fetch_class_ctor_with_named_arguments.phpt | 81 +++++++++++++++++ ...amed_arguments_positional_after_named.phpt | 86 +++++++++++++++++++ .../pdo_fetch_class_private_constructor.phpt | 85 ++++++++++++++++++ ... pdo_fetch_class_with_classtype_flag.phpt} | 0 ... pdo_fetch_class_with_serialize_flag.phpt} | 0 ext/pdo/tests/pdo_stmt_class_ctor_errors.phpt | 51 +++++++++++ ...t_class_ctor_errors_cannot_find_class.phpt | 45 ++++++++++ 9 files changed, 429 insertions(+) rename ext/pdo/tests/{pdo_005.phpt => pdo_fetch_class_basic.phpt} (100%) create mode 100644 ext/pdo/tests/pdo_fetch_class_by_ref_constructor.phpt create mode 100644 ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments.phpt create mode 100644 ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments_positional_after_named.phpt create mode 100644 ext/pdo/tests/pdo_fetch_class_private_constructor.phpt rename ext/pdo/tests/{pdo_009.phpt => pdo_fetch_class_with_classtype_flag.phpt} (100%) rename ext/pdo/tests/{pdo_018.phpt => pdo_fetch_class_with_serialize_flag.phpt} (100%) create mode 100644 ext/pdo/tests/pdo_stmt_class_ctor_errors.phpt create mode 100644 ext/pdo/tests/pdo_stmt_class_ctor_errors_cannot_find_class.phpt diff --git a/ext/pdo/tests/pdo_005.phpt b/ext/pdo/tests/pdo_fetch_class_basic.phpt similarity index 100% rename from ext/pdo/tests/pdo_005.phpt rename to ext/pdo/tests/pdo_fetch_class_basic.phpt diff --git a/ext/pdo/tests/pdo_fetch_class_by_ref_constructor.phpt b/ext/pdo/tests/pdo_fetch_class_by_ref_constructor.phpt new file mode 100644 index 0000000000000..7523a6cf0b764 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_class_by_ref_constructor.phpt @@ -0,0 +1,81 @@ +--TEST-- +PDO Common: PDO::FETCH_CLASS with by-ref constructor and arg by value +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_class_by_ref_ctor(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_class_by_ref_ctor VALUES(1, 'A', 'AA')"); +$db->exec("INSERT INTO pdo_fetch_class_by_ref_ctor VALUES(2, 'B', 'BB')"); +$db->exec("INSERT INTO pdo_fetch_class_by_ref_ctor VALUES(3, 'C', 'CC')"); +$stmt = $db->prepare('SELECT id, val FROM pdo_fetch_class_by_ref_ctor'); + +class TestByRefCtor +{ + public $id; + public $val; + private $str; + + public function __construct(string &$str) + { + echo __METHOD__ . "($str, {$this->id})\n"; + $str .= $this->val; + $this->str = $str; + } +} + +$stmt->execute(); +// Use of random_int(10,10) is to defeat SCCP +var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestByRefCtor', [str_repeat('a', random_int(10,10))])); + +?> +--CLEAN-- + +--EXPECTF-- +TestByRefCtor::__construct(aaaaaaaaaa, 1) +TestByRefCtor::__construct(aaaaaaaaaaA, 2) +TestByRefCtor::__construct(aaaaaaaaaaAB, 3) +array(3) { + [0]=> + object(TestByRefCtor)#%d (3) { + ["id"]=> + string(1) "1" + ["val"]=> + string(1) "A" + ["str":"TestByRefCtor":private]=> + string(11) "aaaaaaaaaaA" + } + [1]=> + object(TestByRefCtor)#%d (3) { + ["id"]=> + string(1) "2" + ["val"]=> + string(1) "B" + ["str":"TestByRefCtor":private]=> + string(12) "aaaaaaaaaaAB" + } + [2]=> + object(TestByRefCtor)#%d (3) { + ["id"]=> + string(1) "3" + ["val"]=> + string(1) "C" + ["str":"TestByRefCtor":private]=> + string(13) "aaaaaaaaaaABC" + } +} diff --git a/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments.phpt b/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments.phpt new file mode 100644 index 0000000000000..95ded7d0b7606 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments.phpt @@ -0,0 +1,81 @@ +--TEST-- +PDO Common: PDO::FETCH_CLASS using named arguments in constructor array +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_class_ctor_named(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named VALUES(1, 'A', 'AA')"); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named VALUES(2, 'B', 'BB')"); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named VALUES(3, 'C', 'CC')"); + +$stmt = $db->prepare('SELECT id, val, val2 from pdo_fetch_class_ctor_named'); + +class TestBase +{ + public $id; + protected $val; + private $val2; + + public function __construct(string $a, string $b) { + echo 'Value of $a: ', $a, PHP_EOL, + 'Value of $b: ', $b, PHP_EOL; + } +} +$stmt->execute(); +var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestBase', ['b' => 'My key is B', 'a' => 'My key is A'])); + +?> +--CLEAN-- + +--EXPECTF-- +Value of $a: My key is B +Value of $b: My key is A +Value of $a: My key is B +Value of $b: My key is A +Value of $a: My key is B +Value of $b: My key is A +array(3) { + [0]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "1" + ["val":protected]=> + string(1) "A" + ["val2":"TestBase":private]=> + string(2) "AA" + } + [1]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "2" + ["val":protected]=> + string(1) "B" + ["val2":"TestBase":private]=> + string(2) "BB" + } + [2]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "3" + ["val":protected]=> + string(1) "C" + ["val2":"TestBase":private]=> + string(2) "CC" + } +} diff --git a/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments_positional_after_named.phpt b/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments_positional_after_named.phpt new file mode 100644 index 0000000000000..7d36a022bd210 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_class_ctor_with_named_arguments_positional_after_named.phpt @@ -0,0 +1,86 @@ +--TEST-- +PDO Common: PDO::FETCH_CLASS using mixed string and int arguments in constructor array +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_class_ctor_named_and_positional(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named_and_positional VALUES(1, 'A', 'AA')"); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named_and_positional VALUES(2, 'B', 'BB')"); +$db->exec("INSERT INTO pdo_fetch_class_ctor_named_and_positional VALUES(3, 'C', 'CC')"); + +$stmt = $db->prepare('SELECT id, val, val2 from pdo_fetch_class_ctor_named_and_positional'); + +class TestBase +{ + public $id; + protected $val; + private $val2; + + public function __construct(string $a, string $b) { + echo 'Value of $a: ', $a, PHP_EOL, + 'Value of $b: ', $b, PHP_EOL; + } +} +$stmt->execute(); + +try { + var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestBase', ['b' => 'My key is B', 'No key'])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +?> +--CLEAN-- + +--EXPECTF-- +Value of $a: My key is B +Value of $b: No key +Value of $a: My key is B +Value of $b: No key +Value of $a: My key is B +Value of $b: No key +array(3) { + [0]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "1" + ["val":protected]=> + string(1) "A" + ["val2":"TestBase":private]=> + string(2) "AA" + } + [1]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "2" + ["val":protected]=> + string(1) "B" + ["val2":"TestBase":private]=> + string(2) "BB" + } + [2]=> + object(TestBase)#%d (3) { + ["id"]=> + string(1) "3" + ["val":protected]=> + string(1) "C" + ["val2":"TestBase":private]=> + string(2) "CC" + } +} diff --git a/ext/pdo/tests/pdo_fetch_class_private_constructor.phpt b/ext/pdo/tests/pdo_fetch_class_private_constructor.phpt new file mode 100644 index 0000000000000..7282e45b05ef6 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_class_private_constructor.phpt @@ -0,0 +1,85 @@ +--TEST-- +PDO Common: PDO::FETCH_CLASS with private constructor and arg by value +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_class_private_ctor(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_class_private_ctor VALUES(1, 'A', 'AA')"); +$db->exec("INSERT INTO pdo_fetch_class_private_ctor VALUES(2, 'B', 'BB')"); +$stmt = $db->prepare('SELECT id, val FROM pdo_fetch_class_private_ctor'); + +class TestPrivateCtor +{ + public $id; + public $val; + + private function __construct(string $str) + { + echo __METHOD__ . "($str, {$this->id})\n"; + } +} + +class TestDerivedPrivateCtor extends TestPrivateCtor {} + +$stmt->execute(); +var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestPrivateCtor', ['test'])); + +$stmt->execute(); +var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestDerivedPrivateCtor', ['testFromDerived'])); + +?> +--CLEAN-- + +--EXPECTF-- +TestPrivateCtor::__construct(test, 1) +TestPrivateCtor::__construct(test, 2) +array(2) { + [0]=> + object(TestPrivateCtor)#%d (2) { + ["id"]=> + string(1) "1" + ["val"]=> + string(1) "A" + } + [1]=> + object(TestPrivateCtor)#%d (2) { + ["id"]=> + string(1) "2" + ["val"]=> + string(1) "B" + } +} +TestPrivateCtor::__construct(testFromDerived, 1) +TestPrivateCtor::__construct(testFromDerived, 2) +array(2) { + [0]=> + object(TestDerivedPrivateCtor)#%d (2) { + ["id"]=> + string(1) "1" + ["val"]=> + string(1) "A" + } + [1]=> + object(TestDerivedPrivateCtor)#%d (2) { + ["id"]=> + string(1) "2" + ["val"]=> + string(1) "B" + } +} diff --git a/ext/pdo/tests/pdo_009.phpt b/ext/pdo/tests/pdo_fetch_class_with_classtype_flag.phpt similarity index 100% rename from ext/pdo/tests/pdo_009.phpt rename to ext/pdo/tests/pdo_fetch_class_with_classtype_flag.phpt diff --git a/ext/pdo/tests/pdo_018.phpt b/ext/pdo/tests/pdo_fetch_class_with_serialize_flag.phpt similarity index 100% rename from ext/pdo/tests/pdo_018.phpt rename to ext/pdo/tests/pdo_fetch_class_with_serialize_flag.phpt diff --git a/ext/pdo/tests/pdo_stmt_class_ctor_errors.phpt b/ext/pdo/tests/pdo_stmt_class_ctor_errors.phpt new file mode 100644 index 0000000000000..3a2984e0b3ce8 --- /dev/null +++ b/ext/pdo/tests/pdo_stmt_class_ctor_errors.phpt @@ -0,0 +1,51 @@ +--TEST-- +PDO Common: Setting PDO::FETCH_CLASS with ctor_args when class has no constructor +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_all_class_ctor_error(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_all_class_ctor_error VALUES(1, 'A', 'AA')"); + +$stmt = $db->prepare('SELECT id, val, val2 from pdo_fetch_all_class_ctor_error'); + +class TestBase +{ + public $id; + protected $val; + private $val2; +} + +$stmt->execute(); +try { + var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'TestBase', [0])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} +try { + var_dump($stmt->setFetchMode(PDO::FETCH_CLASS, 'TestBase', [0])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +?> +--CLEAN-- + +--EXPECT-- +Error: User-supplied statement does not accept constructor arguments +Error: User-supplied statement does not accept constructor arguments diff --git a/ext/pdo/tests/pdo_stmt_class_ctor_errors_cannot_find_class.phpt b/ext/pdo/tests/pdo_stmt_class_ctor_errors_cannot_find_class.phpt new file mode 100644 index 0000000000000..0bd039d864339 --- /dev/null +++ b/ext/pdo/tests/pdo_stmt_class_ctor_errors_cannot_find_class.phpt @@ -0,0 +1,45 @@ +--TEST-- +PDO Common: Setting PDO::FETCH_CLASS with an unknown class +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_all_class_error_unknown(id int NOT NULL PRIMARY KEY, val VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_all_class_error_unknown VALUES(1, 'A', 'AA')"); + +$stmt = $db->prepare('SELECT id, val, val2 from pdo_fetch_all_class_error_unknown'); +$stmt->execute(); + +try { + var_dump($stmt->setFetchMode(PDO::FETCH_CLASS, 'Unknown')); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} +try { + var_dump($stmt->fetchAll(PDO::FETCH_CLASS, 'Unknown')); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +?> +--CLEAN-- + +--EXPECTF-- +TypeError: PDOStatement::setFetchMode(): Argument #2 must be a valid class + +Fatal error: Class "Unknown" not found in %s on line %d