Skip to content

Commit 0baf03d

Browse files
committed
ext/pdo: Add tests for PDO::ATTR_STATEMENT_CLASS attribute
1 parent 2acda55 commit 0baf03d

10 files changed

+584
-1
lines changed
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
--TEST--
2+
PDO Common: Set PDOStatement class with PDO::ATTR_STATEMENT_CLASS overall test
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
$db = PDOTest::factory();
18+
$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
19+
20+
$table = 'pdo_attr_statement_class_basic';
21+
$db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))");
22+
$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')");
23+
$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')");
24+
25+
$default = $db->getAttribute(PDO::ATTR_STATEMENT_CLASS);
26+
var_dump($default);
27+
28+
// Having a public destructor is allowed
29+
class StatementWithPublicDestructor extends PDOStatement {
30+
public function __destruct() {
31+
echo __METHOD__, PHP_EOL;
32+
}
33+
}
34+
35+
try {
36+
var_dump($db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['StatementWithPublicDestructor', []]));
37+
} catch (\Throwable $e) {
38+
echo $e::class, ': ', $e->getMessage(), \PHP_EOL;
39+
}
40+
var_dump($db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['StatementWithPublicDestructor']));
41+
$stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC");
42+
unset($stmt);
43+
44+
echo "Class derived from PDOStatement, with private constructor:\n";
45+
class StatementWithPrivateConstructor extends PDOStatement {
46+
private function __construct($msg) {
47+
echo __METHOD__, PHP_EOL;
48+
var_dump($this);
49+
var_dump($msg);
50+
}
51+
}
52+
var_dump($db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['StatementWithPrivateConstructor', ['param1']]));
53+
$stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC");
54+
unset($stmt);
55+
56+
echo "Class derived from a child of PDOStatement:\n";
57+
class StatementDerivedFromChild extends StatementWithPrivateConstructor {
58+
public function fetchAll($fetch_style = 1, ...$fetch_args): array {
59+
return [];
60+
}
61+
}
62+
63+
var_dump($db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['StatementDerivedFromChild', ['param1']]));
64+
$stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC");
65+
var_dump($stmt->fetchAll());
66+
unset($stmt);
67+
68+
echo "Reset to default PDOStatement class:\n";
69+
var_dump($db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['PDOStatement']));
70+
$stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC");
71+
var_dump($stmt->fetchAll());
72+
unset($stmt);
73+
74+
?>
75+
--CLEAN--
76+
<?php
77+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
78+
$db = PDOTest::factory();
79+
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_basic");
80+
?>
81+
--EXPECT--
82+
array(1) {
83+
[0]=>
84+
string(12) "PDOStatement"
85+
}
86+
bool(true)
87+
bool(true)
88+
StatementWithPublicDestructor::__destruct
89+
Class derived from PDOStatement, with private constructor:
90+
bool(true)
91+
StatementWithPrivateConstructor::__construct
92+
object(StatementWithPrivateConstructor)#2 (1) {
93+
["queryString"]=>
94+
string(68) "SELECT id, label FROM pdo_attr_statement_class_basic ORDER BY id ASC"
95+
}
96+
string(6) "param1"
97+
Class derived from a child of PDOStatement:
98+
bool(true)
99+
StatementWithPrivateConstructor::__construct
100+
object(StatementDerivedFromChild)#2 (1) {
101+
["queryString"]=>
102+
string(68) "SELECT id, label FROM pdo_attr_statement_class_basic ORDER BY id ASC"
103+
}
104+
string(6) "param1"
105+
array(0) {
106+
}
107+
Reset to default PDOStatement class:
108+
bool(true)
109+
array(2) {
110+
[0]=>
111+
array(4) {
112+
["id"]=>
113+
string(1) "1"
114+
[0]=>
115+
string(1) "1"
116+
["label"]=>
117+
string(1) "a"
118+
[1]=>
119+
string(1) "a"
120+
}
121+
[1]=>
122+
array(4) {
123+
["id"]=>
124+
string(1) "2"
125+
[0]=>
126+
string(1) "2"
127+
["label"]=>
128+
string(1) "b"
129+
[1]=>
130+
string(1) "b"
131+
}
132+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
--TEST--
2+
PDO Common: Set PDOStatement class with ctor_args that are freed with GC intervention
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
class Foo extends PDOStatement {
18+
private function __construct($v) {
19+
var_dump($v);
20+
}
21+
}
22+
23+
class Bar extends PDO {
24+
public $statementClass = 'Foo';
25+
function __construct($dsn, $username, $password, $driver_options = []) {
26+
$driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
27+
parent::__construct($dsn, $username, $password, $driver_options);
28+
29+
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, [$this->statementClass, [$this]]);
30+
}
31+
}
32+
33+
$db = PDOTest::factory(Bar::class);
34+
35+
$table = 'pdo_attr_statement_class_ctor_arg_gc';
36+
$db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))");
37+
$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')");
38+
$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')");
39+
$query = "SELECT id, label FROM {$table} ORDER BY id ASC";
40+
$stmt = $db->query($query);
41+
42+
var_dump($stmt instanceof Foo);
43+
var_dump($stmt->queryString === $query);
44+
45+
?>
46+
--CLEAN--
47+
<?php
48+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
49+
$db = PDOTest::factory();
50+
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_ctor_arg_gc");
51+
?>
52+
--EXPECT--
53+
object(Bar)#1 (1) {
54+
["statementClass"]=>
55+
string(3) "Foo"
56+
}
57+
bool(true)
58+
bool(true)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
PDO Common: Set PDOStatement class with ctor_args that are freed without GC intervention as a variable that is modified
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
class Foo extends PDOStatement {
18+
private function __construct($v) {
19+
var_dump($v);
20+
}
21+
}
22+
23+
$db = PDOTest::factory();
24+
25+
$a = ['Foo', ['param1']];
26+
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, $a);
27+
$a[0] = 'Bar';
28+
29+
$table = 'pdo_attr_statement_class_ctor_arg_no_gc_var_modified';
30+
$db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))");
31+
$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')");
32+
$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')");
33+
$query = "SELECT id, label FROM {$table} ORDER BY id ASC";
34+
$stmt = $db->query($query);
35+
36+
var_dump($stmt instanceof Foo);
37+
var_dump($stmt->queryString === $query);
38+
39+
?>
40+
--CLEAN--
41+
<?php
42+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
43+
$db = PDOTest::factory();
44+
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_ctor_arg_no_gc_var_modified");
45+
?>
46+
--EXPECT--
47+
string(6) "param1"
48+
bool(true)
49+
bool(true)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
--TEST--
2+
PDO Common: Set PDOStatement class with ctor_args that are freed without GC intervention as a variable that is unset
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
class Foo extends PDOStatement {
18+
private function __construct($v) {
19+
var_dump($v);
20+
}
21+
}
22+
23+
$db = PDOTest::factory();
24+
25+
$a = ['Foo', ['param1']];
26+
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, $a);
27+
unset($a);
28+
29+
$table = 'pdo_attr_statement_class_ctor_arg_no_gc_var_unset';
30+
$db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))");
31+
$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')");
32+
$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')");
33+
$query = "SELECT id, label FROM {$table} ORDER BY id ASC";
34+
$stmt = $db->query($query);
35+
36+
var_dump($stmt instanceof Foo);
37+
var_dump($stmt->queryString === $query);
38+
39+
?>
40+
--CLEAN--
41+
<?php
42+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
43+
$db = PDOTest::factory();
44+
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_ctor_arg_no_gc_var_unset");
45+
?>
46+
--EXPECT--
47+
string(6) "param1"
48+
bool(true)
49+
bool(true)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
PDO Common: Set PDOStatement class with ctor_args that are freed without GC intervention
3+
--EXTENSIONS--
4+
pdo
5+
--SKIPIF--
6+
<?php
7+
$dir = getenv('REDIR_TEST_DIR');
8+
if (false == $dir) die('skip no driver');
9+
require_once $dir . 'pdo_test.inc';
10+
PDOTest::skip();
11+
?>
12+
--FILE--
13+
<?php
14+
if (getenv('REDIR_TEST_DIR') === false) putenv('REDIR_TEST_DIR='.__DIR__ . '/../../pdo/tests/');
15+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
16+
17+
class Foo extends PDOStatement {
18+
private function __construct($v) {
19+
var_dump($v);
20+
}
21+
}
22+
23+
$db = PDOTest::factory();
24+
25+
$db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('Foo', ['param1']));
26+
27+
$table = 'pdo_attr_statement_class_ctor_arg_no_gc';
28+
$db->exec("CREATE TABLE {$table} (id INT, label CHAR(1), PRIMARY KEY(id))");
29+
$db->exec("INSERT INTO {$table} (id, label) VALUES (1, 'a')");
30+
$db->exec("INSERT INTO {$table} (id, label) VALUES (2, 'b')");
31+
$query = "SELECT id, label FROM {$table} ORDER BY id ASC";
32+
$stmt = $db->query($query);
33+
34+
var_dump($stmt instanceof Foo);
35+
var_dump($stmt->queryString === $query);
36+
37+
?>
38+
--CLEAN--
39+
<?php
40+
require_once getenv('REDIR_TEST_DIR') . 'pdo_test.inc';
41+
$db = PDOTest::factory();
42+
PDOTest::dropTableIfExists($db, "pdo_attr_statement_class_ctor_arg_no_gc");
43+
?>
44+
--EXPECT--
45+
string(6) "param1"
46+
bool(true)
47+
bool(true)

0 commit comments

Comments
 (0)