Skip to content

Commit c55fa01

Browse files
committed
Allow step to be negative for decreasing ranges
1 parent 25aa84d commit c55fa01

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
@@ -2726,6 +2726,7 @@ PHP_FUNCTION(range)
27262726
{
27272727
zval *user_start, *user_end, *user_step = NULL, tmp;
27282728
bool is_step_double = false;
2729+
bool is_step_negative = false;
27292730
double step_double = 1.0;
27302731
zend_long step = 1;
27312732

@@ -2751,10 +2752,7 @@ PHP_FUNCTION(range)
27512752

27522753
/* We only want positive step values. */
27532754
if (step_double < 0.0) {
2754-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2755-
if (UNEXPECTED(EG(exception))) {
2756-
RETURN_THROWS();
2757-
}
2755+
is_step_negative = true;
27582756
step_double *= -1;
27592757
}
27602758
step = zend_dval_to_lval(step_double);
@@ -2765,10 +2763,7 @@ PHP_FUNCTION(range)
27652763
step = Z_LVAL_P(user_step);
27662764
/* We only want positive step values. */
27672765
if (step < 0) {
2768-
php_error_docref(NULL, E_WARNING, "Argument #3 ($step) must be greater than 0, $step multiplied by -1");
2769-
if (UNEXPECTED(EG(exception))) {
2770-
RETURN_THROWS();
2771-
}
2766+
is_step_negative = true;
27722767
step *= -1;
27732768
}
27742769
step_double = (double) step;
@@ -2828,7 +2823,8 @@ PHP_FUNCTION(range)
28282823
unsigned char low = (unsigned char)Z_STRVAL_P(user_start)[0];
28292824
unsigned char high = (unsigned char)Z_STRVAL_P(user_end)[0];
28302825

2831-
if (low > high) { /* Negative Steps */
2826+
/* Decreasing char range */
2827+
if (low > high) {
28322828
if (low - high < step) {
28332829
goto boundary_error;
28342830
}
@@ -2844,7 +2840,10 @@ PHP_FUNCTION(range)
28442840
}
28452841
}
28462842
} ZEND_HASH_FILL_END();
2847-
} else if (high > low) { /* Positive Steps */
2843+
} else if (high > low) { /* Increasing char range */
2844+
if (is_step_negative) {
2845+
goto negative_step_error;
2846+
}
28482847
if (high - low < step) {
28492848
goto boundary_error;
28502849
}
@@ -2872,7 +2871,8 @@ PHP_FUNCTION(range)
28722871
double element;
28732872
uint32_t i, size;
28742873

2875-
if (start_double > end_double) { /* Negative steps */
2874+
/* Decreasing float range */
2875+
if (start_double > end_double) {
28762876
if (start_double - end_double < step_double) {
28772877
goto boundary_error;
28782878
}
@@ -2885,7 +2885,10 @@ PHP_FUNCTION(range)
28852885
ZEND_HASH_FILL_NEXT();
28862886
}
28872887
} ZEND_HASH_FILL_END();
2888-
} else if (end_double > start_double) { /* Positive steps */
2888+
} else if (end_double > start_double) { /* Increasing float range */
2889+
if (is_step_negative) {
2890+
goto negative_step_error;
2891+
}
28892892
if (end_double - start_double < step_double) {
28902893
goto boundary_error;
28912894
}
@@ -2909,7 +2912,8 @@ PHP_FUNCTION(range)
29092912
zend_ulong unsigned_step= (zend_ulong)step;
29102913
uint32_t i, size;
29112914

2912-
if (start_long > end_long) { /* Negative steps */
2915+
/* Decreasing int range */
2916+
if (start_long > end_long) {
29132917
if ((zend_ulong)start_long - end_long < unsigned_step) {
29142918
goto boundary_error;
29152919
}
@@ -2922,7 +2926,10 @@ PHP_FUNCTION(range)
29222926
ZEND_HASH_FILL_NEXT();
29232927
}
29242928
} ZEND_HASH_FILL_END();
2925-
} else if (end_long > start_long) { /* Positive steps */
2929+
} else if (end_long > start_long) { /* Increasing int range */
2930+
if (is_step_negative) {
2931+
goto negative_step_error;
2932+
}
29262933
if ((zend_ulong)end_long - start_long < unsigned_step) {
29272934
goto boundary_error;
29282935
}
@@ -2943,6 +2950,10 @@ PHP_FUNCTION(range)
29432950
}
29442951
return;
29452952

2953+
negative_step_error:
2954+
zend_argument_value_error(3, "must be greater than 0 for increasing ranges");
2955+
RETURN_THROWS();
2956+
29462957
boundary_error:
29472958
zend_argument_value_error(3, "must be less than the range spanned by argument #1 ($start) and argument #2 ($end)");
29482959
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)