Skip to content

Commit 16e9b31

Browse files
committed
Handle digit strings as strings and not ints
1 parent 70cec65 commit 16e9b31

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
@@ -2695,6 +2695,12 @@ PHP_FUNCTION(array_fill_keys)
26952695
zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); \
26962696
} while (0)
26972697

2698+
/* Process input for the range() function
2699+
* 0 on exceptions
2700+
* IS_LONG if only interpretable as int
2701+
* IS_DOUBLE if only interpretable as float
2702+
* IS_STRING if only interpretable as string
2703+
* IS_ARRAY (as IS_LONG < IS_STRING < IS_ARRAY) for ambiguity of single byte strings which contains a digit */
26982704
static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend_long /* restrict */ *lval, double /* restrict */ *dval)
26992705
{
27002706
switch (Z_TYPE_P(input)) {
@@ -2715,6 +2721,14 @@ static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend
27152721
}
27162722
return IS_DOUBLE;
27172723
case IS_STRING: {
2724+
/* Process strings:
2725+
* - Empty strings are converted to 0 with a diagnostic
2726+
* - Check if string is numeric and store the values in passed pointer
2727+
* - If numeric float, this means it cannot be a numeric string with only one byte GOTO IS_DOUBLE
2728+
* - If numeric int, check it is one byte or not
2729+
* - If it one byte, return IS_ARRAY as IS_LONG < IS_STRING < IS_ARRAY
2730+
* - If not should only be interpreted as int, return IS_LONG;
2731+
* - Otherwise is a string and return IS_STRING */
27182732
if (Z_STRLEN_P(input) == 0) {
27192733
const char *arg_name = get_active_function_arg_name(arg_num);
27202734
php_error_docref(NULL, E_WARNING, "Argument #%d ($%s) must not be empty, casted to 0", arg_num, arg_name);
@@ -2731,7 +2745,11 @@ static uint8_t php_range_process_input(const zval *input, uint32_t arg_num, zend
27312745
}
27322746
if (type == IS_LONG) {
27332747
*dval = (double) *lval;
2734-
return IS_LONG;
2748+
if (Z_STRLEN_P(input) == 1) {
2749+
return IS_ARRAY;
2750+
} else {
2751+
return IS_LONG;
2752+
}
27352753
}
27362754
if (Z_STRLEN_P(input) != 1) {
27372755
const char *arg_name = get_active_function_arg_name(arg_num);
@@ -2819,15 +2837,20 @@ PHP_FUNCTION(range)
28192837
}
28202838

28212839
/* If the range is given as strings, generate an array of characters. */
2822-
if (start_type == IS_STRING || end_type == IS_STRING) {
2823-
if (UNEXPECTED(start_type != end_type)) {
2824-
if (start_type != IS_STRING) {
2825-
php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a string if argument #2 ($end)"
2826-
" is a string, argument #2 ($end) converted to 0");
2840+
if (start_type >= IS_STRING || end_type >= IS_STRING) {
2841+
/* If one of the inputs is NOT a string */
2842+
if (UNEXPECTED(start_type + end_type < 2*IS_STRING)) {
2843+
if (start_type < IS_STRING) {
2844+
if (end_type != IS_ARRAY) {
2845+
php_error_docref(NULL, E_WARNING, "Argument #1 ($start) must be a string if"
2846+
" argument #2 ($end) is a string, argument #2 ($end) converted to 0");
2847+
}
28272848
end_type = IS_LONG;
2828-
} else {
2829-
php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a string if argument #1 ($start)"
2830-
" is a string, argument #1 ($start) converted to 0");
2849+
} else if (end_type < IS_STRING) {
2850+
if (start_type != IS_ARRAY) {
2851+
php_error_docref(NULL, E_WARNING, "Argument #2 ($end) must be a string if"
2852+
" argument #1 ($start) is a string, argument #1 ($start) converted to 0");
2853+
}
28312854
start_type = IS_LONG;
28322855
}
28332856
if (UNEXPECTED(EG(exception))) {
@@ -2837,8 +2860,11 @@ PHP_FUNCTION(range)
28372860
}
28382861

28392862
if (is_step_double) {
2840-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2841-
" of characters, inputs converted to 0");
2863+
/* Only emit warning if one of the input is not a numeric digit */
2864+
if (start_type == IS_STRING || end_type == IS_STRING) {
2865+
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be of type int when generating an array"
2866+
" of characters, inputs converted to 0");
2867+
}
28422868
if (UNEXPECTED(EG(exception))) {
28432869
RETURN_THROWS();
28442870
}

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)