Я просто написал ужасную функцию PHP, мне нужна помощь (elseif chain - switch?)

Я создаю сайт, который определяет значение массива в зависимости от времени. Я написал этот ужасный (функциональный) script, и мне интересно, могу ли я сделать его более кратким. Я начал с оператора case/switch, но не смог получить несколько условных выражений, работающих с ним. Здесь грязное дело:

if ($now < november 18th) {
    $array_to_use = $home;
}
elseif (november 18th < $now && $now < november 21st ) {
    $array_to_use = $driving;
}
elseif (november 21st < $now && $now < november 22nd) {
    $array_to_use = $flying;
}
...
...
...
elseif (february 1st < $now) {
    $array_to_use = $arrived;
}
else {
    $array_to_use = $default;
}

Расписание на самом деле сложнее и содержит 13 elseif операторов. Может кто-то, пожалуйста, подтвердите, что у меня только что был блок кодера и что есть лучший способ сделать это?

EDIT: Я изменил временные метки Unix на грубые реальные времена, чтобы было легче понять, что я делаю (надеюсь)

РЕДАКТИРОВАТЬ 2: Пожалуйста, простите, что в настоящее время неработающие часы Javascript, но это сайт, над которым я работаю:

Таблица времени.

Каждый массив основан на моем местоположении, и есть 15 "они в настоящее время" основаны на времени. Это небольшая проблемная область с известными временами начала и окончания, поэтому гибкость не является ключевой, просто получая ее все написанное. Вы можете видеть, как время непрерывное, и только один массив строк должен быть выбран за раз.

Ответ 1

Сначала, пожалуйста, пожалуйста, выньте свои жестко закодированные цифры и поместите их в константы.

$FLIGHT_START_TIME = 1258956001;
$FLIGHT_END_TIME   = 1260511201;

Во-вторых, я бы сделал мини-функции для каждого из условных выражений:

т.е.

function isFlying($time)
{
    return ( $FLIGHT_START_TIME < $time && $time < $FLIGHT_END_TIME );
}

В-третьих, возьмите весь набор условных выражений и поместите его в функцию, чтобы получить текущее состояние и замените в своих вызовах функции:

function getStateArrayForTime($time)
{

   if (isDriving($time)
   {
       return $driving;
   }
   if ( isFlying($time) )
   {
        return $flying;
   }
...etc
}

Наконец, замените весь встроенный раздел кода на один вызов функции:

$currentState = getStateArrayForTime($now);

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

поэтому замените реализацию getStateArrayForTime на:

function getStateArrayForTime ($time)
{
// 
$states = array (
    array("startTime" => 1258956001, "endTime" => 1260511201, "state" => $flying),
    array("startTime" => 1260511201, "endTime" => 1260517000, "state" => $driving),
..etc...
);
    foreach($states as $checkStateArray)
    {
        if($checkStateArray['startTime'] < $time && $time < $checkStateArray['endTime'])
        {
            return $checkStateArray['state'];
        }
    }
    return null;
}

Наконец, некоторые люди могут спросить: "Почему дела в этом порядке?" Я не могу претендовать на кредит вообще, кроме приложения, но у Мартина Фаулера есть замечательная книга под названием "Рефакторинг", которая объясняет, почему вы очищаете код за один шаг за раз и проверяете на каждом этапе пути, а затем, наконец, замените оптовые функции, которые не имеют смысла, все время проверяя, что они функционально эквивалентны.

Ответ 2

Это может быть излишним, но я бы сделал что-то подобное, чтобы я мог использовать все диапазоны времени в одном ясном месте:

@timeWindows = ({ start -> 0, end -> 1258783201, array -> $home },
                ... ,
                {start -> 1260511201, end -> MAXVAL, array -> $arrived});

а затем цикл, подобный

$array_to_use = $default;

foreach (my $window in @timeWindows) {
   if (($now > $window->start) && ($now < $window->end)) {
       $array_to_use = $window->array;
       last;
   }
}

Извините, что в Perl я не знаю PHP, но я думаю, что это похоже.

Ответ 3

Вы можете поместить время и массив для использования в массиве и их цикл для выбора.

$Selctions = array(
    1258783201 => $Home,
    1258956001 => $Driving,
    1260511201 => $Flying,
    ...
    1260511201 => $Arriving
);

// MUST SORT so that the checking will not skip
ksort($Selction);
$TimeToUse = -1;
$Now       = ...;
foreach ($Selctions as $Time => $Array) {
    if ($Now < $Time) {
        $TimeToUse = $Time;
        break;
    }
}
$ArrayToUse = ($TimeToUse != -1) ? $Selctions[$TimeToUse] : $Default;

Этот метод может использоваться только тогда, когда время не имеет пробела (один диапазон справа от другого).

Надеюсь, что это поможет.

Ответ 4

Вы можете использовать оператор switch, делая что-то вроде этого:

switch (true)
{
    case $now < 1258783201:
        // your stuff
        break;
    case $now < 1258783201
        // more of your stuff
        break;
    //...
}

Это по крайней мере немного чище...

Ответ 5

Что-то вроде этого:

$array_to_use = null;
$dispatch = array(1258783201, $home, 1258956001, $driving, ..., $arrived);
for ($i=0; i<count($dispatch); $i+=2) {
    if ($now<$dispatch[$i]) {
        $array_to_use = $dispatch[$i+1];
        break;
    }
}
if ($array_to_use==null) $array_to_use = $dispatch[count($dispatch)-1];

Вам также нужно подумать, нужно ли вам "<". или "< =".

Ответ 6

Возможно, вам захочется изучить Command Pattern; это также может помочь в этом обстоятельстве.