64

I have four conditions that I need to go through and I thought it would be best to use the switch statement in PHP. However, I need to check whether an integer is, let's say, less than or equal, or greater than and equal.

switch ($count) {
    case 20:
        $priority = 'low';
        break;

    case 40:
        $priority = 'medium';
        break;

    case 60:
        $priority = 'high';
        break;

    case 80:
        $priority = 'severe';
        break;
}

With an if() statement it would look like the following:

if ($count <= 20) {
    $priority = 'low';
} elseif ($count <= 40) {
    $priority = 'medium';
} elseif ($count <= 60) {
    $priority = 'high';
} else {
    $priority = 'severe';
}

Is that possible in switch-case?

3
  • 8
    Well you can switch(true) and return true on the cases that satisfy the range Example. If this was on a smaller scale, you could just repeat the numbers in range, and have them flow into each other Example. But yeah for your example, you should use an if statement. Commented Jul 17, 2014 at 20:26
  • @DaveChen Thats a nice trick. Commented Jul 17, 2014 at 20:41
  • According to this apparently you can -- search for (randomizer) -- php.net/manual/en/control-structures.switch.php Commented Jul 17, 2014 at 20:43

7 Answers 7

184

A more general case for solving this problem is:

switch (true) {
    case $count <= 20:
        $priority = 'low';
        break;

    case $count <= 40:
        $priority = 'medium';
        break;

    case $count <= 60:
        $priority = 'high';
        break;

    default:
        $priority = 'severe';
        break;
}

Also, since PHP 8.0 you can use a more concise match operator:

$priority = match(true) {
    $count <= 20 => 'low',
    $count <= 40 => 'medium',
    $count <= 60 => 'high',
    default => 'severe',
};
Sign up to request clarification or add additional context in comments.

4 Comments

The WHOLE reason (the only reason, IMO) to ever use a script-bloating switch block is to perform a single evaluation of a value and find the satifying case(s). This "solution" abandons this benefit performing multiple evaluations to satisfy true, so it is just as inefficient as an if-elseif-else block but with much more code bloat. I recommend something like Havenard's calculated lookup, it is concise, easy to maintain, efficient, and highly scalable.
The other solution only applies if ranges can be pre-calculated. This solution is perfect for ranges that are arbitrarily chosen. Imagine a date-time diff in seconds and you state blocks like "more than 1 year ago", "more than 1 month ago", "a few days ago", "yesterday", "a few hour ago", "a few minutes ago", "a few seconds ago", "just now". This needs the solution stated in this answer.
In addition: What is a switch other than a chain of if-elses where the first expression of the equality remains constant? A classic switch( $a ){ case 3: aaa; break; case 5: bbb; break; default: zzz; break; } is in fact an if $a == 3 do aaa, else-if $a == 5 do bbb else do zzz. This solution here is in fact a more compact notation of a if true == ($count <= 20) do aaa else-if true == ($count <=40) do bbb else if true == ($count <=60) do ccc else do zzz. Nothing bad in it, no?
That's an odd use for switch (), in most languages each case has to be a constant literal. It's weird to see that PHP supports doing this.
11

Switches can't do that, but in this particular case you can do something like this:

switch ((int)(($count - 1) / 20)) {
    case 0:
        $priority = 'low';
        break;
    case 1:
        $priority = 'medium';
        break;
    case 2:
        $priority = 'high';
        break;
    case 3:
        $priority = 'severe';
        break;
}

So in (int)(($count - 1) / 20) all values from 0 to 20 will eval to 0, 21 to 40 will eval to 1 and so on, allowing you to use the switch statement for this purpose.

And since we are concatenating values, we can even simplify to an array:

$priorities = ['low', 'medium', 'high', 'severe'];
$priority = $priorities[(int)(($count - 1) / 20)];

3 Comments

You don't need to cast as int because php doesn't permit floating point keys -- they will be truncated to integers automagically. 3v4l.org/R1kL1
@mickmackusa mmm... at any rate I suppose it is prudent to future proof the code and not rely on obscure conversions.
@Havenard Your prophecy has been fulfilled! Best to cast it explicitly now.
9

There is a way that works in PHP 7 using ternary assignment operators. The operator was introduced earlier on (5.4?), but I never tested the code on other versions. I wrote the whole switch code there, however for brevity here is just the specific clause. Let's say we want the condition to match for all numbers greater than or equal to five:

switch($value){
    case ($value >= 5 ? $value : !$value): // Do something here
    break;
}

We either allow the $value to pass unchanged or we negate the value according to the condition. A $value will always match itself or fail the test against its negation.

1 Comment

Can confirm that this approach works back to atleast php 5.5
2

No. switch() statements are for doing multiple equality tests. They're basically just a slightly easier to read (but also more hazardous) version of

if (x == 'a') { ... }
else if (x == 'b') { ... } 
else if (x == 'c') { ... }

code. There is no way to change a switch() away from == to < or any other comparison operator. It's strictly for equality testing.

1 Comment

@MacGyver in his solution he is still using "==" in the switch. His code is checking (($count <= 20) == true) for example.
1

Using the ternary operator:

$priority =
    // "switch" comparison for $count
    $count <= 20 ? 'low' :
    ($count <= 40 ? 'medium' :
    ($count <= 60 ? 'high' :
    // default above 60
    'severe'));

I know the common complaint that ternary operators can be hard to understand, but this is just too easy to do with a simple ?:.

It operates like the Excel "If" formula:

=IF( logical_test, value_if_true, value_if_false )
$variable = logical_test ? value_if_true : value_if_false

And you can nest the if statements (place a second ?: in the 'value_if_false' section of the first one), which is certainly where it can become confusing to read, but less so when you write it out line by line, as above.

My code above is basically equivalent to the if() statement written by the OP.

2 Comments

Oh, I didn't see @bytephunk using ternary operators before I posted
Still, plus 1, because the way you formatted it makes it look elegant and easy to understand in the code.
0

For PHP >= 8

$priority = match (intdiv($count, 20)) {
    0, 1 => 'low',
    2 => 'medium',
    3 => 'high',
    default=>'none'
}

1 Comment

it would have been a useful answer if it provided a generic approach instead of smart but too specific solution.
-1

I can also confirm bytepunk's answer here is functional.

Also, expending the concept with PHP 7:

switch ($interval->days)
{
    case 0:
        return '1 day';
        // break;
    case (($interval->days >= 1 && $interval->days <= 7) ?? $interval->days):
        return '1 week';
        // break;
    case (($interval->days >= 8 && $interval->days <= 31) ?? $interval->days):
        return '1 month';
        // break;
    case (($interval->days >= 31 && $interval->days <= 93) ?? $interval->days):
        return '2-3 months';
        // break;
    default:
        return '3+ months';
}

I will admit that this isn't the cleanest of code, so perhaps wrapping each case with a static-like pure function would neat things up a bit, and not forgetting to name each function (or create one generic function with parameters) to match the case. This will make it more readable.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.