Лучший способ сделать переключение PHP с несколькими значениями для каждого случая? - PullRequest
66 голосов
/ 21 августа 2009

Как бы вы сделали этот оператор PHP switch?

Также обратите внимание, что это гораздо меньшие версии, к 1, который мне нужно создать, будет добавлено гораздо больше значений.

Версия 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; 
} 

Версия 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; 
} 

ОБНОВЛЕНИЕ - Результаты испытаний

Я провел тест скорости на 10 000 итераций,

Time1: 0.0199389457703 // If-заявления
Time2: 0.0389049446106 // переключение операторов
Time3: 0.106977939606 // Массивы

Ответы [ 14 ]

50 голосов
/ 21 августа 2009

Для любой ситуации, когда у вас есть неизвестная строка, и вам нужно выяснить, с какой из других строк она соответствует, единственное решение, которое не замедляется, когда вы добавляете больше элементов использовать массив, но иметь все возможные строки в качестве ключей. Таким образом, ваш переключатель может быть заменен следующим:

// 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);

Это выглядит не так чисто, как switch(), но это единственное быстрое решение, которое не включает в себя написание небольшой библиотеки функций и классов для поддержания чистоты. Все еще очень легко добавлять элементы в массивы.

25 голосов
/ 21 августа 2009

Версия 2 не работает !!

case 'users.online' || 'users.location' || ...

точно так же, как:

case True:

и что case будет выбрано для любого значения $p, если $p не является пустой строкой.

|| Не имеет никакого особого значения внутри оператора case, вы не сравниваете $p с каждой из этих строк, вы просто проверяете, не является ли оно False.

8 голосов
/ 21 августа 2009

Поместите эти многочисленные значения в массив и выполните запрос к массиву, так как регистр переключателя, кажется, скрывает основную семантику того, чего вы пытаетесь достичь, когда в качестве условия используется строковая переменная, делая его более трудным для чтения и понять, например:

$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';
}
4 голосов
/ 21 февраля 2014

Некоторые другие идеи, еще не упомянутые:

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; 
}

Кто-то, вероятно, будет жаловаться на проблемы с читабельностью # 2, но у меня не будет проблем с наследованием такого кода.

4 голосов
/ 10 октября 2012

Для полноты картины я укажу, что нарушенная логика "Версия 2" может быть заменена оператором switch, который работает, а также использует массивы для скорости и ясности вот так:

// 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);
}    
4 голосов
/ 21 августа 2009

Если бы кто-нибудь еще когда-либо поддерживал ваш код, они почти наверняка сделали бы двойную версию 2 - это крайне нестандартно.

Я бы придерживался версии 1. Я из школы, хотя в этом случае заявления без собственного блока операторов должны иметь явный комментарий // fall through рядом с ними, чтобы указать, что это действительно ваше намерение провалиться, тем самым устраняя любую двусмысленность того, собирались ли вы по-другому вести дела и забыли или что-то в этом роде.

3 голосов
/ 21 августа 2009

Версия 1, безусловно, проще для глаз, более ясна в отношении ваших намерений, и ее легче добавлять в кейс-условия.

Я никогда не пробовал вторую версию. Во многих языках это даже не скомпилируется, потому что метки каждого случая должны соответствовать константному выражению.

1 голос
/ 29 апреля 2011

Никакая версия 2 на самом деле не работает, но если вам нужен такой подход, вы можете сделать следующее (возможно, не самое быстрое, но, возможно, более интуитивное):

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

1 голос
/ 21 августа 2009

Я определенно предпочитаю версию 1. Версия 2 может потребовать меньше строк кода, но ее будет чрезвычайно трудно читать, когда вы получите много значений, как вы и предполагали.

(Честно говоря, я даже не знал, что Версия 2 до сих пор была законной. Я никогда раньше не видел, чтобы это делалось так.)

0 голосов
/ 29 марта 2019

возможно

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