Skip to content

Commit bef2e32

Browse files
committed
Handle digit strings as strings and not ints
1 parent 93f8913 commit bef2e32

File tree

5 files changed

+170
-57
lines changed

5 files changed

+170
-57
lines changed

ext/standard/array.c

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2781,6 +2781,12 @@ PHP_FUNCTION(array_fill_keys)
27812781
zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
27822782
} while (0)
27832783

2784+
/* Process input for the range() function
2785+
* 0 on exceptions
2786+
* IS_LONG if only interpretable as int
2787+
* IS_DOUBLE if only interpretable as float
2788+
* IS_STRING if only interpretable as string
2789+
* IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
27842790
static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
27852791
{
27862792
switch (Z_TYPE_P(input)) {
@@ -2801,6 +2807,14 @@ static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend
28012807
}
28022808
return IS_DOUBLE;
28032809
case IS_STRING: {
2810+
/* Process strings:
2811+
* - Empty strings are converted to 0 with a diagnostic
2812+
* - Check if string is numeric and store the values in passed pointer
2813+
* - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2814+
* - If numeric int, check it is one byte or not
2815+
* - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2816+
* - If not should only be interpreted as int, return IS_LONG;
2817+
* - Otherwise is a string and return IS_STRING */
28042818
if (Z_STRLEN_P(input) == 0) {
28052819
const char *arg_name = get_active_function_arg_name(arg_num);
28062820
php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
@@ -2817,7 +2831,11 @@ static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend
28172831
}
28182832
if (type == IS_LONG) {
28192833
*dval = (double) *lval;
2820-
return IS_LONG;
2834+
if (Z_STRLEN_P(input) == 1) {
2835+
return IS_ARRAY;
2836+
} else {
2837+
return IS_LONG;
2838+
}
28212839
}
28222840
if (Z_STRLEN_P(input) != 1) {
28232841
const char *arg_name = get_active_function_arg_name(arg_num);
@@ -2905,15 +2923,20 @@ PHP_FUNCTION(range)
29052923
}
29062924

29072925
/* If the range is given as strings, generate an array of characters. */
2908-
if (start_type == IS_STRING || end_type == IS_STRING) {
2909-
if (UNEXPECTED(start_type != end_type)) {
2910-
if (start_type != IS_STRING) {
2911-
php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a string if argument #2 ($end)"
2912-
" is a string, argument #2 ($end) converted to 0");
2926+
if (start_type >= IS_STRING || end_type >= IS_STRING) {
2927+
/* If one of the inputs is NOT a string */
2928+
if (UNEXPECTED(start_type + end_type < 2*IS_STRING)) {
2929+
if (start_type < IS_STRING) {
2930+
if (end_type != IS_ARRAY) {
2931+
php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a string if"
2932+
" argument #2 ($end) is a string, argument #2 ($end) converted to 0");
2933+
}
29132934
end_type = IS_LONG;
2914-
} else {
2915-
php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a string if argument #1 ($start)"
2916-
" is a string, argument #1 ($start) converted to 0");
2935+
} else if (end_type < IS_STRING) {
2936+
if (start_type != IS_ARRAY) {
2937+
php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a string if"
2938+
" argument #1 ($start) is a string, argument #1 ($start) converted to 0");
2939+
}
29172940
start_type = IS_LONG;
29182941
}
29192942
if (UNEXPECTED(EG(exception))) {
@@ -2923,8 +2946,11 @@ PHP_FUNCTION(range)
29232946
}
29242947

29252948
if (is_step_double) {
2926-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2927-
" of characters, inputs converted to 0");
2949+
/* Only emit warning if one of the input is not a numeric digit */
2950+
if (start_type == IS_STRING || end_type == IS_STRING) {
2951+
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2952+
" of characters, inputs converted to 0");
2953+
}
29282954
if (UNEXPECTED(EG(exception))) {
29292955
RETURN_THROWS();
29302956
}

ext/standard/tests/array/range/range_inputs_numeric_basic.phpt

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ var_dump( range(1, 2, 0.1) );
3232
var_dump( range(2, 1, 0.1) );
3333

3434
var_dump( range(1, 2, "0.1") );
35-
var_dump( range("1", "2", 0.1) );
3635

3736
echo "Done\n";
3837
?>
@@ -272,28 +271,4 @@ array(11) {
272271
[10]=>
273272
float(2)
274273
}
275-
array(11) {
276-
[0]=>
277-
float(1)
278-
[1]=>
279-
float(1.1)
280-
[2]=>
281-
float(1.2)
282-
[3]=>
283-
float(1.3)
284-
[4]=>
285-
float(1.4)
286-
[5]=>
287-
float(1.5)
288-
[6]=>
289-
float(1.6)
290-
[7]=>
291-
float(1.7)
292-
[8]=>
293-
float(1.8)
294-
[9]=>
295-
float(1.9)
296-
[10]=>
297-
float(2)
298-
}
299274
Done
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
--TEST--
2+
Test range() function digits
3+
--FILE--
4+
<?php
5+
echo "Only digits\n";
6+
var_dump( range("1", "9") );
7+
var_dump( range("9", "1") );
8+
9+
echo "Only digits and char\n";
10+
var_dump( range("9", "A") );
11+
var_dump( range("A", "9") );
12+
echo "Done\n";
13+
?>
14+
--EXPECT--
15+
Only digits
16+
array(9) {
17+
[0]=>
18+
string(1) "1"
19+
[1]=>
20+
string(1) "2"
21+
[2]=>
22+
string(1) "3"
23+
[3]=>
24+
string(1) "4"
25+
[4]=>
26+
string(1) "5"
27+
[5]=>
28+
string(1) "6"
29+
[6]=>
30+
string(1) "7"
31+
[7]=>
32+
string(1) "8"
33+
[8]=>
34+
string(1) "9"
35+
}
36+
array(9) {
37+
[0]=>
38+
string(1) "9"
39+
[1]=>
40+
string(1) "8"
41+
[2]=>
42+
string(1) "7"
43+
[3]=>
44+
string(1) "6"
45+
[4]=>
46+
string(1) "5"
47+
[5]=>
48+
string(1) "4"
49+
[6]=>
50+
string(1) "3"
51+
[7]=>
52+
string(1) "2"
53+
[8]=>
54+
string(1) "1"
55+
}
56+
Only digits and char
57+
array(9) {
58+
[0]=>
59+
string(1) "9"
60+
[1]=>
61+
string(1) ":"
62+
[2]=>
63+
string(1) ";"
64+
[3]=>
65+
string(1) "<"
66+
[4]=>
67+
string(1) "="
68+
[5]=>
69+
string(1) ">"
70+
[6]=>
71+
string(1) "?"
72+
[7]=>
73+
string(1) "@"
74+
[8]=>
75+
string(1) "A"
76+
}
77+
array(9) {
78+
[0]=>
79+
string(1) "A"
80+
[1]=>
81+
string(1) "@"
82+
[2]=>
83+
string(1) "?"
84+
[3]=>
85+
string(1) ">"
86+
[4]=>
87+
string(1) "="
88+
[5]=>
89+
string(1) "<"
90+
[6]=>
91+
string(1) ";"
92+
[7]=>
93+
string(1) ":"
94+
[8]=>
95+
string(1) "9"
96+
}
97+
Done
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
Test range() function where boundary are string digits and step is a float
3+
--INI--
4+
serialize_precision=14
5+
--FILE--
6+
<?php
7+
var_dump( range("1", "2", 0.1) );
8+
?>
9+
--EXPECT--
10+
array(11) {
11+
[0]=>
12+
float(1)
13+
[1]=>
14+
float(1.1)
15+
[2]=>
16+
float(1.2)
17+
[3]=>
18+
float(1.3)
19+
[4]=>
20+
float(1.4)
21+
[5]=>
22+
float(1.5)
23+
[6]=>
24+
float(1.6)
25+
[7]=>
26+
float(1.7)
27+
[8]=>
28+
float(1.8)
29+
[9]=>
30+
float(1.9)
31+
[10]=>
32+
float(2)
33+
}

ext/standard/tests/array/range/range_inputs_string_invalid.phpt

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Test range() function with basic/expected string inputs
2+
Test range() function with unexpected string inputs
33
--INI--
44
serialize_precision=14
55
--FILE--
@@ -12,9 +12,7 @@ echo "Range cannot operate on an empty string\n";
1212
var_dump( range("Z", "") ); // Both strings are cast to int, i.e. 0
1313
var_dump( range("", "Z") ); // Both strings are cast to int, i.e. 0
1414

15-
echo "Mixing numeric string and character\n";
16-
var_dump( range("1", "A") ); // The char is cast to an int, i.e. 0
17-
var_dump( range("?", "1") ); // The char is cast to an int, i.e. 0
15+
echo "Mixing numeric float string and character\n";
1816
var_dump( range("3.5", "A") ); // The char is cast to a float, i.e. 0
1917
var_dump( range("?", "3.5") ); // The char is cast to a float, i.e. 0
2018

@@ -52,23 +50,7 @@ array(1) {
5250
[0]=>
5351
int(0)
5452
}
55-
Mixing numeric string and character
56-
57-
Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 in %s on line %d
58-
array(2) {
59-
[0]=>
60-
int(1)
61-
[1]=>
62-
int(0)
63-
}
64-
65-
Warning: range(): Argument #2 ($end) must be a string if argument #1 ($start) is a string, argument #1 ($start) converted to 0 in %s on line %d
66-
array(2) {
67-
[0]=>
68-
int(0)
69-
[1]=>
70-
int(1)
71-
}
53+
Mixing numeric float string and character
7254

7355
Warning: range(): Argument #1 ($start) must be a string if argument #2 ($end) is a string, argument #2 ($end) converted to 0 in %s on line %d
7456
array(4) {

0 commit comments

Comments
 (0)