1--TEST--
2mysqli_stmt_bind_result()
3--SKIPIF--
4<?php
5require_once('skipif.inc');
6require_once('skipifconnectfailure.inc');
7?>
8--FILE--
9<?php
10    require_once("connect.inc");
11    require('table.inc');
12
13    $stmt = mysqli_stmt_init($link);
14    if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test ORDER BY id LIMIT 1"))
15        printf("[002a] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
16
17    mysqli_stmt_close($stmt);
18    $stmt = mysqli_stmt_init($link);
19
20    $id = null;
21    $label = null;
22    $foo = null;
23
24    try {
25        mysqli_stmt_bind_result($stmt, $id);
26    } catch (Error $exception) {
27        echo $exception->getMessage() . "\n";
28    }
29
30    if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test ORDER BY id LIMIT 1"))
31        printf("[004] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
32
33    try {
34        mysqli_stmt_bind_result($stmt, $id);
35    } catch (\ArgumentCountError $e) {
36        echo $e->getMessage() . PHP_EOL;
37    }
38
39    if (true !== ($tmp = mysqli_stmt_bind_result($stmt, $id, $label)))
40        printf("[006] Expecting boolean/true, got %s/%s\n", gettype($tmp), $tmp);
41
42    try {
43        mysqli_stmt_bind_result($stmt, $id, $label, $foo);
44    } catch (\ArgumentCountError $e) {
45        echo $e->getMessage() . PHP_EOL;
46    }
47
48    if (!mysqli_stmt_execute($stmt))
49        printf("[008] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
50
51    while (mysqli_stmt_fetch($stmt)) {
52        var_dump($id);
53        var_dump($label);
54    }
55    mysqli_stmt_close($stmt);
56
57
58    function func_mysqli_stmt_bind_result($link, $engine, $bind_type, $sql_type, $bind_value, $offset, $type_hint = null) {
59
60        if (!mysqli_query($link, "DROP TABLE IF EXISTS test")) {
61            printf("[%04d] [%d] %s\n", $offset, mysqli_errno($link), mysqli_error($link));
62            return false;
63        }
64
65        if (!mysqli_query($link, sprintf("CREATE TABLE test(id INT, label %s, PRIMARY KEY(id)) ENGINE = %s", $sql_type, $engine))) {
66            // don't bail - column type might not be supported by the server, ignore this
67            return false;
68        }
69
70        if (!$stmt = mysqli_stmt_init($link)) {
71            printf("[%04d] [%d] %s\n", $offset + 1, mysqli_errno($link), mysqli_error($link));
72            return false;
73        }
74
75        if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (?, ?)")) {
76            printf("[%04d] [%d] %s\n", $offset + 2, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
77            return false;
78        }
79
80        $id = null;
81        if (!mysqli_stmt_bind_param($stmt, "i" . $bind_type, $id, $bind_value)) {
82            printf("[%04d] [%d] %s\n", $offset + 3, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
83            mysqli_stmt_close($stmt);
84            return false;
85        }
86
87        for ($id = 1; $id < 4; $id++) {
88            if (!mysqli_stmt_execute($stmt)) {
89                printf("[%04d] [%d] %s\n", $offset + 3 + $id, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
90                mysqli_stmt_close($stmt);
91                return false;
92            }
93        }
94        mysqli_stmt_close($stmt);
95
96        $stmt = mysqli_stmt_init($link);
97
98        if (!mysqli_stmt_prepare($stmt, "SELECT id, label FROM test")) {
99            printf("[%04d] [%d] %s\n", $offset + 7, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
100            mysqli_stmt_close($stmt);
101            return false;
102        }
103
104        if (!mysqli_stmt_execute($stmt)) {
105            printf("[%04d] [%d] %s\n", $offset + 8, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
106            mysqli_stmt_close($stmt);
107            return false;
108        }
109
110        $result = mysqli_stmt_result_metadata($stmt);
111
112        $bind_res = null;
113        if (!mysqli_stmt_bind_result($stmt, $id, $bind_res)) {
114            printf("[%04d] [%d] %s\n", $offset + 9, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
115            mysqli_stmt_close($stmt);
116            return false;
117        }
118        $num = 0;
119        $fields = mysqli_fetch_fields($result);
120
121        while (mysqli_stmt_fetch($stmt)) {
122            if (!gettype($bind_res)=="unicode") {
123                if ($bind_res !== $bind_value && (!$type_hint || ($type_hint !== gettype($bind_res)))) {
124                    printf("[%04d] [%d] Expecting %s/'%s' [type hint = %s], got %s/'%s'\n",
125                        $offset + 10, $num,
126                        gettype($bind_value), $bind_value, $type_hint,
127                        gettype($bind_res), $bind_res);
128                        mysqli_stmt_close($stmt);
129                        return false;
130                }
131            }
132            $num++;
133        }
134
135        if ($num != 3) {
136            printf("[%04d] [%d] %s, expecting 3 results, got only %d results\n",
137                $offset + 11, mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt), $num);
138            mysqli_stmt_close($stmt);
139            return false;
140        }
141
142        mysqli_stmt_close($stmt);
143        return true;
144    }
145
146
147    function func_mysqli_stmt_bind_make_string($len) {
148
149        $ret = '';
150        for ($i = 0; $i < $len; $i++)
151            $ret .= chr(mt_rand(65, 90));
152
153        return $ret;
154    }
155
156    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT", -11, 20);
157    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT", NULL, 40);
158    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT UNSIGNED", 1, 60);
159    func_mysqli_stmt_bind_result($link, $engine, "i", "TINYINT UNSIGNED", NULL, 80);
160
161    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOL", 1, 100);
162    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOL", NULL, 120);
163    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOLEAN", 0, 140);
164    func_mysqli_stmt_bind_result($link, $engine, "i", "BOOLEAN", NULL, 160);
165
166    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", -32768, 180);
167    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", 32767, 200);
168    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT", NULL, 220);
169    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT UNSIGNED", 65535, 240);
170    func_mysqli_stmt_bind_result($link, $engine, "i", "SMALLINT UNSIGNED", NULL, 260);
171
172    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", -8388608, 280, "integer");
173    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", 8388607, 300, "integer");
174    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT", NULL, 320);
175    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT UNSIGNED", 16777215, 340, "integer");
176    func_mysqli_stmt_bind_result($link, $engine, "d", "MEDIUMINT UNSIGNED", NULL, 360);
177
178    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", (defined("PHP_INT_MAX")) ? max(-1 * PHP_INT_MAX + 1, -2147483648) : 1, 380);
179    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", -2147483647, 400, "integer");
180    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", (defined("PHP_INT_MAX")) ? min(2147483647, PHP_INT_MAX) : 1, 420);
181    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER", NULL, 440);
182    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", (defined("PHP_INT_MAX")) ? min(4294967295, 2147483647) : 1, 460);
183    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", 4294967295, 480, (defined("PHP_INT_MAX") && (4294967295 > PHP_INT_MAX)) ? "string" : null);
184    func_mysqli_stmt_bind_result($link, $engine, "i", "INTEGER UNSIGNED", NULL, 500);
185
186    /* test is broken too: we bind "integer" but value is a float
187    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -9223372036854775808, 520);
188    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", 18446744073709551615, 560);
189    */
190    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", NULL, 540);
191    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", NULL, 580);
192    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -1, 1780);
193    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", 1, 1800);
194    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT", -1 * PHP_INT_MAX + 1, 1820);
195    func_mysqli_stmt_bind_result($link, $engine, "i", "BIGINT UNSIGNED", PHP_INT_MAX, 1840);
196    func_mysqli_stmt_bind_result($link, $engine, "s", "BIGINT UNSIGNED", "18446744073709551615", 1860);
197    func_mysqli_stmt_bind_result($link, $engine, "s", "BIGINT", "-9223372036854775808", 1880);
198
199    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT", -9223372036854775808 - 1.1, 600);
200    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT", NULL, 620);
201    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT UNSIGNED", 18446744073709551615 + 1.1, 640);
202    func_mysqli_stmt_bind_result($link, $engine, "d", "FLOAT UNSIGNED ", NULL, 660);
203
204    // Yes, we need the temporary variable. The PHP casting will fouls us otherwise.
205    $tmp = strval('-99999999.99');
206    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2)", $tmp, 680, "string");
207    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2)", NULL, 700);
208    $tmp = strval('99999999.99');
209    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2) UNSIGNED", $tmp , 720, "string");
210    func_mysqli_stmt_bind_result($link, $engine, "d", "DOUBLE(10,2) UNSIGNED", NULL, 740);
211    $tmp = strval('-99999999.99');
212    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", $tmp, 760, "string");
213    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", NULL, 780);
214    $tmp = strval('99999999.99');
215    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", $tmp, 800, "string");
216    func_mysqli_stmt_bind_result($link, $engine, "d", "DECIMAL(10,2)", NULL, 820);
217
218    // don't care about date() strict TZ warnings...
219    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE", @date('Y-m-d'), 840);
220    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE NOT NULL", @date('Y-m-d'), 860);
221    func_mysqli_stmt_bind_result($link, $engine, "s", "DATE", NULL, 880);
222
223    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME", @date('Y-m-d H:i:s'), 900);
224    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME NOT NULL", @date('Y-m-d H:i:s'), 920);
225    func_mysqli_stmt_bind_result($link, $engine, "s", "DATETIME", NULL, 940);
226
227    func_mysqli_stmt_bind_result($link, $engine, "s", "TIMESTAMP", @date('Y-m-d H:i:s'), 960);
228
229    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", @date('H:i:s'), 980);
230    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME NOT NULL", @date('H:i:s'), 1000);
231    func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", NULL, 1020);
232
233    $tmp = intval(@date('Y'));
234    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR", $tmp, 1040, "integer");
235    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR NOT NULL", $tmp, 1060, "integer");
236    func_mysqli_stmt_bind_result($link, $engine, "s", "YEAR", NULL, 1080);
237
238    $string255 = func_mysqli_stmt_bind_make_string(255);
239    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1)", "a", 1110, 'string');
240    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(255)", $string255, 1120, 'string');
241    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1) NOT NULL", "a", 1140, 'string');
242    func_mysqli_stmt_bind_result($link, $engine, "s", "CHAR(1)", NULL, 1160);
243
244    $string65k = func_mysqli_stmt_bind_make_string(65535);
245    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1)", "a", 1180, 'string');
246    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(255)", $string255, 1200, 'string');
247    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(65635)", $string65k, 1220, 'string');
248    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1) NOT NULL", "a", 1240, 'string');
249    func_mysqli_stmt_bind_result($link, $engine, "s", "VARCHAR(1)", NULL, 1260);
250
251    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", "a", 1280);
252    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", chr(0), 1300);
253    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1) NOT NULL", "b", 1320);
254    func_mysqli_stmt_bind_result($link, $engine, "s", "BINARY(1)", NULL, 1340);
255
256    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", "a", 1360);
257    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", chr(0), 1380);
258    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1) NOT NULL", "b", 1400);
259    func_mysqli_stmt_bind_result($link, $engine, "s", "VARBINARY(1)", NULL, 1420);
260
261    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", "a", 1440);
262    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", chr(0), 1460);
263    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB NOT NULL", "b", 1480);
264    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYBLOB", NULL, 1500);
265
266    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT", "a", 1520, 'string');
267    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT NOT NULL", "a", 1540, 'string');
268    func_mysqli_stmt_bind_result($link, $engine, "s", "TINYTEXT", NULL, 1560, 'string');
269
270    // Note: you cannot insert any blob values this way. But you can check the API at least partly this way
271    // Extra BLOB tests are in mysqli_stmt_send_long()
272    func_mysqli_stmt_bind_result($link, $engine, "b", "BLOB", "", 1580);
273    func_mysqli_stmt_bind_result($link, $engine, "b", "TEXT", "", 1600, 'string');
274    func_mysqli_stmt_bind_result($link, $engine, "b", "MEDIUMBLOB", "", 1620);
275    func_mysqli_stmt_bind_result($link, $engine, "b", "MEDIUMTEXT", "", 1640, 'string');
276
277    /* Is this one related? http://bugs.php.net/bug.php?id=35759 */
278    if (($IS_MYSQLND) || (!$IS_MYSQLND && (ini_get('memory_limit') > 4294967296))) {
279        /* NOTE: the MySQL Client Library - not mysqlnd - will allocate
280        a hugge max_length(type) = 4GB bind buffer */
281        func_mysqli_stmt_bind_result($link, $engine, "b", "LONGBLOB", "", 1660);
282        func_mysqli_stmt_bind_result($link, $engine, "b", "LONGTEXT", "", 1680, 'string');
283    }
284
285    func_mysqli_stmt_bind_result($link, $engine, "s", "ENUM('a', 'b')", "a", 1700, 'string');
286    func_mysqli_stmt_bind_result($link, $engine, "s", "ENUM('a', 'b')", NULL, 1720, 'string');
287    func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", "a", 1740, 'string');
288    func_mysqli_stmt_bind_result($link, $engine, "s", "SET('a', 'b')", NULL, 1760, 'string');
289
290    if (mysqli_get_server_version($link) >= 50600)
291        func_mysqli_stmt_bind_result($link, $engine, "s", "TIME", "13:31:34.123456", 1770, "13:31:34");
292
293    $stmt = mysqli_stmt_init($link);
294    if (!mysqli_stmt_prepare($stmt, "INSERT INTO test(id, label) VALUES (1000, 'z')"))
295        printf("[3001] [%d] %s\n", mysqli_stmt_errno($stmt), mysqli_stmt_error($stmt));
296
297    $id = null;
298    try {
299        mysqli_stmt_bind_result($stmt, $id);
300    } catch (\ArgumentCountError $e) {
301        $e->getMessage() . \PHP_EOL;
302    }
303
304    mysqli_stmt_close($stmt);
305
306    mysqli_close($link);
307    print "done!";
308?>
309--CLEAN--
310<?php
311    require_once("clean_table.inc");
312?>
313--EXPECTF--
314mysqli_stmt object is not fully initialized
315Number of bind variables doesn't match number of fields in prepared statement
316Number of bind variables doesn't match number of fields in prepared statement
317int(1)
318%s(1) "a"
319done!
320