From 15d3b10c35db0dfb3dd1d1c489b8053232c0bf2f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jan 2025 18:41:30 +0000 Subject: [PATCH 1/2] ext/pdo: Improve testing for FETCH_FUNC mode --- ext/pdo/tests/pdo_011.phpt | 305 ------------------ ext/pdo/tests/pdo_fetch_func_001.phpt | 108 +++++++ ext/pdo/tests/pdo_fetch_function_basic.phpt | 99 ++++++ .../pdo_fetch_function_incorrect_call.phpt | 50 +++ ...do_fetch_function_incorrect_callables.phpt | 89 +++++ ...tch_function_overload_statement_class.phpt | 65 ++++ .../pdo_fetch_function_with_grouping.phpt | 63 ++++ .../tests/pdo_fetch_function_with_unique.phpt | 55 ++++ ...tch_function_with_unique_and_grouping.phpt | 52 +++ ext/pdo/tests/pdo_test.inc | 19 +- ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt | 151 --------- 11 files changed, 599 insertions(+), 457 deletions(-) delete mode 100644 ext/pdo/tests/pdo_011.phpt create mode 100644 ext/pdo/tests/pdo_fetch_func_001.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_basic.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_incorrect_call.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_overload_statement_class.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_with_grouping.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_with_unique.phpt create mode 100644 ext/pdo/tests/pdo_fetch_function_with_unique_and_grouping.phpt delete mode 100644 ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt diff --git a/ext/pdo/tests/pdo_011.phpt b/ext/pdo/tests/pdo_011.phpt deleted file mode 100644 index 88c2677c0899..000000000000 --- a/ext/pdo/tests/pdo_011.phpt +++ /dev/null @@ -1,305 +0,0 @@ ---TEST-- -PDO Common: PDO::FETCH_FUNC and statement overloading ---EXTENSIONS-- -pdo ---SKIPIF-- - ---FILE-- -exec('CREATE TABLE test011(id int NOT NULL PRIMARY KEY, val VARCHAR(10), grp VARCHAR(10))'); -$db->exec("INSERT INTO test011 VALUES(1, 'A', 'Group1')"); -$db->exec("INSERT INTO test011 VALUES(2, 'B', 'Group1')"); -$db->exec("INSERT INTO test011 VALUES(3, 'C', 'Group2')"); -$db->exec("INSERT INTO test011 VALUES(4, 'D', 'Group2')"); - -class DerivedStatement extends PDOStatement -{ - private function __construct(public $name, $db) - { - echo __METHOD__ . "($name)\n"; - } - - function reTrieve($id, $val) { - echo __METHOD__ . "($id,$val)\n"; - return array($id=>$val); - } -} - -$select1 = $db->prepare('SELECT grp, id FROM test011'); -$select2 = $db->prepare('SELECT id, val FROM test011'); -$derived = $db->prepare('SELECT id, val FROM test011', array(PDO::ATTR_STATEMENT_CLASS=>array('DerivedStatement', array('Overloaded', $db)))); - -class Test1 -{ - public function __construct(public $id, public $val) - { - echo __METHOD__ . "($id,$val)\n"; - } - - static public function factory($id, $val) - { - echo __METHOD__ . "($id,$val)\n"; - return new self($id, $val); - } -} - -function callback($id,$val='N/A') -{ - echo __METHOD__ . "($id,$val)\n"; - return array($id=>$val); -} - -$f = new Test1(0,0); - -$select1->execute(); -var_dump($select1->fetchAll(PDO::FETCH_FUNC|PDO::FETCH_GROUP, 'callback')); - -$select2->execute(); -var_dump($select2->fetchAll(PDO::FETCH_FUNC, 'callback')); - -$select2->execute(); -var_dump($select2->fetchAll(PDO::FETCH_FUNC, array('Test1','factory'))); - -$select2->execute(); -var_dump($select2->fetchAll(PDO::FETCH_FUNC, array($f, 'factory'))); - -var_dump(get_class($derived)); -$derived->execute(); -var_dump($derived->fetchAll(PDO::FETCH_FUNC, array($derived, 'retrieve'))); -$derived->execute(); -var_dump($derived->fetchAll(PDO::FETCH_FUNC, array($derived, 'reTrieve'))); -$derived->execute(); -var_dump($derived->fetchAll(PDO::FETCH_FUNC, array($derived, 'RETRIEVE'))); - -?> ---CLEAN-- - ---EXPECTF-- -DerivedStatement::__construct(Overloaded) -Test1::__construct(0,0) -callback(1,N/A) -callback(2,N/A) -callback(3,N/A) -callback(4,N/A) -array(2) { - ["Group1"]=> - array(2) { - [0]=> - array(1) { - [1]=> - string(3) "N/A" - } - [1]=> - array(1) { - [2]=> - string(3) "N/A" - } - } - ["Group2"]=> - array(2) { - [0]=> - array(1) { - [3]=> - string(3) "N/A" - } - [1]=> - array(1) { - [4]=> - string(3) "N/A" - } - } -} -callback(1,A) -callback(2,B) -callback(3,C) -callback(4,D) -array(4) { - [0]=> - array(1) { - [1]=> - string(1) "A" - } - [1]=> - array(1) { - [2]=> - string(1) "B" - } - [2]=> - array(1) { - [3]=> - string(1) "C" - } - [3]=> - array(1) { - [4]=> - string(1) "D" - } -} -Test1::factory(1,A) -Test1::__construct(1,A) -Test1::factory(2,B) -Test1::__construct(2,B) -Test1::factory(3,C) -Test1::__construct(3,C) -Test1::factory(4,D) -Test1::__construct(4,D) -array(4) { - [0]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "1" - ["val"]=> - string(1) "A" - } - [1]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "2" - ["val"]=> - string(1) "B" - } - [2]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "3" - ["val"]=> - string(1) "C" - } - [3]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "4" - ["val"]=> - string(1) "D" - } -} -Test1::factory(1,A) -Test1::__construct(1,A) -Test1::factory(2,B) -Test1::__construct(2,B) -Test1::factory(3,C) -Test1::__construct(3,C) -Test1::factory(4,D) -Test1::__construct(4,D) -array(4) { - [0]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "1" - ["val"]=> - string(1) "A" - } - [1]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "2" - ["val"]=> - string(1) "B" - } - [2]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "3" - ["val"]=> - string(1) "C" - } - [3]=> - object(Test1)#%d (2) { - ["id"]=> - string(1) "4" - ["val"]=> - string(1) "D" - } -} -string(16) "DerivedStatement" -DerivedStatement::reTrieve(1,A) -DerivedStatement::reTrieve(2,B) -DerivedStatement::reTrieve(3,C) -DerivedStatement::reTrieve(4,D) -array(4) { - [0]=> - array(1) { - [1]=> - string(1) "A" - } - [1]=> - array(1) { - [2]=> - string(1) "B" - } - [2]=> - array(1) { - [3]=> - string(1) "C" - } - [3]=> - array(1) { - [4]=> - string(1) "D" - } -} -DerivedStatement::reTrieve(1,A) -DerivedStatement::reTrieve(2,B) -DerivedStatement::reTrieve(3,C) -DerivedStatement::reTrieve(4,D) -array(4) { - [0]=> - array(1) { - [1]=> - string(1) "A" - } - [1]=> - array(1) { - [2]=> - string(1) "B" - } - [2]=> - array(1) { - [3]=> - string(1) "C" - } - [3]=> - array(1) { - [4]=> - string(1) "D" - } -} -DerivedStatement::reTrieve(1,A) -DerivedStatement::reTrieve(2,B) -DerivedStatement::reTrieve(3,C) -DerivedStatement::reTrieve(4,D) -array(4) { - [0]=> - array(1) { - [1]=> - string(1) "A" - } - [1]=> - array(1) { - [2]=> - string(1) "B" - } - [2]=> - array(1) { - [3]=> - string(1) "C" - } - [3]=> - array(1) { - [4]=> - string(1) "D" - } -} diff --git a/ext/pdo/tests/pdo_fetch_func_001.phpt b/ext/pdo/tests/pdo_fetch_func_001.phpt new file mode 100644 index 000000000000..a5bcdc4699e7 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_func_001.phpt @@ -0,0 +1,108 @@ +--TEST-- +PDO Common: Testing PDO::FETCH_FUNC with various callables +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE test_fetch_func_001 (id INTEGER , name VARCHAR)'); +$db->exec('INSERT INTO test_fetch_func_001 VALUES (1, "php")'); +$db->exec('INSERT INTO test_fetch_func_001 VALUES (2, "sql")'); + +echo "Anonymous function:\n"; +$st = $db->query('SELECT * FROM test_fetch_func_001'); +$st->fetchAll( + PDO::FETCH_FUNC, + function($x, $y) { + echo $x, ' ', $y, PHP_EOL; + } +); + +echo "Internal function:\n"; +$st = $db->query('SELECT name FROM test_fetch_func_001'); +var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); + +echo "Relative class callable:\n"; +class foo { + public function method($x) { + return __METHOD__ . "($x)"; + } +} +class bar extends foo { + public function __construct($db) { + $st = $db->query('SELECT * FROM test_fetch_func_001'); + var_dump($st->fetchAll(PDO::FETCH_FUNC, [$this, 'parent::method'])); + } + + static public function factory($x, $y) { + return __METHOD__ . "($x, $y)"; + } +} + +$bar = new bar($db); + +echo '["bar", "factory"] callable:', "\n"; +$st = $db->query('SELECT * FROM test_fetch_func_001'); +var_dump($st->fetchAll(PDO::FETCH_FUNC, ['bar', 'factory'])); + +echo '[$bar, "factory"] callable:', "\n"; +$st = $db->query('SELECT * FROM test_fetch_func_001'); +var_dump($st->fetchAll(PDO::FETCH_FUNC, [$bar, 'factory'])); + +echo '"bar::factory" callable:', "\n"; +$st = $db->query('SELECT * FROM test_fetch_func_001'); +var_dump($st->fetchAll(PDO::FETCH_FUNC, 'bar::factory')); + +?> +--EXPECTF-- +Anonymous function: +1 php +2 sql +Internal function: +array(2) { + [0]=> + string(3) "PHP" + [1]=> + string(3) "SQL" +} +Relative class callable: + +Deprecated: Callables of the form ["bar", "parent::method"] are deprecated in %s on line %d +array(2) { + [0]=> + string(14) "foo::method(1)" + [1]=> + string(14) "foo::method(2)" +} +["bar", "factory"] callable: +array(2) { + [0]=> + string(20) "bar::factory(1, php)" + [1]=> + string(20) "bar::factory(2, sql)" +} +[$bar, "factory"] callable: +array(2) { + [0]=> + string(20) "bar::factory(1, php)" + [1]=> + string(20) "bar::factory(2, sql)" +} +"bar::factory" callable: +array(2) { + [0]=> + string(20) "bar::factory(1, php)" + [1]=> + string(20) "bar::factory(2, sql)" +} diff --git a/ext/pdo/tests/pdo_fetch_function_basic.phpt b/ext/pdo/tests/pdo_fetch_function_basic.phpt new file mode 100644 index 000000000000..522eca7005a0 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_basic.phpt @@ -0,0 +1,99 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with a simple callback +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_basic(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_basic VALUES (1, 'A', 'alpha')"); +$db->exec("INSERT INTO pdo_fetch_function_basic VALUES (2, 'B', 'beta')"); +$db->exec("INSERT INTO pdo_fetch_function_basic VALUES (3, 'C', 'gamma')"); +$db->exec("INSERT INTO pdo_fetch_function_basic VALUES (4, 'D', 'delta')"); + +echo "SELECT id, val1:\n"; + +function associateValWithId(int $id, string $val): array { + echo __FUNCTION__, '(', var_export($id, true), ', ', var_export($val, true) , ")\n"; + return [$id=>$val]; +} + +$selectIdVal = $db->prepare('SELECT id, val1 FROM pdo_fetch_function_basic'); +$selectIdVal->execute(); +$result = $selectIdVal->fetchAll(PDO::FETCH_FUNC, 'associateValWithId'); +var_dump($result); + +echo "SELECT *:\n"; + +function selectAllCallback(int $id, string $val1, string $val2): string { + echo __FUNCTION__, '(', var_export($id, true), + ', ', var_export($val1, true), + ', ', var_export($val2, true) , ")\n"; + return $val1 . $val2; +} + +$selectAll = $db->prepare('SELECT * FROM pdo_fetch_function_basic'); +$selectAll->execute(); +$result = $selectAll->fetchAll(PDO::FETCH_FUNC, 'selectAllCallback'); +var_dump($result); + +?> +--CLEAN-- + +--EXPECT-- +SELECT id, val1: +associateValWithId(1, 'A') +associateValWithId(2, 'B') +associateValWithId(3, 'C') +associateValWithId(4, 'D') +array(4) { + [0]=> + array(1) { + [1]=> + string(1) "A" + } + [1]=> + array(1) { + [2]=> + string(1) "B" + } + [2]=> + array(1) { + [3]=> + string(1) "C" + } + [3]=> + array(1) { + [4]=> + string(1) "D" + } +} +SELECT *: +selectAllCallback(1, 'A', 'alpha') +selectAllCallback(2, 'B', 'beta') +selectAllCallback(3, 'C', 'gamma') +selectAllCallback(4, 'D', 'delta') +array(4) { + [0]=> + string(6) "Aalpha" + [1]=> + string(5) "Bbeta" + [2]=> + string(6) "Cgamma" + [3]=> + string(6) "Ddelta" +} diff --git a/ext/pdo/tests/pdo_fetch_function_incorrect_call.phpt b/ext/pdo/tests/pdo_fetch_function_incorrect_call.phpt new file mode 100644 index 000000000000..63c342b598f7 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_incorrect_call.phpt @@ -0,0 +1,50 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with a call that is invalid +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_incorrect_call(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_call VALUES(1, 'A', 'alpha')"); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_call VALUES(2, 'B', 'beta')"); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_call VALUES(3, 'C', 'gamma')"); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_call VALUES(4, 'D', 'delta')"); + +$selectIdVal = $db->prepare('SELECT val1, val2 FROM pdo_fetch_function_incorrect_call'); + +function bogusCallback(stdClass $obj, string $str, array $arr) { + echo "Called\n"; + return "how?"; +} + +$selectIdVal->execute(); + +echo "Fetch all with bogus call:\n"; +try { + $result = $selectIdVal->fetchAll(PDO::FETCH_FUNC, 'bogusCallback'); + var_dump($result); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--CLEAN-- + +--EXPECT-- +Fetch all with bogus call: +TypeError: bogusCallback(): Argument #1 ($obj) must be of type stdClass, string given diff --git a/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt b/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt new file mode 100644 index 000000000000..870719949411 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt @@ -0,0 +1,89 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with invalid callables +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +prepare($dummy_query); +$stmt->execute(); + +class Bar { + static private function privateStatic($x, $y) { + return $x; + } + + public function instanceMethod($x, $y) { + return $x .'==='. $y; + } +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, 'nothing')); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, '')); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, NULL)); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, 1)); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, ['self', 'foo'])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, ['bar', 'instanceMethod'])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, ['bar', 'privateStatic'])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +try { + var_dump($stmt->fetchAll(PDO::FETCH_FUNC, ['bar', 'nonexistent'])); +} catch (\Throwable $e) { + echo $e::class, ': ', $e->getMessage(), \PHP_EOL; +} + +?> +--EXPECT-- +TypeError: function "nothing" not found or invalid function name +TypeError: function "" not found or invalid function name +TypeError: PDOStatement::fetchAll(): Argument #2 must be a callable, null given +TypeError: no array or string given +TypeError: cannot access "self" when no class scope is active +TypeError: non-static method Bar::instanceMethod() cannot be called statically +TypeError: cannot access private method Bar::privateStatic() +TypeError: class Bar does not have a method "nonexistent" diff --git a/ext/pdo/tests/pdo_fetch_function_overload_statement_class.phpt b/ext/pdo/tests/pdo_fetch_function_overload_statement_class.phpt new file mode 100644 index 000000000000..d9c1a01eeeaa --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_overload_statement_class.phpt @@ -0,0 +1,65 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC and statement overloading +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_overload_statement_class(id int NOT NULL PRIMARY KEY, val VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_overload_statement_class VALUES (1, 'A')"); +$db->exec("INSERT INTO pdo_fetch_function_overload_statement_class VALUES (2, 'B')"); +$db->exec("INSERT INTO pdo_fetch_function_overload_statement_class VALUES (3, 'C')"); +$db->exec("INSERT INTO pdo_fetch_function_overload_statement_class VALUES (4, 'D')"); + +class DerivedStatement extends PDOStatement +{ + private function __construct(public $name, $db) + { + echo __METHOD__ . "($name)\n"; + } + + function reTrieve($id, $val) { + echo __METHOD__ . "($id,$val)\n"; + return "$id => $val"; + } +} + +$derived = $db->prepare('SELECT id, val FROM pdo_fetch_function_overload_statement_class', [PDO::ATTR_STATEMENT_CLASS => ['DerivedStatement', ['Overloaded', $db]]]); +var_dump(get_class($derived)); +$derived->execute(); +var_dump($derived->fetchAll(PDO::FETCH_FUNC, [$derived, 'retrieve'])); + +?> +--CLEAN-- + +--EXPECT-- +DerivedStatement::__construct(Overloaded) +string(16) "DerivedStatement" +DerivedStatement::reTrieve(1,A) +DerivedStatement::reTrieve(2,B) +DerivedStatement::reTrieve(3,C) +DerivedStatement::reTrieve(4,D) +array(4) { + [0]=> + string(6) "1 => A" + [1]=> + string(6) "2 => B" + [2]=> + string(6) "3 => C" + [3]=> + string(6) "4 => D" +} diff --git a/ext/pdo/tests/pdo_fetch_function_with_grouping.phpt b/ext/pdo/tests/pdo_fetch_function_with_grouping.phpt new file mode 100644 index 000000000000..e3d20347c813 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_with_grouping.phpt @@ -0,0 +1,63 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with a simple callback and grouping (PDO::FETCH_FUNC|PDO::FETCH_GROUP) +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_with_grouping(id int NOT NULL PRIMARY KEY, val VARCHAR(10), grp VARCHAR(10))'); + +$db->exec("INSERT INTO pdo_fetch_function_with_grouping VALUES (1, 'A', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_function_with_grouping VALUES (2, 'B', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_function_with_grouping VALUES (3, 'C', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_function_with_grouping VALUES (4, 'D', 'Group2')"); + +$selectGroupId = $db->prepare('SELECT grp, id, val FROM pdo_fetch_function_with_grouping'); + +function associateValWithId(int $id, string $value) { + echo __FUNCTION__, '(', var_export($id, true), ', ', var_export($value, true) , ")\n"; + return "$id => $value"; +} + +$selectGroupId->execute(); +$result = $selectGroupId->fetchAll(PDO::FETCH_FUNC|PDO::FETCH_GROUP, 'associateValWithId'); +var_dump($result); + +?> +--CLEAN-- + +--EXPECT-- +associateValWithId(1, 'A') +associateValWithId(2, 'B') +associateValWithId(3, 'C') +associateValWithId(4, 'D') +array(2) { + ["Group1"]=> + array(2) { + [0]=> + string(6) "1 => A" + [1]=> + string(6) "2 => B" + } + ["Group2"]=> + array(2) { + [0]=> + string(6) "3 => C" + [1]=> + string(6) "4 => D" + } +} diff --git a/ext/pdo/tests/pdo_fetch_function_with_unique.phpt b/ext/pdo/tests/pdo_fetch_function_with_unique.phpt new file mode 100644 index 000000000000..5dc412242030 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_with_unique.phpt @@ -0,0 +1,55 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with a simple callback and uniqueness (PDO::FETCH_FUNC|PDO::FETCH_UNIQUE) +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_with_uniqueness(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness VALUES (1, 'A', 'alpha')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness VALUES (2, 'B', 'beta')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness VALUES (3, 'C', 'gamma')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness VALUES (4, 'D', 'delta')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT val1, val2 FROM pdo_fetch_function_with_uniqueness'); +$pdoQuery->execute(); +$result = $pdoQuery->fetchAll(PDO::FETCH_FUNC|PDO::FETCH_UNIQUE, 'to_upper_with_log'); +var_dump($result); + +?> +--CLEAN-- + +--EXPECT-- +to_upper_with_log('alpha') +to_upper_with_log('beta') +to_upper_with_log('gamma') +to_upper_with_log('delta') +array(4) { + ["A"]=> + string(5) "ALPHA" + ["B"]=> + string(4) "BETA" + ["C"]=> + string(5) "GAMMA" + ["D"]=> + string(5) "DELTA" +} diff --git a/ext/pdo/tests/pdo_fetch_function_with_unique_and_grouping.phpt b/ext/pdo/tests/pdo_fetch_function_with_unique_and_grouping.phpt new file mode 100644 index 000000000000..5a9b15ed1713 --- /dev/null +++ b/ext/pdo/tests/pdo_fetch_function_with_unique_and_grouping.phpt @@ -0,0 +1,52 @@ +--TEST-- +PDO Common: PDO::FETCH_FUNC with a simple callback and uniqueness+grouping (PDO::FETCH_FUNC|PDO::FETCH_UNIQUE|PDO::FETCH_GROUP) +--EXTENSIONS-- +pdo +--SKIPIF-- + +--FILE-- +exec('CREATE TABLE pdo_fetch_function_with_uniqueness_and_grouping(id int NOT NULL PRIMARY KEY, val1 VARCHAR(10), val2 VARCHAR(10), grp VARCHAR(10))'); +// Firebird does not allow inserting multiple rows with INSERT INTO +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness_and_grouping VALUES (1, 'A', 'alpha', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness_and_grouping VALUES (2, 'B', 'beta', 'Group1')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness_and_grouping VALUES (3, 'C', 'gamma', 'Group2')"); +$db->exec("INSERT INTO pdo_fetch_function_with_uniqueness_and_grouping VALUES (4, 'D', 'delta', 'Group2')"); + +function to_upper_with_log(string $str): string { + echo __FUNCTION__, '(', var_export($str, true), ')', PHP_EOL; + return strtoupper($str); +} + +$pdoQuery = $db->prepare('SELECT grp, val2, val1 FROM pdo_fetch_function_with_uniqueness_and_grouping'); +$pdoQuery->execute(); +$result = $pdoQuery->fetchAll(PDO::FETCH_FUNC|PDO::FETCH_UNIQUE|PDO::FETCH_GROUP, 'to_upper_with_log'); +var_dump($result); + +?> +--CLEAN-- + +--EXPECT-- +to_upper_with_log('alpha') +to_upper_with_log('beta') +to_upper_with_log('gamma') +to_upper_with_log('delta') +array(2) { + ["Group1"]=> + string(4) "BETA" + ["Group2"]=> + string(5) "DELTA" +} diff --git a/ext/pdo/tests/pdo_test.inc b/ext/pdo/tests/pdo_test.inc index 0c5d5412a273..a1c35ee3236b 100644 --- a/ext/pdo/tests/pdo_test.inc +++ b/ext/pdo/tests/pdo_test.inc @@ -96,4 +96,21 @@ class PDOTest { }; } } -?> + +/** See https://stackoverflow.com/a/3732466 */ +function get_dummy_sql_request(): string +{ + $dsn = getenv('PDOTEST_DSN'); + + // Firebird: https://www.firebirdfaq.org/faq30/ + if (str_starts_with($dsn, 'firebird')) { + return 'SELECT current_timestamp FROM RDB$DATABASE'; + } + + // Oracle + if (str_starts_with($dsn, 'oci')) { + return 'SELECT 1 FROM DUAL'; + } + + return 'SELECT 1'; +} diff --git a/ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt b/ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt deleted file mode 100644 index e9c2c7e5d5e4..000000000000 --- a/ext/pdo_sqlite/tests/pdo_fetch_func_001.phpt +++ /dev/null @@ -1,151 +0,0 @@ ---TEST-- -Testing several callbacks using PDO::FETCH_FUNC ---EXTENSIONS-- -pdo_sqlite ---FILE-- -setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); - -$db->exec('CREATE TABLE test_fetch_func_001 (id INTEGER , name VARCHAR)'); -$db->exec('INSERT INTO test_fetch_func_001 VALUES(1, "php"), (2, "")'); - -$st = $db->query('SELECT * FROM test_fetch_func_001'); -$st->fetchAll( - PDO::FETCH_FUNC, - function($x, $y) use ($st) { - var_dump($st, $x, $y); - } -); - -$st = $db->query('SELECT name FROM test_fetch_func_001'); -var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, 'nothing')); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, '')); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, NULL)); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, 1)); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, array('self', 'foo'))); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -class foo { - public function method($x) { - return "--- $x ---"; - } -} -class bar extends foo { - public function __construct($db) { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, array($this, 'parent::method'))); - } - - static public function test1($x, $y) { - return $x .'---'. $y; - } - - private function test2($x, $y) { - return $x; - } - - public function test3($x, $y) { - return $x .'==='. $y; - } -} - -new bar($db); - -$st = $db->query('SELECT * FROM test_fetch_func_001'); -var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test1'))); - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test2'))); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'test3'))); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -try { - $st = $db->query('SELECT * FROM test_fetch_func_001'); - var_dump($st->fetchAll(PDO::FETCH_FUNC, array('bar', 'inexistent'))); -} catch (\TypeError $e) { - echo $e->getMessage(), \PHP_EOL; -} - -?> ---EXPECTF-- -object(PDOStatement)#%d (1) { - ["queryString"]=> - string(33) "SELECT * FROM test_fetch_func_001" -} -int(1) -string(3) "php" -object(PDOStatement)#%d (1) { - ["queryString"]=> - string(33) "SELECT * FROM test_fetch_func_001" -} -int(2) -string(0) "" -array(2) { - [0]=> - string(3) "PHP" - [1]=> - string(0) "" -} -function "nothing" not found or invalid function name -function "" not found or invalid function name -PDOStatement::fetchAll(): Argument #2 must be a callable, null given -no array or string given -cannot access "self" when no class scope is active - -Deprecated: Callables of the form ["bar", "parent::method"] are deprecated in %s on line %d -array(2) { - [0]=> - string(9) "--- 1 ---" - [1]=> - string(9) "--- 2 ---" -} -array(2) { - [0]=> - string(7) "1---php" - [1]=> - string(4) "2---" -} -non-static method bar::test2() cannot be called statically -non-static method bar::test3() cannot be called statically -class bar does not have a method "inexistent" From 1bb1071d956776ae52d419e8218182502b838cad Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Thu, 2 Jan 2025 22:50:22 +0000 Subject: [PATCH 2/2] fix?2 --- ext/pdo/tests/pdo_fetch_func_001.phpt | 24 ++++++++++++------- ...do_fetch_function_incorrect_callables.phpt | 13 +++++++--- ext/pdo/tests/pdo_test.inc | 18 +------------- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/ext/pdo/tests/pdo_fetch_func_001.phpt b/ext/pdo/tests/pdo_fetch_func_001.phpt index a5bcdc4699e7..93af976d38c6 100644 --- a/ext/pdo/tests/pdo_fetch_func_001.phpt +++ b/ext/pdo/tests/pdo_fetch_func_001.phpt @@ -16,12 +16,12 @@ if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); -$db->exec('CREATE TABLE test_fetch_func_001 (id INTEGER , name VARCHAR)'); -$db->exec('INSERT INTO test_fetch_func_001 VALUES (1, "php")'); -$db->exec('INSERT INTO test_fetch_func_001 VALUES (2, "sql")'); +$db->exec('CREATE TABLE pdo_fetch_function_001(id int NOT NULL PRIMARY KEY, pl_name VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_001 VALUES (1, 'php')"); +$db->exec("INSERT INTO pdo_fetch_function_001 VALUES (2, 'sql')"); echo "Anonymous function:\n"; -$st = $db->query('SELECT * FROM test_fetch_func_001'); +$st = $db->query('SELECT * FROM pdo_fetch_function_001'); $st->fetchAll( PDO::FETCH_FUNC, function($x, $y) { @@ -30,7 +30,7 @@ $st->fetchAll( ); echo "Internal function:\n"; -$st = $db->query('SELECT name FROM test_fetch_func_001'); +$st = $db->query('SELECT pl_name FROM pdo_fetch_function_001'); var_dump($st->fetchAll(PDO::FETCH_FUNC, 'strtoupper')); echo "Relative class callable:\n"; @@ -41,7 +41,7 @@ class foo { } class bar extends foo { public function __construct($db) { - $st = $db->query('SELECT * FROM test_fetch_func_001'); + $st = $db->query('SELECT * FROM pdo_fetch_function_001'); var_dump($st->fetchAll(PDO::FETCH_FUNC, [$this, 'parent::method'])); } @@ -53,17 +53,23 @@ class bar extends foo { $bar = new bar($db); echo '["bar", "factory"] callable:', "\n"; -$st = $db->query('SELECT * FROM test_fetch_func_001'); +$st = $db->query('SELECT * FROM pdo_fetch_function_001'); var_dump($st->fetchAll(PDO::FETCH_FUNC, ['bar', 'factory'])); echo '[$bar, "factory"] callable:', "\n"; -$st = $db->query('SELECT * FROM test_fetch_func_001'); +$st = $db->query('SELECT * FROM pdo_fetch_function_001'); var_dump($st->fetchAll(PDO::FETCH_FUNC, [$bar, 'factory'])); echo '"bar::factory" callable:', "\n"; -$st = $db->query('SELECT * FROM test_fetch_func_001'); +$st = $db->query('SELECT * FROM pdo_fetch_function_001'); var_dump($st->fetchAll(PDO::FETCH_FUNC, 'bar::factory')); +?> +--CLEAN-- + --EXPECTF-- Anonymous function: diff --git a/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt b/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt index 870719949411..f06ae3c6a6b6 100644 --- a/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt +++ b/ext/pdo/tests/pdo_fetch_function_incorrect_callables.phpt @@ -15,9 +15,10 @@ if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../ require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc'; $db = PDOTest::factory(); -$dummy_query = get_dummy_sql_request(); -$stmt = $db->prepare($dummy_query); -$stmt->execute(); +$db->exec('CREATE TABLE pdo_fetch_function_incorrect_callable(id int NOT NULL PRIMARY KEY, pl_name VARCHAR(10))'); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_callable VALUES (1, 'php')"); +$db->exec("INSERT INTO pdo_fetch_function_incorrect_callable VALUES (2, 'sql')"); +$stmt = $db->query('SELECT * FROM pdo_fetch_function_incorrect_callable'); class Bar { static private function privateStatic($x, $y) { @@ -77,6 +78,12 @@ try { echo $e::class, ': ', $e->getMessage(), \PHP_EOL; } +?> +--CLEAN-- + --EXPECT-- TypeError: function "nothing" not found or invalid function name diff --git a/ext/pdo/tests/pdo_test.inc b/ext/pdo/tests/pdo_test.inc index a1c35ee3236b..8cd60f68aa51 100644 --- a/ext/pdo/tests/pdo_test.inc +++ b/ext/pdo/tests/pdo_test.inc @@ -97,20 +97,4 @@ class PDOTest { } } -/** See https://stackoverflow.com/a/3732466 */ -function get_dummy_sql_request(): string -{ - $dsn = getenv('PDOTEST_DSN'); - - // Firebird: https://www.firebirdfaq.org/faq30/ - if (str_starts_with($dsn, 'firebird')) { - return 'SELECT current_timestamp FROM RDB$DATABASE'; - } - - // Oracle - if (str_starts_with($dsn, 'oci')) { - return 'SELECT 1 FROM DUAL'; - } - - return 'SELECT 1'; -} +?>