Skip to content

Commit 54222a6

Browse files
Implement mysqli_fetch_column (#6798)
* Implement mysqli_fetch_column
1 parent 810fb59 commit 54222a6

6 files changed

+200
-1
lines changed

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,9 @@ PHP 8.1 UPGRADE NOTES
226226
. Binding in execute has been added to mysqli prepared statements.
227227
Parameters can now be passed to mysqli_stmt::execute as an array.
228228
RFC: https://wiki.php.net/rfc/mysqli_bind_in_execute
229+
. A new function has been added to mysqli_result called mysqli_fetch_column().
230+
It allows for fetching single scalar values from the result set.
231+
RFC: https://wiki.php.net/rfc/mysqli_fetch_column
229232

230233
- PDO MySQL:
231234
. The PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY attribute has been added, which

ext/mysqli/mysqli.stub.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,11 @@ public function fetch_object(string $class = "stdClass", array $constructor_args
426426
*/
427427
public function fetch_row() {}
428428

429+
/**
430+
* @alias mysqli_fetch_column
431+
*/
432+
public function fetch_column(int $column = 0): null|int|float|string|false {}
433+
429434
/**
430435
* @return bool
431436
* @alias mysqli_field_seek
@@ -664,6 +669,8 @@ function mysqli_fetch_object(mysqli_result $result, string $class = "stdClass",
664669

665670
function mysqli_fetch_row(mysqli_result $result): array|null|false {}
666671

672+
function mysqli_fetch_column(mysqli_result $result, int $column = 0): null|int|float|string|false {}
673+
667674
function mysqli_field_count(mysqli $mysql): int {}
668675

669676
function mysqli_field_seek(mysqli_result $result, int $index): bool {}

ext/mysqli/mysqli_arginfo.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: b0232d18f570208d673ad7535ca60997e038acb8 */
2+
* Stub hash: 42fdb807a547cfa3ba35bb2b8a4ee0f0d556a18c */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mysqli_affected_rows, 0, 1, MAY_BE_LONG|MAY_BE_STRING)
55
ZEND_ARG_OBJ_INFO(0, mysql, mysqli, 0)
@@ -115,6 +115,11 @@ ZEND_END_ARG_INFO()
115115

116116
#define arginfo_mysqli_fetch_row arginfo_mysqli_fetch_assoc
117117

118+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_mysqli_fetch_column, 0, 1, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_FALSE)
119+
ZEND_ARG_OBJ_INFO(0, result, mysqli_result, 0)
120+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, column, IS_LONG, 0, "0")
121+
ZEND_END_ARG_INFO()
122+
118123
#define arginfo_mysqli_field_count arginfo_mysqli_errno
119124

120125
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_mysqli_field_seek, 0, 2, _IS_BOOL, 0)
@@ -609,6 +614,10 @@ ZEND_END_ARG_INFO()
609614

610615
#define arginfo_class_mysqli_result_fetch_row arginfo_class_mysqli_character_set_name
611616

617+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_mysqli_result_fetch_column, 0, 0, MAY_BE_NULL|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_STRING|MAY_BE_FALSE)
618+
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, column, IS_LONG, 0, "0")
619+
ZEND_END_ARG_INFO()
620+
612621
#define arginfo_class_mysqli_result_field_seek arginfo_class_mysqli_result_fetch_field_direct
613622

614623
#define arginfo_class_mysqli_result_free_result arginfo_class_mysqli_character_set_name
@@ -709,6 +718,7 @@ ZEND_FUNCTION(mysqli_fetch_array);
709718
ZEND_FUNCTION(mysqli_fetch_assoc);
710719
ZEND_FUNCTION(mysqli_fetch_object);
711720
ZEND_FUNCTION(mysqli_fetch_row);
721+
ZEND_FUNCTION(mysqli_fetch_column);
712722
ZEND_FUNCTION(mysqli_field_count);
713723
ZEND_FUNCTION(mysqli_field_seek);
714724
ZEND_FUNCTION(mysqli_field_tell);
@@ -833,6 +843,7 @@ static const zend_function_entry ext_functions[] = {
833843
ZEND_FE(mysqli_fetch_assoc, arginfo_mysqli_fetch_assoc)
834844
ZEND_FE(mysqli_fetch_object, arginfo_mysqli_fetch_object)
835845
ZEND_FE(mysqli_fetch_row, arginfo_mysqli_fetch_row)
846+
ZEND_FE(mysqli_fetch_column, arginfo_mysqli_fetch_column)
836847
ZEND_FE(mysqli_field_count, arginfo_mysqli_field_count)
837848
ZEND_FE(mysqli_field_seek, arginfo_mysqli_field_seek)
838849
ZEND_FE(mysqli_field_tell, arginfo_mysqli_field_tell)
@@ -998,6 +1009,7 @@ static const zend_function_entry class_mysqli_result_methods[] = {
9981009
ZEND_ME_MAPPING(fetch_assoc, mysqli_fetch_assoc, arginfo_class_mysqli_result_fetch_assoc, ZEND_ACC_PUBLIC)
9991010
ZEND_ME_MAPPING(fetch_object, mysqli_fetch_object, arginfo_class_mysqli_result_fetch_object, ZEND_ACC_PUBLIC)
10001011
ZEND_ME_MAPPING(fetch_row, mysqli_fetch_row, arginfo_class_mysqli_result_fetch_row, ZEND_ACC_PUBLIC)
1012+
ZEND_ME_MAPPING(fetch_column, mysqli_fetch_column, arginfo_class_mysqli_result_fetch_column, ZEND_ACC_PUBLIC)
10011013
ZEND_ME_MAPPING(field_seek, mysqli_field_seek, arginfo_class_mysqli_result_field_seek, ZEND_ACC_PUBLIC)
10021014
ZEND_ME_MAPPING(free_result, mysqli_free_result, arginfo_class_mysqli_result_free_result, ZEND_ACC_PUBLIC)
10031015
ZEND_ME(mysqli_result, getIterator, arginfo_class_mysqli_result_getIterator, ZEND_ACC_PUBLIC)

ext/mysqli/mysqli_nonapi.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,39 @@ PHP_FUNCTION(mysqli_fetch_assoc)
436436
}
437437
/* }}} */
438438

439+
/* {{{ Fetch a column from the result row */
440+
PHP_FUNCTION(mysqli_fetch_column)
441+
{
442+
MYSQL_RES *result;
443+
zval *mysql_result;
444+
zval row_array;
445+
zend_long col_no = 0;
446+
447+
if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &col_no) == FAILURE) {
448+
RETURN_THROWS();
449+
}
450+
MYSQLI_FETCH_RESOURCE(result, MYSQL_RES*, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
451+
452+
if (col_no < 0) {
453+
zend_argument_value_error(ERROR_ARG_POS(2), "must be greater than or equal to 0");
454+
RETURN_THROWS();
455+
}
456+
if (col_no >= mysql_num_fields(result)) {
457+
zend_argument_value_error(ERROR_ARG_POS(2), "must be less than the number of fields for this result set");
458+
RETURN_THROWS();
459+
}
460+
461+
php_mysqli_fetch_into_hash_aux(&row_array, result, MYSQLI_NUM);
462+
if (Z_TYPE(row_array) != IS_ARRAY) {
463+
zval_ptr_dtor_nogc(&row_array);
464+
RETURN_FALSE;
465+
}
466+
467+
ZVAL_COPY(return_value, zend_hash_index_find(Z_ARR(row_array), col_no));
468+
zval_ptr_dtor_nogc(&row_array);
469+
}
470+
/* }}} */
471+
439472
/* {{{ Fetches all result rows as an associative array, a numeric array, or both */
440473
PHP_FUNCTION(mysqli_fetch_all)
441474
{

ext/mysqli/tests/mysqli_class_mysqli_result_interface.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ require_once('skipifconnectfailure.inc');
3535
'fetch_fields' => true,
3636
'fetch_object' => true,
3737
'fetch_row' => true,
38+
'fetch_column' => true,
3839
'field_seek' => true,
3940
'free' => true,
4041
'free_result' => true,
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
--TEST--
2+
mysqli_fetch_column()
3+
--SKIPIF--
4+
<?php
5+
require_once 'skipif.inc';
6+
require_once 'skipifconnectfailure.inc';
7+
?>
8+
--FILE--
9+
<?php
10+
11+
require_once "connect.inc";
12+
require 'table.inc';
13+
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
14+
15+
$res = mysqli_query($link, "SELECT id, label FROM test ORDER BY id LIMIT 1");
16+
17+
print "[001]\n";
18+
var_dump(mysqli_fetch_column($res));
19+
20+
print "[002]\n";
21+
var_dump(mysqli_fetch_column($res));
22+
23+
24+
$link->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true);
25+
26+
$res = mysqli_query($link, "SELECT
27+
1 AS a,
28+
2 AS a
29+
");
30+
print "[003]\n";
31+
var_dump(mysqli_fetch_column($res, 0));
32+
33+
$res = mysqli_query($link, "SELECT
34+
1 AS a,
35+
2 AS a
36+
");
37+
print "[004]\n";
38+
var_dump(mysqli_fetch_column($res, 1));
39+
40+
$res = mysqli_query($link, "SELECT
41+
1 AS a,
42+
2 AS a,
43+
3
44+
");
45+
print "[005]\n";
46+
var_dump(mysqli_fetch_column($res, 2));
47+
48+
$res = mysqli_query($link, "SELECT
49+
1 AS a,
50+
2 AS a,
51+
3,
52+
NULL AS d
53+
");
54+
print "[006]\n";
55+
var_dump(mysqli_fetch_column($res, 3));
56+
57+
$res = mysqli_query($link, "SELECT
58+
1 AS a,
59+
2 AS a,
60+
3,
61+
NULL AS d,
62+
true AS e
63+
");
64+
print "[007]\n";
65+
var_dump(mysqli_fetch_column($res, 4));
66+
67+
$res = mysqli_query($link, "SELECT
68+
1.42 AS a
69+
");
70+
print "[008]\n";
71+
var_dump(mysqli_fetch_column($res, 0));
72+
73+
$res = mysqli_query($link, "SELECT
74+
1.42E0 AS a
75+
");
76+
print "[009]\n";
77+
var_dump(mysqli_fetch_column($res, 0));
78+
79+
$res = mysqli_query($link, "SELECT id, label FROM test ORDER BY id LIMIT 1");
80+
print "[010]\n";
81+
try {
82+
var_dump(mysqli_fetch_column($res, -1));
83+
} catch (\ValueError $e) {
84+
echo $e->getMessage(), \PHP_EOL;
85+
}
86+
87+
$res = mysqli_query($link, "SELECT id, label FROM test ORDER BY id LIMIT 1");
88+
print "[011]\n";
89+
try {
90+
var_dump(mysqli_fetch_column($res, 2));
91+
} catch (\ValueError $e) {
92+
echo $e->getMessage(), \PHP_EOL;
93+
}
94+
95+
mysqli_free_result($res);
96+
try {
97+
mysqli_fetch_column($res);
98+
} catch (Error $exception) {
99+
echo $exception->getMessage() . "\n";
100+
}
101+
102+
$res = $link->query("SELECT id, label FROM test ORDER BY id LIMIT 2");
103+
104+
print "[012]\n";
105+
var_dump($res->fetch_column());
106+
107+
print "[013]\n";
108+
var_dump($res->fetch_column(1));
109+
110+
mysqli_close($link);
111+
?>
112+
--CLEAN--
113+
<?php
114+
require_once "clean_table.inc";
115+
?>
116+
--EXPECT--
117+
[001]
118+
string(1) "1"
119+
[002]
120+
bool(false)
121+
[003]
122+
int(1)
123+
[004]
124+
int(2)
125+
[005]
126+
int(3)
127+
[006]
128+
NULL
129+
[007]
130+
int(1)
131+
[008]
132+
string(4) "1.42"
133+
[009]
134+
float(1.42)
135+
[010]
136+
mysqli_fetch_column(): Argument #2 ($column) must be greater than or equal to 0
137+
[011]
138+
mysqli_fetch_column(): Argument #2 ($column) must be less than the number of fields for this result set
139+
mysqli_result object is already closed
140+
[012]
141+
int(1)
142+
[013]
143+
string(1) "b"

0 commit comments

Comments
 (0)