← Lekcje
PHP · lekcja 05

Instrukcje warunkowe 2/2: switch, match i ?:

Krótsze zapisy tych samych decyzji

  1. switch … case … break … default i pułapka bez break
  2. match: nowsza, ścisła wersja z PHP 8
  3. Operator ? : i ?? dla brakującej wartości
  4. Ćwiczenie: plan dnia z adresu w menu.php
Rozgrzewka · 3 minuty

Rozgrzewka: if, else, elseif

$points = 80;
if ($points >= 90) { echo "A"; }
elseif ($points >= 70) { echo "B"; }
elseif ($points >= 50) { echo "C"; }
else { echo "D"; }
  1. Co wypisze ten kod i dlaczego nie „BC”?
  2. Co wypisze if ($points = 0) { echo "zero"; } else { echo "nie zero"; }?
  3. Które z tych wartości if uzna za fałsz: "0", "a", 0.0, [], " "?
Warunki · switch

Automat z guzikami: switch

switch ($day) case "pn":siłownia case "wt":nauka PHP case "sr":basen case "czw":kino default:wolne "wt" reszta
$day = "wt";
switch ($day) {
    case "pn":
        echo "Siłownia";
        break;
    case "wt":
        echo "Nauka PHP";   // to
        break;
    default:
        echo "Wolne";
}

Automat z guzikami: wciskacie guzik, wypada jedna rzecz. switch bierze jedną wartość i porównuje ją po kolei z każdym case. Gdy wartość pasuje do któregoś case, wykonuje się kod aż do break. Gdy nic nie pasuje, wykonuje się kod w default.

PHP Manual: switch · php.net/manual/en/control-structures.switch.php
Warunki · switch kontra elseif

Kiedy switch, kiedy elseif?

// to samo, co switch z poprzedniego slajdu (switch porównuje luźno, jak ==)
if ($day === "pn") {
    echo "Siłownia";
} elseif ($day === "wt") {
    echo "Nauka PHP";
} else {
    echo "Wolne";
}
// dwa dni, jeden kod: case pod case
case "sb":
case "nd":
    echo "Weekend";
    break;
SytuacjaBierzecie
Jedna zmienna, konkretne wartości („pn”, 404)switch
Zakresy (>= 70), różne zmienne, &&elseif
Dwa albo trzy przypadkikrótsze wygrywa

switch porównuje luźno, jak ==: case 1: złapie też tekst "1". Dane z adresu i formularza to zawsze tekst, więc przy liczbach o tym pamiętajcie.

PHP Manual: switch („switch/case does a loose comparison”, przykład z kilkoma case) · php.net/manual/en/control-structures.switch.php
Warunki · pułapka

Bez break: domino leci dalej

case 1 stoi case 2 case 3 default $n = 2 trafia tutaj bez break przewraca resztę
$n = 2;
switch ($n) {
    case 1: echo "jeden ";
    case 2: echo "dwa ";
    case 3: echo "trzy ";
    default: echo "inne";
}
// wypisze: dwa trzy inne

PHP znajduje pasujący case i od tego miejsca wykonuje wszystko w dół, aż trafi na break albo koniec klamry. To „przelot” (ang. fall-through). Nie jest błędem składni, więc nikt Was nie ostrzeże. Zasada: każdy case kończy się break, chyba że celowo sklejacie dwa przypadki jak „sb” i „nd”.

PHP Manual: switch („If you don't write a break statement at the end of a case's statement list, PHP will go on executing the statements of the following case”) · php.net/manual/en/control-structures.switch.php
Warunki · match (PHP 8)

match: switch bez pułapek

$code = 404;
$message = match ($code) {
    200, 201 => "OK",
    404      => "Nie znaleziono",
    default  => "Inny kod",
};
echo $message;   // Nie znaleziono

echo match ("1") {
    1       => "liczba 1",
    default => "coś innego",
};                // coś innego
switchmatch
Porównanieluźne ==ścisłe ===
Przelot bez breaktaknie ma break, nie ma przelotu
Zwraca wartośćnietak, przypisujecie do zmiennej
Nic nie pasujepomijabłąd UnhandledMatchError
Od wersjizawszePHP 8.0

Kilka wartości w jednej gałęzi rozdzielacie przecinkiem. default dopisujcie zawsze, żeby nie dostać błędu przy nieznanej wartości.

PHP Manual: match · php.net/manual/en/control-structures.match.php
Warunki · operator ? :

Jedna linia: warunek ? tak : nie

$age = 20;
// if … else w czterech liniach
if ($age >= 18) {
    $label = "dorosły";
} else {
    $label = "niepełnoletni";
}

// to samo w jednej
$label = $age >= 18 ? "dorosły" : "niepełnoletni";
echo $label;   // dorosły
  • Nazwa: operator warunkowy (ternary, trójargumentowy). Czytacie: jeśli $age >= 18, to „dorosły”, inaczej „niepełnoletni”.
  • Operator zwraca wartość: wstawiacie ją do zmiennej albo prosto do echo. if tego nie potrafi.
  • Do jednego prostego wyboru. Kilka gałęzi: wracacie do if albo match.

Zagnieżdżony ? : wewnątrz innego ? : bez nawiasów: PHP 8 tego nie przyjmie, zgłosi błąd i wskaże, gdzie dać nawiasy. Celowo, bo taki zapis mylił ludzi.

Ciekawostka z Wikipedii: skrócony zapis $a ?: $b zwie się w innych językach (Kotlin, Groovy) „operatorem Elvisa”, bo ?: obrócony na bok przypomina fryzurę Elvisa Presleya. W PHP działa od PHP 5.3.

PHP Manual: Comparison Operators, „Ternary Operator” (przykład #4; ostrzeżenie o zagnieżdżaniu przy przykładzie #5) · php.net/manual/en/language.operators.comparison.php · ciekawostka: Wikipedia, „Elvis operator”, en.wikipedia.org/wiki/Elvis_operator
Warunki · operator ??

Koło zapasowe: ??

$_GET["name"] wartość z adresu ?? $name jest: jedzie dalej "gość": gdy brak (null)
// adres: menu.php?name=Ola
$name = $_GET["name"] ?? "gość";
echo "Cześć, " . $name;   // Cześć, Ola

// adres: menu.php  (bez ?name=)
$name = $_GET["name"] ?? "gość";
echo "Cześć, " . $name;   // Cześć, gość

$_GET to tablica z danymi wpisanymi w adresie po znaku ?: ?name=Ola daje $_GET["name"] równe "Ola". Gdy w adresie nic nie ma, klucza nie ma, a ?? po cichu podstawia wartość z prawej. Bez ?? PHP wypisałoby ostrzeżenie „Undefined array key”. Pełna lekcja o formularzach później.

PHP Manual: Comparison Operators, „Null Coalescing Operator” (przykład #7) · php.net/manual/en/language.operators.comparison.php · $_GET · php.net/manual/en/reserved.variables.get.php
Egzamin INF.03

Tak pytają na egzaminie

Wskaż instrukcję równoważną do instrukcji switch zapisanej językiem PHP.

switch ($number) {
    case 10:
    case 20: $number++; break;
    default: $number = 0;
}
  1. Instrukcja 1.
  2. Instrukcja 2.
  3. Instrukcja 3.
  4. Instrukcja 4.
// Instrukcja 1.
if ($number==10)
  $number++;
else
  $number = 0;
// Instrukcja 3.
if ($number==10 or $number==20)
  $number++;
// Instrukcja 2.
if ($number==10 or $number==20)
  $number++;
else
  $number = 0;
// Instrukcja 4.
if ($number==10 and $number==20)
  $number++;
else
  $number = 0;

Dwa case pod sobą to przelot celowy: 10 wpada do kodu dla 20.

CKE, EE.09, czerwiec 2021, zad. 4
Ćwiczenie przy komputerze · 10 minut

Ćwiczenie 1: menu.php

  1. W folderze swojego projektu utwórz menu.php. Pierwsza linia: $day = $_GET["day"] ?? "brak";
  2. Napisz switch ($day) z planem dnia dla pn, wt, sr, czw, pt (jedno zdanie na dzień). sb i nd sklej w jeden „Weekend”.
  3. case "brak": Dodaj do adresu ?day=pn. default: Nie znam takiego dnia.
  4. Sprawdź w przeglądarce adresy z tabeli. Plik zostaw w folderze kursu.
Koniec adresuMa się pojawić
menu.php?day=wtTwój plan na wtorek
menu.php?day=ndWeekend
menu.phpDodaj do adresu ?day=pn
menu.php?day=xyzNie znam takiego dnia.

Początek adresu zawsze localhost/. Każdy case kończysz break, inaczej zobaczysz plan kilku dni naraz.

Dla szybkich: przepisz to samo na match z echo przed nim.

Cały plik
$day = $_GET["day"] ?? "brak";
switch ($day) {
    case 'pn':  echo "Siłownia."; break;
    case 'wt':  echo "Nauka PHP."; break;
    case 'sr':  echo "Basen."; break;
    case 'czw': echo "Kino."; break;
    case 'pt':  echo "Znajomi."; break;
    case 'sb':
    case 'nd':  echo "Weekend"; break;
    case "brak": echo "Dodaj do adresu ?day=pn"; break;
    default:   echo "Nie znam takiego dnia.";
}
// dla szybkich: to samo przez match (skrót)
echo match ($day) {
    'pn' => "Siłownia.", /* … */ 'sb', 'nd' => "Weekend",
    'brak' => "Dodaj do adresu ?day=pn",
    default => "Nie znam takiego dnia.",
};

Pominięte break w ćwiczeniu 1 objawia się planem kilku dni naraz. Kto wypisze $day w default, dostaje pytanie o ?day=<b>x</b>.

PHP Manual, control-structures.switch.php, language.operators.comparison.php (??)
Ćwiczenie na kartce · 5 minut

Ćwiczenie 2: co wypisze?

Bez komputera. Zapisz wynik każdego fragmentu, potem sprawdź uruchamiając kod.

// 1.
$k = 3;
switch ($k) {
    case 3: echo "C";
    case 4: echo "D"; break;
    case 5: echo "E";
}

Co wypisze ten kod?

// 2.
$m = "2";
switch ($m) {
    case 2: echo "dwa"; break;
    default: echo "inne";
}

Co wypisze ten kod?

// 3.
echo match ("2") {
    2       => "dwa",
    default => "inne",
};

Co wypisze ten kod?

// 4.
$t = 15;
echo $t > 10 ? "dużo" : "mało";

Co wypisze ten kod?

// 5.
$data = [];
echo $data['id'] ?? 0;

Co wypisze ten kod?

Fragmenty 2 i 3 wyglądają tak samo, a dają różne wyniki. Kto ma 5 na 5, tłumaczy sąsiadowi, dlaczego.

Na koniec

Sprawdź się

  1. Do czego służy break w switch i co się dzieje, gdy go zabraknie?
  2. Jakie dwie różnice dzielą match od switch?
  3. Co wypisze echo $_GET["page"] ?? 1;, gdy w adresie nie ma ?page=?

Następna lekcja: pętle. Ten sam kod wykonany wiele razy, bez kopiowania.

Poza szkołą

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
switch na tekstach i liczbachW nowym kodzie (PHP 8) pisze się match: ścisłe porównanie, bez break, zwraca wartość. switch zostaje w starszych projektach, które trzeba umieć czytać.switch: zastępowane
$_GET["day"] ?? "brak"Tak samo, tylko wartość sprawdza się z listą dozwolonych, a wypisuje się przez htmlspecialchars(). Bez tego ktoś wstrzyknie w stronę obcy skrypt (atak XSS).nadal w użyciu
? : do jednego wyboruJedna linia, jeden wybór. Zagnieżdżone ? : od PHP 8 wymagają nawiasów, a w zespołach są zakazane.zagnieżdżone: unikane
case bez breakNarzędzia sprawdzające kod (PHP_CodeSniffer) oznaczają przelot bez komentarza jako błąd. Celowy przelot dostaje komentarz // no break.bez komentarza: unikane
Kody „pn”, „wt” jako tekstStały zbiór wartości (dni, statusy zamówień) opisuje typ enum (PHP 8.1) w parze z match: literówka wychodzi od razu.nadal w użyciu
PHP Manual: match · control-structures.match.php · htmlspecialchars · php.net/manual/en/function.htmlspecialchars.php · Enumerations · php.net/manual/en/language.enumerations.php
← → slajdy · F pełny ekran · S spis