Skip to content

Commit c5f08d2

Browse files
committed
Allow step to be negative for decreasing ranges
1 parent b92f92f commit c5f08d2

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
@@ -2840,6 +2840,7 @@ PHP_FUNCTION(range)
28402840
{
28412841
zval *user_start, *user_end, *user_step = NULL, tmp;
28422842
bool is_step_double = false;
2843+
bool is_step_negative = false;
28432844
double step_double = 1.0;
28442845
zend_long step = 1;
28452846

@@ -2865,10 +2866,7 @@ PHP_FUNCTION(range)
28652866

28662867
/* We only want positive step values. */
28672868
if (step_double < 0.0) {
2868-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2869-
if (UNEXPECTED(EG(exception))) {
2870-
RETURN_THROWS();
2871-
}
2869+
is_step_negative = true;
28722870
step_double *= -1;
28732871
}
28742872
step = zend_dval_to_lval(step_double);
@@ -2879,10 +2877,7 @@ PHP_FUNCTION(range)
28792877
step = Z_LVAL_P(user_step);
28802878
/* We only want positive step values. */
28812879
if (step < 0) {
2882-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2883-
if (UNEXPECTED(EG(exception))) {
2884-
RETURN_THROWS();
2885-
}
2880+
is_step_negative = true;
28862881
step *= -1;
28872882
}
28882883
step_double = (double) step;
@@ -2942,7 +2937,8 @@ PHP_FUNCTION(range)
29422937
unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
29432938
unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
29442939

2945-
if (low > high) { /* Negative Steps */
2940+
/* Decreasing char range */
2941+
if (low > high) {
29462942
if (low - high < step) {
29472943
goto boundary_error;
29482944
}
@@ -2958,7 +2954,10 @@ PHP_FUNCTION(range)
29582954
}
29592955
}
29602956
} ZEND_HASH_FILL_END();
2961-
} else if (high > low) { /* Positive Steps */
2957+
} else if (high > low) { /* Increasing char range */
2958+
if (is_step_negative) {
2959+
goto negative_step_error;
2960+
}
29622961
if (high - low < step) {
29632962
goto boundary_error;
29642963
}
@@ -2986,7 +2985,8 @@ PHP_FUNCTION(range)
29862985
double element;
29872986
uint32_t i, size;
29882987

2989-
if (start_double > end_double) { /* Negative steps */
2988+
/* Decreasing float range */
2989+
if (start_double > end_double) {
29902990
if (start_double - end_double < step_double) {
29912991
goto boundary_error;
29922992
}
@@ -2999,7 +2999,10 @@ PHP_FUNCTION(range)
29992999
ZEND_HASH_FILL_NEXT();
30003000
}
30013001
} ZEND_HASH_FILL_END();
3002-
} else if (end_double > start_double) { /* Positive steps */
3002+
} else if (end_double > start_double) { /* Increasing float range */
3003+
if (is_step_negative) {
3004+
goto negative_step_error;
3005+
}
30033006
if (end_double - start_double < step_double) {
30043007
goto boundary_error;
30053008
}
@@ -3023,7 +3026,8 @@ PHP_FUNCTION(range)
30233026
zend_ulong unsigned_step= (zend_ulong)step;
30243027
uint32_t i, size;
30253028

3026-
if (start_long > end_long) { /* Negative steps */
3029+
/* Decreasing int range */
3030+
if (start_long > end_long) {
30273031
if ((zend_ulong)start_long - end_long < unsigned_step) {
30283032
goto boundary_error;
30293033
}
@@ -3036,7 +3040,10 @@ PHP_FUNCTION(range)
30363040
ZEND_HASH_FILL_NEXT();
30373041
}
30383042
} ZEND_HASH_FILL_END();
3039-
} else if (end_long > start_long) { /* Positive steps */
3043+
} else if (end_long > start_long) { /* Increasing int range */
3044+
if (is_step_negative) {
3045+
goto negative_step_error;
3046+
}
30403047
if ((zend_ulong)end_long - start_long < unsigned_step) {
30413048
goto boundary_error;
30423049
}
@@ -3057,6 +3064,10 @@ PHP_FUNCTION(range)
30573064
}
30583065
return;
30593066

3067+
negative_step_error:
3068+
zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
3069+
RETURN_THROWS();
3070+
30603071
boundary_error:
30613072
zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
30623073
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)