Skip to content

Commit 71dfb12

Browse files
committed
Allow step to be negative for decreasing ranges
1 parent 4f51aad commit 71dfb12

File tree

3 files changed

+63
-59
lines changed

3 files changed

+63
-59
lines changed

ext/standard/array.c

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2754,6 +2754,7 @@ PHP_FUNCTION(range)
27542754
{
27552755
zval *user_start, *user_end, *user_step = NULL, tmp;
27562756
bool is_step_double = false;
2757+
bool is_step_negative = false;
27572758
double step_double = 1.0;
27582759
zend_long step = 1;
27592760

@@ -2779,10 +2780,7 @@ PHP_FUNCTION(range)
27792780

27802781
/* We only want positive step values. */
27812782
if (step_double < 0.0) {
2782-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2783-
if (UNEXPECTED(EG(exception))) {
2784-
RETURN_THROWS();
2785-
}
2783+
is_step_negative = true;
27862784
step_double *= -1;
27872785
}
27882786
step = zend_dval_to_lval(step_double);
@@ -2793,10 +2791,7 @@ PHP_FUNCTION(range)
27932791
step = Z_LVAL_P(user_step);
27942792
/* We only want positive step values. */
27952793
if (step < 0) {
2796-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2797-
if (UNEXPECTED(EG(exception))) {
2798-
RETURN_THROWS();
2799-
}
2794+
is_step_negative = true;
28002795
step *= -1;
28012796
}
28022797
step_double = (double) step;
@@ -2856,7 +2851,8 @@ PHP_FUNCTION(range)
28562851
unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
28572852
unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
28582853

2859-
if (low > high) { /* Negative Steps */
2854+
/* Decreasing char range */
2855+
if (low > high) {
28602856
if (low - high < step) {
28612857
goto boundary_error;
28622858
}
@@ -2872,7 +2868,10 @@ PHP_FUNCTION(range)
28722868
}
28732869
}
28742870
} ZEND_HASH_FILL_END();
2875-
} else if (high > low) { /* Positive Steps */
2871+
} else if (high > low) { /* Increasing char range */
2872+
if (is_step_negative) {
2873+
goto negative_step_error;
2874+
}
28762875
if (high - low < step) {
28772876
goto boundary_error;
28782877
}
@@ -2900,7 +2899,8 @@ PHP_FUNCTION(range)
29002899
double element;
29012900
uint32_t i, size;
29022901

2903-
if (start_double > end_double) { /* Negative steps */
2902+
/* Decreasing float range */
2903+
if (start_double > end_double) {
29042904
if (start_double - end_double < step_double) {
29052905
goto boundary_error;
29062906
}
@@ -2913,7 +2913,10 @@ PHP_FUNCTION(range)
29132913
ZEND_HASH_FILL_NEXT();
29142914
}
29152915
} ZEND_HASH_FILL_END();
2916-
} else if (end_double > start_double) { /* Positive steps */
2916+
} else if (end_double > start_double) { /* Increasing float range */
2917+
if (is_step_negative) {
2918+
goto negative_step_error;
2919+
}
29172920
if (end_double - start_double < step_double) {
29182921
goto boundary_error;
29192922
}
@@ -2937,7 +2940,8 @@ PHP_FUNCTION(range)
29372940
zend_ulong unsigned_step= (zend_ulong)step;
29382941
uint32_t i, size;
29392942

2940-
if (start_long > end_long) { /* Negative steps */
2943+
/* Decreasing int range */
2944+
if (start_long > end_long) {
29412945
if ((zend_ulong)start_long - end_long < unsigned_step) {
29422946
goto boundary_error;
29432947
}
@@ -2950,7 +2954,10 @@ PHP_FUNCTION(range)
29502954
ZEND_HASH_FILL_NEXT();
29512955
}
29522956
} ZEND_HASH_FILL_END();
2953-
} else if (end_long > start_long) { /* Positive steps */
2957+
} else if (end_long > start_long) { /* Increasing int range */
2958+
if (is_step_negative) {
2959+
goto negative_step_error;
2960+
}
29542961
if ((zend_ulong)end_long - start_long < unsigned_step) {
29552962
goto boundary_error;
29562963
}
@@ -2971,6 +2978,10 @@ PHP_FUNCTION(range)
29712978
}
29722979
return;
29732980

2981+
negative_step_error:
2982+
zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
2983+
RETURN_THROWS();
2984+
29742985
boundary_error:
29752986
zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
29762987
RETURN_THROWS();
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
range() allows $step parameter to be negative for decreasing ranges
3+
--INI--
4+
precision=14
5+
--FILE--
6+
<?php
7+
var_dump(range('c', 'a', -1));
8+
var_dump(range(3, 1, -1));
9+
var_dump(range(3.5, 1.5, -1.5));
10+
?>
11+
--EXPECT--
12+
array(3) {
13+
[0]=>
14+
string(1) "c"
15+
[1]=>
16+
string(1) "b"
17+
[2]=>
18+
string(1) "a"
19+
}
20+
array(3) {
21+
[0]=>
22+
int(3)
23+
[1]=>
24+
int(2)
25+
[2]=>
26+
int(1)
27+
}
28+
array(2) {
29+
[0]=>
30+
float(3.5)
31+
[1]=>
32+
float(2)
33+
}

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

Lines changed: 5 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ try {
8787
echo $e->getMessage(), "\n";
8888
}
8989

90-
echo "Step must not be negative\n";
90+
echo "Step must not be negative for increasing ranges\n";
9191
try {
9292
var_dump(range('a', 'c', -1));
9393
} catch (\ValueError $e) {
@@ -98,11 +98,6 @@ try {
9898
} catch (\ValueError $e) {
9999
echo $e->getMessage(), "\n";
100100
}
101-
try {
102-
var_dump(range('a', 'c', -1));
103-
} catch (\ValueError $e) {
104-
echo $e->getMessage(), "\n";
105-
}
106101
try {
107102
var_dump(range(1.5, 3.5, -1.5));
108103
} catch (\ValueError $e) {
@@ -133,42 +128,7 @@ range(): Argument #3 ($step) must be less than the range spanned by argument #1
133128
range(): Argument #3 ($step) must be less than the range spanned by argument #1 ($start) and argument #2 ($end)
134129
-- Testing ( (low > high) && (low-high < step) ) for characters --
135130
range(): Argument #3 ($step) must be less than the range spanned by argument #1 ($start) and argument #2 ($end)
136-
Step must not be negative
137-
138-
Warning: range(): Argument #3 ($step) must be greater than 0, $step multiplied by -1 in %s on line %d
139-
array(3) {
140-
[0]=>
141-
string(1) "a"
142-
[1]=>
143-
string(1) "b"
144-
[2]=>
145-
string(1) "c"
146-
}
147-
148-
Warning: range(): Argument #3 ($step) must be greater than 0, $step multiplied by -1 in %s on line %d
149-
array(3) {
150-
[0]=>
151-
int(1)
152-
[1]=>
153-
int(2)
154-
[2]=>
155-
int(3)
156-
}
157-
158-
Warning: range(): Argument #3 ($step) must be greater than 0, $step multiplied by -1 in %s on line %d
159-
array(3) {
160-
[0]=>
161-
string(1) "a"
162-
[1]=>
163-
string(1) "b"
164-
[2]=>
165-
string(1) "c"
166-
}
167-
168-
Warning: range(): Argument #3 ($step) must be greater than 0, $step multiplied by -1 in %s on line %d
169-
array(2) {
170-
[0]=>
171-
float(1.5)
172-
[1]=>
173-
float(3)
174-
}
131+
Step must not be negative for increasing ranges
132+
range(): Argument #3 ($step) must be greater than 0 for increasing ranges
133+
range(): Argument #3 ($step) must be greater than 0 for increasing ranges
134+
range(): Argument #3 ($step) must be greater than 0 for increasing ranges

0 commit comments

Comments
 (0)