Best way to do a PHP switch with multiple values per case?
PhpSwitch StatementPhp 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;