switch w PHP - case, break, default i praktyczne przykłady

switch w PHP - instrukcja case, break i default na przykładzie koduInstrukcja switch w PHP służy do wyboru jednego z kilku wariantów działania na podstawie wartości tego samego wyrażenia. Najczęściej zastępuje rozbudowany ciąg if i elseif, gdy program porównuje np. status zamówienia, rolę użytkownika, kod operacji albo wybraną opcję z kilkoma konkretnymi wartościami. Sama składnia switch - case - break jest prosta, ale jej zachowanie ma kilka istotnych szczegółów. PHP używa w switch luźnego porównania, pominięcie break powoduje przejście do kolejnego przypadku, a continue wewnątrz switch nie działa tak, jak można oczekiwać po innych językach programowania.

W praktyce warto również wiedzieć, kiedy użyć klasycznego switch, kiedy zwykłego if/elseif, a kiedy w nowszym PHP lepszym rozwiązaniem będzie wyrażenie match. Poniższe przykłady pokazują zarówno typowe zastosowania, jak i przypadki, które mogą prowadzić do trudnych do zauważenia błędów.

Jak działa switch w PHP?

Podstawowa konstrukcja switch składa się z wyrażenia podawanego w nawiasach oraz kolejnych przypadków oznaczonych przez case.

$status = "paid";

switch ($status) {
case "new":
echo "Nowe zamówienie";
break;

```
case "paid":
    echo "Zamówienie opłacone";
    break;

case "sent":
    echo "Zamówienie wysłane";
    break;
```

}

PHP sprawdza kolejne przypadki i szuka wartości odpowiadającej wartości wyrażenia $status. W tym przykładzie wykonany zostanie drugi blok:

Zamówienie opłacone

Typowa składnia wygląda więc następująco:

switch ($value) {
    case $option1:
        // kod
        break;

```
case $option2:
    // kod
    break;
```

}

Konstrukcja jest szczególnie czytelna wtedy, gdy jedną wartość porównujemy z wieloma konkretnymi możliwościami.

case - definiowanie możliwych wartości

Każdy case określa wartość, dla której ma zostać wykonany znajdujący się pod nim kod.

Przykładowo dla numeru dnia tygodnia:

$day = 3;

switch ($day) {
case 1:
echo "Poniedziałek";
break;

```
case 2:
    echo "Wtorek";
    break;

case 3:
    echo "Środa";
    break;

case 4:
    echo "Czwartek";
    break;

case 5:
    echo "Piątek";
    break;
```

}

Wynikiem będzie:

Środa

Wartość przy case nie musi być liczbą. Bardzo często są to ciągi znaków, stałe albo przypadki oparte na enumach.

Po co jest break w switch?

break kończy wykonywanie aktualnej konstrukcji switch. Jego znaczenie najlepiej widać po celowym usunięciu go z przykładu.

$role = "editor";

switch ($role) {
case "editor":
echo "Panel redaktora" . PHP_EOL;

```
case "user":
    echo "Panel użytkownika" . PHP_EOL;

case "guest":
    echo "Widok gościa";
```

}

Po dopasowaniu editor PHP nie zakończy automatycznie instrukcji. Będzie wykonywać kolejne bloki aż do napotkania break albo końca całego switch.

W tym przypadku otrzymamy więc:

Panel redaktora
Panel użytkownika
Widok gościa

Jeżeli każdy przypadek ma działać niezależnie, poprawna wersja wymaga break:

switch ($role) {
    case "editor":
        echo "Panel redaktora";
        break;

```
case "user":
    echo "Panel użytkownika";
    break;

case "guest":
    echo "Widok gościa";
    break;
```

}

Brak break jest jednym z najczęstszych źródeł błędów w klasycznym switch.

default - przypadek domyślny

default odpowiada mniej więcej końcowemu else. Wykonywany jest wtedy, gdy żaden case nie pasuje do sprawdzanej wartości.

$status = "unknown";

switch ($status) {
case "active":
echo "Konto aktywne";
break;

```
case "blocked":
    echo "Konto zablokowane";
    break;

default:
    echo "Nieznany status konta";
```

}

Wynik:

Nieznany status konta

default nie jest obowiązkowy. W kodzie produkcyjnym często jednak warto go mieć, ponieważ pozwala jawnie zdecydować, co ma się wydarzyć po otrzymaniu nieoczekiwanej wartości.

W zależności od charakteru aplikacji przypadek domyślny może nie wyświetlać tekstu, lecz np. zgłaszać wyjątek:

default:
    throw new InvalidArgumentException(
        "Nieobsługiwany status: " . $status
    );

Takie rozwiązanie może być bezpieczniejsze niż ciche pomijanie wartości, której programista nie przewidział.

Kilka case dla tego samego kodu

Kilka wartości może prowadzić do tego samego bloku instrukcji. Wystarczy umieścić kolejne case jeden pod drugim.

$day = "sobota";

switch ($day) {
case "sobota":
case "niedziela":
echo "Weekend";
break;

```
case "poniedziałek":
case "wtorek":
case "środa":
case "czwartek":
case "piątek":
    echo "Dzień roboczy";
    break;
```

}

Dla sobota i niedziela wykonany zostanie ten sam kod.

Ten zapis wykorzystuje mechanizm przechodzenia pomiędzy przypadkami, ale robi to celowo. Pierwszy case nie zawiera żadnych instrukcji, więc następny reprezentuje wspólny blok dla obu wartości.

Podobny schemat dobrze sprawdza się np. dla kilku kodów HTTP:

switch ($statusCode) {
    case 200:
    case 201:
    case 204:
        echo "Żądanie zakończone powodzeniem";
        break;

```
case 400:
case 404:
    echo "Błąd żądania";
    break;

case 500:
case 503:
    echo "Błąd serwera";
    break;
```

}

Fall-through - celowe przechodzenie między case

Brak break nie zawsze jest błędem. Mechanizm określany jako fall-through można wykorzystać świadomie, jeśli wykonanie jednego przypadku ma przejść również przez kod następnego.

$plan = "premium";

switch ($plan) {
case "premium":
echo "Raporty zaawansowane" . PHP_EOL;

```
case "standard":
    echo "Eksport danych" . PHP_EOL;

case "basic":
    echo "Podstawowy panel" . PHP_EOL;
    break;
```

}

Dla planu premium zostaną wykonane wszystkie trzy fragmenty. Można w ten sposób modelować funkcje, które zawierają możliwości niższych poziomów.

Technicznie rozwiązanie jest poprawne, ale warto stosować je oszczędnie. Czytelnik kodu bardzo łatwo może uznać brak break za przypadkowy błąd. Jeśli fall-through jest zamierzony, powinno to wynikać z konstrukcji kodu albo zostać jasno opisane komentarzem.

switch z wartościami tekstowymi

Jednym z najczęstszych praktycznych zastosowań switch jest obsługa tekstowych nazw operacji.

$action = "delete";

switch ($action) {
case "create":
echo "Tworzenie rekordu";
break;

```
case "update":
    echo "Aktualizacja rekordu";
    break;

case "delete":
    echo "Usuwanie rekordu";
    break;

default:
    echo "Nieznana operacja";
```

}

Taki zapis jest zwykle czytelniejszy niż długa konstrukcja:

if ($action == "create") {
    // ...
} elseif ($action == "update") {
    // ...
} elseif ($action == "delete") {
    // ...
}

W obu przypadkach logika może być poprawna, ale switch mocniej podkreśla, że cały wybór zależy od jednej zmiennej.

Pułapka: switch używa luźnego porównania

To jeden z najważniejszych szczegółów switch w PHP. Dopasowanie przypadków nie korzysta z operatora identyczności ===. Reguły odpowiadają luźnemu porównaniu ==.

Dlatego liczba i odpowiadający jej numeric string mogą zostać uznane za równe:

$value = "10";

switch ($value) {
case 10:
echo "Dopasowano liczbę 10";
break;

```
default:
    echo "Brak dopasowania";
```

}

Przypadek 10 zostanie dopasowany mimo tego, że $value jest stringiem.

Jeszcze bardziej zdradliwy może być przykład z booleanami:

$value = 0;

switch ($value) {
case false:
echo "false";
break;

```
case 0:
    echo "zero";
    break;
```

}

Pierwszy przypadek może zostać dopasowany już przy case false, ponieważ porównanie jest luźne. Kolejność przypadków zaczyna więc wpływać na wynik w sposób, którego nie widać na pierwszy rzut oka.

Jeżeli typ wartości ma znaczenie dla logiki programu, klasyczny switch może nie być najlepszym wyborem. W PHP 8 alternatywą jest match, który wykorzystuje ścisłe porównanie ===.

Warto też pamiętać, że reguły luźnych porównań uległy zmianom w PHP 8. Szczególnie porównania liczb z nienumerycznymi ciągami znaków zachowują się inaczej niż w PHP 7. Starszy kod oparty na nietypowych konwersjach typów należy więc testować po migracji.

switch(true) - warunki i zakresy

Czasami można spotkać konstrukcję switch (true). Pozwala ona używać w case całych warunków zamiast konkretnych wartości.

Przykładowo możemy przypisać ocenę do wyniku testu:

$score = 82;

switch (true) {
case $score >= 90:
echo "Ocena bardzo dobra";
break;

```
case $score >= 75:
    echo "Ocena dobra";
    break;

case $score >= 50:
    echo "Ocena dostateczna";
    break;

default:
    echo "Brak zaliczenia";
```

}

PHP porównuje tutaj wartość true z rezultatem kolejnych warunków. Pierwszy warunek zwracający wartość pasującą do true uruchamia odpowiadający mu blok.

Kolejność ma ogromne znaczenie. Najbardziej restrykcyjne progi powinny znajdować się na początku. Gdyby $score >= 50 umieścić jako pierwszy przypadek, wynik 95 zostałby zakwalifikowany już do tego bloku.

Technika switch(true) działa, ale nie zawsze jest najbardziej czytelna. Dla klasycznych zakresów często prostszy będzie zwykły zapis:

if ($score >= 90) {
    echo "Ocena bardzo dobra";
} elseif ($score >= 75) {
    echo "Ocena dobra";
} elseif ($score >= 50) {
    echo "Ocena dostateczna";
} else {
    echo "Brak zaliczenia";
}

Jeżeli warunki przestają być prostym wyborem jednej wartości, przewaga switch nad if/elseif często znika.

switch w funkcji i return zamiast break

Jeżeli switch znajduje się wewnątrz funkcji i każdy przypadek od razu zwraca wynik, break nie jest potrzebny.

function getStatusLabel(string $status): string
{
    switch ($status) {
        case "new":
            return "Nowe";

```
    case "paid":
        return "Opłacone";

    case "sent":
        return "Wysłane";

    default:
        return "Nieznane";
}
```

}

return kończy działanie całej funkcji, więc wykonanie nie może przejść do następnego case.

Przykładowe użycie:

echo getStatusLabel("paid");

zwróci:

Opłacone

Ten przypadek jest również dobrym kandydatem do zastąpienia przez match, ponieważ cała konstrukcja służy wyłącznie do wybrania wartości wynikowej.

switch w pętli - break 2 i continue 2

Bardziej wymagające zachowanie pojawia się, gdy switch znajduje się wewnątrz pętli.

$statuses = ["new", "skip", "paid"];

foreach ($statuses as $status) {
switch ($status) {
case "skip":
break;

```
    case "new":
        echo "Nowe" . PHP_EOL;
        break;

    case "paid":
        echo "Opłacone" . PHP_EOL;
        break;
}
```

}

Zwykłe break opuszcza jedynie najbliższy switch. Pętla foreach będzie kontynuowana.

PHP pozwala podać liczbę poziomów, które mają zostać opuszczone:

break 2;

Jeżeli switch znajduje się bezpośrednio wewnątrz pętli, break 2 zakończy zarówno switch, jak i pętlę.

Szczególnej uwagi wymaga natomiast continue. W PHP samodzielne:

continue;

skierowane do konstrukcji switch zachowuje się podobnie jak break i generuje ostrzeżenie, ponieważ bardzo często oznacza pomyłkę programisty.

Jeżeli zamiarem jest pominięcie pozostałej części aktualnej iteracji zewnętrznej pętli, należy użyć:

continue 2;

Przykład:

$statuses = ["new", "skip", "paid"];

foreach ($statuses as $status) {
switch ($status) {
case "skip":
continue 2;
}

```
echo "Przetwarzam: " . $status . PHP_EOL;
```

}

Dla wartości skip program przejdzie bezpośrednio do następnej iteracji foreach.

switch czy if/elseif?

Obie konstrukcje mogą często rozwiązać ten sam problem, ale dobrze sprawdzają się w innych sytuacjach.

Przypadek switch if/elseif
Jedna zmienna, wiele konkretnych wartości Bardzo czytelny Poprawny
Wiele niezależnych warunków Mało naturalny Lepszy
Zakresy liczbowe Możliwe przez switch(true) Zwykle czytelniejszy
Porównania złożone Mało wygodny Lepszy
Celowe fall-through Obsługiwane Wymaga innej konstrukcji

Dobrym kandydatem do switch jest więc:

$status === "new"
$status === "paid"
$status === "sent"
$status === "cancelled"

Natomiast zestaw:

$age >= 18
$user->isActive()
$country === "PL"
$balance > 0

to kilka różnych warunków i zwykle znacznie naturalniej będzie zapisać je przez if oraz elseif.

switch czy match w PHP 8?

Od PHP 8.0 dostępne jest wyrażenie match. Na pierwszy rzut oka przypomina switch, ale różnice są fundamentalne.

Klasyczny kod:

$status = "paid";

switch ($status) {
case "new":
$label = "Nowe";
break;

```
case "paid":
    $label = "Opłacone";
    break;

case "sent":
    $label = "Wysłane";
    break;

default:
    $label = "Nieznane";
```

}

można zapisać przez match:

$label = match ($status) {
    "new" => "Nowe",
    "paid" => "Opłacone",
    "sent" => "Wysłane",
    default => "Nieznane",
};

Najważniejsze różnice:

Cecha switch match
Rodzaj konstrukcji instrukcja wyrażenie zwracające wartość
Porównanie == ===
break często wymagany niepotrzebny
Fall-through tak nie
Przypisanie wyniku pośrednio bezpośrednio
Brak dopasowania bez default brak wykonania przypadku UnhandledMatchError

Różnicę w porównaniu typów można zobaczyć bardzo łatwo.

switch:

$value = "10";

switch ($value) {
case 10:
echo "Dopasowano";
break;
}

może dopasować string "10" do liczby 10.

Natomiast:

$value = "10";

$result = match ($value) {
10 => "Liczba",
"10" => "String",
};

rozróżni obie wartości, ponieważ match stosuje ścisłe porównanie typu i wartości.

Nie oznacza to, że switch stał się przestarzały. Nadal przydaje się, gdy potrzebujemy wieloinstrukcyjnych bloków, świadomego fall-through albo pracujemy z istniejącym API i kodem wykorzystującym tę konstrukcję. Jeśli jednak zadaniem jest przede wszystkim zamiana jednej wartości na inną, match w PHP 8 jest często krótszy i mniej podatny na pomyłki.

Najczęstsze błędy w switch

Brak break

Najbardziej klasyczny błąd:

switch ($status) {
    case "new":
        echo "Nowe";

```
case "paid":
    echo "Opłacone";
```

}

Po dopasowaniu new zostanie wykonany również kod kolejnego przypadku.

Założenie, że switch używa ===

Nie używa. Porównania są luźne, dlatego różne typy mogą zostać uznane za równe.

$value = "5";

switch ($value) {
case 5:
echo "Dopasowanie";
break;
}

Jeżeli rozróżnienie typu jest ważne, trzeba wybrać inną konstrukcję lub zastosować match.

Używanie continue zamiast continue 2

Przy switch umieszczonym w pętli sam continue nie oznacza automatycznie „przejdź do następnej iteracji pętli”. Do tego celu służy continue 2.

Rozbudowane switch(true)

Jeżeli kolejne case zawierają zupełnie niezależne i skomplikowane wyrażenia, kod przestaje korzystać z największej zalety switch - czytelnego wyboru spośród kilku wartości tego samego wyrażenia.

Zbyt duży switch

Konstrukcja mająca kilkadziesiąt przypadków, a w każdym po kilkadziesiąt linii kodu, może sygnalizować problem z organizacją programu. Nie jest to wada samej instrukcji switch. Często oznacza po prostu, że poszczególne operacje powinny zostać rozdzielone na funkcje, metody albo osobne klasy.

Zamiast:

switch ($action) {
    case "create":
        // kilkadziesiąt linii
        break;

```
case "update":
    // kilkadziesiąt linii
    break;

case "delete":
    // kilkadziesiąt linii
    break;
```

}

czytelniejsze może być:

switch ($action) {
    case "create":
        createRecord();
        break;

```
case "update":
    updateRecord();
    break;

case "delete":
    deleteRecord();
    break;
```

}

switch najlepiej sprawdza się wtedy, gdy jego rola jest oczywista: jedna wartość trafia do jednego z kilku jasno określonych przypadków. W PHP trzeba jednak pamiętać o luźnym porównaniu, działaniu break i mechanizmie fall-through. W nowym kodzie warto dodatkowo porównywać tę konstrukcję z match. Jeśli potrzebujemy po prostu przypisać wynik na podstawie wartości i zależy nam na ścisłym porównaniu typów, match często będzie bezpieczniejszy. Jeśli natomiast każdy przypadek zawiera własny blok instrukcji albo świadomie korzystamy z przechodzenia między przypadkami, klasyczny switch nadal pozostaje użytecznym elementem PHP.

Komentarze