1--TEST--
2PDO::ATTR_STATEMENT_CLASS
3--EXTENSIONS--
4pdo_mysql
5--SKIPIF--
6<?php
7require_once __DIR__ . '/inc/mysql_pdo_test.inc';
8MySQLPDOTest::skip();
9?>
10--FILE--
11<?php
12    require_once __DIR__ . '/inc/mysql_pdo_test.inc';
13    $db = MySQLPDOTest::factory();
14    $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
15
16    $table = 'pdo_mysql_attr_statement_class';
17
18    MySQLPDOTest::createTestTable($table, $db);
19
20    $default =  $db->getAttribute(PDO::ATTR_STATEMENT_CLASS);
21    var_dump($default);
22
23    try {
24        $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, 'foo');
25    } catch (\TypeError $e) {
26        echo $e->getMessage(), \PHP_EOL;
27    }
28    try {
29        $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['classname']);
30    } catch (\TypeError $e) {
31        echo $e->getMessage(), \PHP_EOL;
32    }
33    // unknown class
34    try {
35        $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['classname', []]);
36    } catch (\TypeError $e) {
37        echo $e->getMessage(), \PHP_EOL;
38    }
39
40    // class not derived from PDOStatement
41    class myclass {
42        function __construct() {
43            printf("myclass\n");
44        }
45    }
46
47    try {
48        $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['myclass', []]);
49    } catch (\TypeError $e) {
50        echo $e->getMessage(), \PHP_EOL;
51    }
52
53    // public constructor not allowed
54    class mystatement extends PDOStatement {
55        public function __construct() {
56            printf("mystatement\n");
57        }
58    }
59
60    try {
61        if (false !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['mystatement', []])))
62            printf("[006] Expecting boolean/false got %s\n", var_export($tmp, true));
63    } catch (\Error $e) {
64        echo get_class($e), ': ', $e->getMessage(), \PHP_EOL;
65    }
66
67
68    // ... but a public destructor is allowed
69    class mystatement2 extends PDOStatement {
70        public function __destruct() {
71            printf("mystatement\n");
72        }
73    }
74
75    if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement2', array()))))
76        printf("[007] Expecting boolean/true got %s\n", var_export($tmp, true));
77
78    // private constructor
79    class mystatement3 extends PDOStatement {
80        private function __construct($msg) {
81            printf("mystatement3\n");
82            var_dump($msg);
83        }
84    }
85    if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement3', array('param1')))))
86        printf("[008] Expecting boolean/true got %s\n", var_export($tmp, true));
87
88    // private constructor
89    class mystatement4 extends PDOStatement {
90        private function __construct($msg) {
91            printf("%s\n", get_class($this));
92            var_dump($msg);
93        }
94    }
95    if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement4', array('param1')))))
96        printf("[008] Expecting boolean/true got %s\n", var_export($tmp, true));
97
98    var_dump($db->getAttribute(PDO::ATTR_STATEMENT_CLASS));
99    $stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC LIMIT 2");
100
101    class mystatement5 extends mystatement4 {
102        public function fetchAll($fetch_style = 1, ...$fetch_args): array {
103            return [];
104        }
105    }
106
107    if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('mystatement5', array('mystatement5')))))
108        printf("[009] Expecting boolean/true got %s\n", var_export($tmp, true));
109    $stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC LIMIT 2");
110    var_dump($stmt->fetchAll());
111
112    if (true !== ($tmp = $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('PDOStatement'))))
113        printf("[010] Expecting boolean/true got %s\n", var_export($tmp, true));
114
115    $stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC LIMIT 1");
116    var_dump($stmt->fetchAll());
117
118    // Yes, this is a fatal error and I want it to fail.
119    abstract class mystatement6 extends mystatement5 {
120    }
121    try {
122        $db->setAttribute(PDO::ATTR_STATEMENT_CLASS, ['mystatement6', ['mystatement6']]);
123        $stmt = $db->query("SELECT id, label FROM {$table} ORDER BY id ASC LIMIT 1");
124    } catch (\Error $e) {
125        echo get_class($e), ': ', $e->getMessage(), \PHP_EOL;
126    }
127?>
128--CLEAN--
129<?php
130require_once __DIR__ . '/inc/mysql_pdo_test.inc';
131$db = MySQLPDOTest::factory();
132$db->query('DROP TABLE IF EXISTS pdo_mysql_attr_statement_class');
133?>
134--EXPECT--
135array(1) {
136  [0]=>
137  string(12) "PDOStatement"
138}
139PDO::ATTR_STATEMENT_CLASS value must be of type array, string given
140PDO::ATTR_STATEMENT_CLASS class must be a valid class
141PDO::ATTR_STATEMENT_CLASS class must be a valid class
142PDO::ATTR_STATEMENT_CLASS class must be derived from PDOStatement
143TypeError: User-supplied statement class cannot have a public constructor
144array(2) {
145  [0]=>
146  string(12) "mystatement4"
147  [1]=>
148  array(1) {
149    [0]=>
150    string(6) "param1"
151  }
152}
153mystatement4
154string(6) "param1"
155mystatement5
156string(12) "mystatement5"
157array(0) {
158}
159array(1) {
160  [0]=>
161  array(4) {
162    ["id"]=>
163    string(1) "1"
164    [0]=>
165    string(1) "1"
166    ["label"]=>
167    string(1) "a"
168    [1]=>
169    string(1) "a"
170  }
171}
172Error: Cannot instantiate abstract class mystatement6
173