Best way to do a PHP switch with multiple values per case?

PhpSwitch Statement

Php Problem Overview


How would you do this PHP switch statement?

Also note that these are much smaller versions, the 1 I need to create will have a lot more values added to it.

Version 1:

switch ($p) { 
	case 'home': 
	case '': 
		$current_home = 'current';
	break; 
	
	case 'users.online': 
	case 'users.location': 
	case 'users.featured': 
	case 'users.new': 
	case 'users.browse': 
	case 'users.search': 
	case 'users.staff': 
		$current_users = 'current';
	break;
	 
	case 'forum': 
		$current_forum = 'current';
	break; 
} 

Version 2:

switch ($p) { 
	case 'home': 
		$current_home = 'current';
	break; 
	
	case 'users.online' || 'users.location' || 'users.featured' || 'users.browse' || 'users.search' || 'users.staff': 
		$current_users = 'current';
	break;
	
	case 'forum': 
		$current_forum = 'current';
	break; 
} 

UPDATE - Test Results

I ran some speed test on 10,000 iterations,

Time1: 0.0199389457703 // If statements
Time2: 0.0389049446106 //switch statements
Time3: 0.106977939606 // Arrays

Php Solutions


Solution 1 - Php

For any situation where you have an unknown string and you need to figure out which of a bunch of other strings it matches up to, the only solution which doesn't get slower as you add more items is to use an array, but have all the possible strings as keys. So your switch can be replaced with the following:

// used for $current_home = 'current';
$group1 = array(
        'home'  => True,
        );

// used for $current_users = 'current';
$group2 = array(
        'users.online'      => True,
        'users.location'    => True,
        'users.featured'    => True,
        'users.new'         => True,
        'users.browse'      => True,
        'users.search'      => True,
        'users.staff'       => True,
        );

// used for $current_forum = 'current';
$group3 = array(
        'forum'     => True,
        );

if(isset($group1[$p]))
    $current_home = 'current';
else if(isset($group2[$p]))
    $current_users = 'current';
else if(isset($group3[$p]))
    $current_forum = 'current';
else
    user_error("\$p is invalid", E_USER_ERROR);

This doesn't look as clean as a switch(), but it is the only fast solution which doesn't include writing a small library of functions and classes to keep it tidy. It is still very easy to add items to the arrays.

Solution 2 - Php

Version 2 does not work!!
case 'users.online' || 'users.location' || ...

is exactly the same as:

case True:

and that case will be chosen for any value of $p, unless $p is the empty string.

|| Does not have any special meaning inside a case statement, you are not comparing $p to each of those strings, you are just checking to see if it's not False.

Solution 3 - Php

Put those many values into an array and query the array, as the switch-case seems to hide the underlying semantics of what you're trying to achieve when a string variable is used as the condition, making it harder to read and understand, e.g.:

$current_home = null;
$current_users = null;
$current_forum = null;

$lotsOfStrings = array('users.online', 'users.location', 'users.featured', 'users.new');

if(empty($p)) {
    $current_home = 'current';
}

if(in_array($p,$lotsOfStrings)) {
    $current_users = 'current';
}

if(0 === strcmp('forum',$p)) {
    $current_forum = 'current';
}

Solution 4 - Php

For the sake of completeness, I'll point out that the broken "Version 2" logic can be replaced with a switch statement that works, and also make use of arrays for both speed and clarity, like so:

// used for $current_home = 'current';
$home_group = array(
    'home'  => True,
);

// used for $current_users = 'current';
$user_group = array(
    'users.online'      => True,
    'users.location'    => True,
    'users.featured'    => True,
    'users.new'         => True,
    'users.browse'      => True,
    'users.search'      => True,
    'users.staff'       => True,
);

// used for $current_forum = 'current';
$forum_group = array(
    'forum'     => True,
);

switch (true) {
    case isset($home_group[$p]):
        $current_home = 'current';
        break;
    case isset($user_group[$p]):
        $current_users = 'current';
        break;
    case isset($forum_group[$p]):
        $current_forum = 'current';
        break;
    default:
        user_error("\$p is invalid", E_USER_ERROR);
}    

Solution 5 - Php

Nowadays you can do...

switch ([$group1, $group2]){
    case ["users", "location"]:
    case ["users", "online"]:
        Ju_le_do_the_thing();
        break;
    case ["forum", $group2]:
        Foo_the_bar();
        break;
}

Here's a ready ready-to-run set of code demonstrating the concept:

<?php

function show_off_array_switch(){
  $first_array = ["users", "forum", "StubbornSoda"];
  $second_array = ["location", "online", "DownWithPepsiAndCoke"];

  $rand1 = array_rand($first_array);
  $rand2 = array_rand($second_array);

  $group1 = $first_array[$rand1];
  $group2 = $second_array[$rand2];

  switch ([$group1, $group2]){
      case ["users", "location"]:
      case ["users", "online"]:
          echo "Users and Online";
          break;
      case ["forum", $group2]:
          echo "Forum and variable";
          break;
      default:
          echo "default";
  }
  echo "\nThe above result was generated using the array: \n" . print_r([$group1, $group2], true);
}

for ($i = 0; $i < 10; $i++){
  show_off_array_switch();
}

The following has (for one random run) output the following:

 Users and Online
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => online
)
Users and Online
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => online
)
default
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => DownWithPepsiAndCoke
)
Users and Online
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => location
)
Forum and variable
The above result was generated using the array: 
Array
(
    [0] => forum
    [1] => DownWithPepsiAndCoke
)
Forum and variable
The above result was generated using the array: 
Array
(
    [0] => forum
    [1] => DownWithPepsiAndCoke
)
Forum and variable
The above result was generated using the array: 
Array
(
    [0] => forum
    [1] => online
)
default
The above result was generated using the array: 
Array
(
    [0] => StubbornSoda
    [1] => location
)
Users and Online
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => location
)
Users and Online
The above result was generated using the array: 
Array
(
    [0] => users
    [1] => location
)

Solution 6 - Php

If anyone else was ever to maintain your code, they would almost certainly do a double take on version 2 -- that's extremely non-standard.

I would stick with version 1. I'm of the school of though that case statements without a statement block of their own should have an explicit // fall through comment next to them to indicate it is indeed your intent to fall through, thereby removing any ambiguity of whether you were going to handle the cases differently and forgot or something.

Solution 7 - Php

Some other ideas not mentioned yet:

switch(true){ 
  case in_array($p, array('home', '')): 
    $current_home = 'current'; break;

  case preg_match('/^users\.(online|location|featured|new|browse|search|staff)$/', $p):
    $current_users = 'current'; break;

  case 'forum' == $p:
    $current_forum = 'current'; break; 
}

Someone will probably complain about readability issues with #2, but I would have no problem inheriting code like that.

Solution 8 - Php

Version 1 is certainly easier on the eyes, clearer as to your intentions, and easier to add case-conditions to.

I've never tried the second version. In many languages, this wouldn't even compile because each case labels has to evaluate to a constant-expression.

Solution 9 - Php

I definitely prefer Version 1. Version 2 may require less lines of code, but it will be extremely hard to read once you have a lot of values in there like you're predicting.

(Honestly, I didn't even know Version 2 was legal until now. I've never seen it done that way before.)

Solution 10 - Php

No version 2 doesn't actually work but if you want this kind of approach you can do the following (probably not the speediest, but arguably more intuitive):

switch (true) {
case ($var === 'something' || $var === 'something else'):
// do some stuff
break;
}

Solution 11 - Php

maybe

		switch ($variable) {
		case 0:
			exit;
			break;
		case (1 || 3 || 4 || 5 || 6):
			die(var_dump('expression'));
		default:
			die(var_dump('default'));
			# code...
			break;
	}

Solution 12 - Php

I think version 1 is the way to go. It is a lot easier to read and understand.

Solution 13 - Php

if( in_array( $test, $array1 ) )
{
    // do this
}
else if( stristr( $test, 'commonpart' ) )
{
    // do this
}
else
{
    switch( $test )
    {
        case 1:
            // do this
            break;
        case 2:
            // do this
            break;
        default:
            // do this
            break;
    }
}

Solution 14 - Php

Switch in combination with variable variables will give you more flexibility:

<?php
$p = 'home'; //For testing

$p = ( strpos($p, 'users') !== false? 'users': $p);
switch ($p) { 
    default:
        $varContainer = 'current_' . $p; //Stores the variable [$current_"xyORz"] into $varContainer
        ${$varContainer} = 'current'; //Sets the VALUE of [$current_"xyORz"] to 'current'
    break;

}
//For testing
echo $current_home;
?>

To learn more, checkout variable variables and the examples I submitted to php manual:
Example 1: http://www.php.net/manual/en/language.variables.variable.php#105293
Example 2: http://www.php.net/manual/en/language.variables.variable.php#105282

PS: This example code is SMALL AND SIMPLE, just the way I like it. It's tested and works too

Solution 15 - Php

You are simply looking for common patterns in strings. I would have thought a regular expression would be a more efficient way of doing this as PHP implements this with preg_match so little code to write and probably massively quicker. For example:

case preg_match('/^users./'):
// do something here
break;

Solution 16 - Php

From PHP documentation you can do that: https://www.php.net/manual/en/control-structures.switch.php#41767

$input = 'users.online';
$checked = null;
        
switch (true):
    case ($input === 'home'):
        $checked = 'ok 1';
        break;

    case (in_array($input , ['users.online', 'users.location', 'users.featured', 'users.browse', 'users.search', 'users.staff'])):
        $checked = 'ok 2';
        break;

    case ($input === 'forum'):
        $checked = 'ok 3';
        break;
endswitch;

echo $checked;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJasonDavisView Question on Stackoverflow
Solution 1 - Phptoo much phpView Answer on Stackoverflow
Solution 2 - Phptoo much phpView Answer on Stackoverflow
Solution 3 - Phpkarim79View Answer on Stackoverflow
Solution 4 - PhpPeterView Answer on Stackoverflow
Solution 5 - PhplilHarView Answer on Stackoverflow
Solution 6 - PhpMark RushakoffView Answer on Stackoverflow
Solution 7 - PhppguardiarioView Answer on Stackoverflow
Solution 8 - PhpRobert CartainoView Answer on Stackoverflow
Solution 9 - PhpJosh LeitzelView Answer on Stackoverflow
Solution 10 - PhptomfumbView Answer on Stackoverflow
Solution 11 - PhpTimenty OgurtsovView Answer on Stackoverflow
Solution 12 - PhpJosh CurrenView Answer on Stackoverflow
Solution 13 - PhpAnOldManView Answer on Stackoverflow
Solution 14 - PhpOmarView Answer on Stackoverflow
Solution 15 - Phpuser5688936View Answer on Stackoverflow
Solution 16 - PhpOussama ANDALOUSSIView Answer on Stackoverflow