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