Základní kurz 10: Podmínky

Autor: Joker
V rámci základního kurzu jsme už mluvili o výrazech, proměnných a operátorech. Naučili jsme se hodnoty proměnných používat ve výpočtech, předávat je jako argumenty do funkcí a výsledky výpočtů a funkcí si uložit do proměnných anebo je vypsat. Chybí ještě jedna důležitá věc, ke které se právě dostáváme: Jak ovlivnit chod samotného skriptu v závislosti na hodnotě nějaké proměnné nebo výsledku nějakého výrazu.

Ukážeme si, jak provést či neprovést některou část skriptu v závislosti na hodnotě proměnné nebo výsledku výpočtu. A konečně poodkryjeme význam datového typu boolean (logická hodnota).

Zápis podmínky v PHP

Podmínka začíná klíčovým slovem if, následuje v závorkách výraz nebo hodnota určující samotnou podmínku a pak kód, který se provede v případě splnění podmínky. Očekává se, že hodnota nebo výsledek výrazu určující splnění podmínky bude typu boolean (logická hodnota). Pokud je jiného typu, přetypuje se (podle pravidel, o kterých jsme mluvili v kapitole o přetypování). Hodnota true se považuje za splnění podmínky, hodnota false za nesplnění.

Příklad:

<?php
$podminka = true;
if ($podminka) {
  echo "Podmínka byla splněna";
}
?>

Zkuste si uvedený kód uložit do kořene webu třeba jako soubor priklad_podminka.php a otevřít v prohlížeči ( http://localhost/priklad_podminka.php ). Mělo by se vypsat "Podmínka byla splněna". Pak zkuste hodnotu $podminka nastavit na false místo true. Nemělo by se vypsat nic.

Jestliže je v bloku jediný příkaz, tak jako v příkladu výše, není povinné ho uzavírat do složených závorek. Když tedy složené závorky vynecháte, patří k podmínce jen první následující příkaz. Dávejte v takovém případě pozor na formátování kódu, protože při nevhodném formátování lze vytvořit matoucí kód:

<?php
// ukázka nevhodného formátování
$podminka = false;
if ($podminka)
echo "Něco "; // toto závisí na splnění podmínky
echo "Něco jiného "; // toto už k podmínce nepatří!
?>

Vhodnější formátování kódu v takovém případě je:
<?php
if ($podminka) {
  echo "Něco ";
}
echo "Něco jiného ";
?>

S tím souvisí i další věc: Za definici podmínky nikdy nedávejte středník! Protože za definicí podmínky středník být nemá a zároveň středník ukončuje příkaz, vezme se jako prázdný příkaz a zároveň ukončí podmínku. Tato chyba je záludná, protože takový kód je sice logicky chybně, ale syntakticky správně a žádnou chybu nenahlásí.
Ukázka:
<?php
$podminka = false;
// Středník na konci podmínky se vezme jako prázdný příkaz a ukončí podmínku
if ($podminka);
  // Nevznikne žádná chyba, ale následující text se vypíše vždy:
  echo "Podmínka byla splněna";
?>

Vykonání kódu jen při nesplnění podmínky

Za blokem if může následovat klíčové slovo else a za ním kód, který se provede v případě, že podmínka naopak splněna není. V případě splnění podmínky se tedy provede blok if, v případě nesplnění blok else, je-li uveden.
Příklad:

<?php
$text = "Ahoj!";
if (empty($text)) {
  echo "Text je prázdný";
}
else {
  echo "Text není prázdný";
}
?>

Opět můžete uvedený kód vyzkoušet tak jak je v příkladu a s prázdným řetězcem v $text. Připomeňme funkci empty, která vrátí true v případě, že proměnná je prázdná nebo není nastavena a jinak false.

Můžeme také vylepšit kód pro vložení souboru, který jsme si ukázali v kapitole o základech syntaxe.
Připomeňme si kód druhy.php:

<?php
// vloží soubor prvni.php
include "prvni.php";
?>

V případě, že soubor prvni.php nebude existovat, generuje tento kód varování (warning). Řekněme, že bychom chtěli místo toho vypsat do stránky nějaké srozumitelné chybové hlášení.
Použijeme k tomu funkci is_file, které se jako argument předává název souboru a funkce vrátí true pokud soubor toho jména existuje a false pokud neexistuje.
<?php
// vloží soubor prvni.php, pokud existuje
if (is_file("prvni.php")) {
  include "prvni.php";
} else {
  echo "Nepodařilo se najít pomocný soubor pro stránku!";
}
?>

Podmíněný operátor

Poměrně častá je specifická situace, kdy chceme do proměnné nastavit jednu ze dvou hodnot podle toho, jestli nějaká podmínka byla či nebyla splněna. V takových případech lze podmínku if-else nahradit takzvaným podmíněným operátorem. Tvoří ho otazník a dvojtečka ?: a jako jediný operátor v PHP má tři operandy: Nejdřív je podmínka, kterou chceme testovat. Následuje otazník a hodnota operátoru v případě, že podmínka je splněna (vrátí true). Pak dvojtečka a hodnota v případě, že podmínka splněna není (vrátí false). Například když budeme chtít vypsat uživateli hlášku o existenci určitého souboru:

<?php
$soubor = "prvni.php";
if (is_file($soubor)) {
  $hlaska = "Soubor je dostupný";
}
else {
  $hlaska = "Soubor nenalezen";
}
echo "Stav souboru $soubor: $hlaska";
?>

Stejný skript lze napsat s použitím podmíněného operátoru:
<?php
$soubor = "prvni.php";
$hlaska = is_file($soubor) ? "Soubor je dostupný" : "Soubor nenalezen";
echo "Stav souboru $soubor: $hlaska";
?>

Nebo i bez použití proměnné $hlaska:
<?php
$soubor = "prvni.php";
echo "Stav souboru $soubor: " . (is_file($soubor) ? "Soubor je dostupný" : "Soubor nenalezen");
?>
(Všimněte si použití operátoru pro spojování řetězců a podmíněného operátoru pro vrácení správné hlášky.)

V minulé kapitole o polích jsme si také ukázali předání dat od uživatele na server v poli $_GET. Podmíněný operátor můžeme využít pro nastavení výchozí hodnoty v případě, že hodnota od uživatele není vyplněna:

<?php
    // vložení hlavičky stránky, kterou jsme vytvořili ve 4. kapitole
    include "spolecna_hlavicka.php";

    $hodnota = empty($_GET["cislo"]) ? intval($_GET["cislo"]) : 0;
?>

Kód je kratší a pro pokročilejšího programátora i přehlednější. Pro začátečníka ale může naopak být složitější na pochopení. Komu to připadá moc složité, může použít klasické if-else, výsledek bude stejný. Naopak všechna if-else podmíněným operátorem nenahrazujte, podmíněný operátor je vhodný jen pro určité situace. Vždy mějte na paměti přehlednost a čitelnost kódu. Je lepší mít více řádků přehledného kódu, než se snažit za každou cenu zhušťovat kód na úkor přehlednosti. Byť v kódu roztaženém přes zbytečně mnoho řádků se někdy také špatně orientuje.

Když je řeč o přehlednosti kódu, může se stát, že při splnění podmínky nechcete udělat nic a máte jen kód pro nesplnění. Někdo v takovém případě nechá blok if prázdný a kód dá do bloku else. To nedělejte. Raději napište obrácenou podmínku a kód umístěte do bloku if. Pamatujte, že jakoukoli podmínku lze otočit operátorem negace (! před podmínkou).

Například chceme vypsat hlášku o neexistenci souboru.
Méně přehledné řešení:

<?php
// toto nedělejte
if (is_file("prvni.php")) {
} else {
  echo "Soubor nenalezen!";
}
?>

Lepší řešení je podmínku otočit:
<?php
if (!is_file("prvni.php")) {
  echo "Soubor nenalezen!";
}
?>

Rozlišení více než dvou situací

Zatím jsme rozlišovali jen splnění a nesplnění podmínky. Někdy ale potřebujete rozlišit situací více.

Například číslo je větší, menší nebo rovno nějaké hodnotě. Pak můžete za podmínkou místo else uvést elseif, další podmínku a blok kódu. Tento kód se provede v případě, že není splněna podmínka bloku if a zároveň je splněna podmínka daného elseif. Takových částí může být i více za sebou a na konci může opět následovat blok else, který se provede, pokud není splněna ani jedna z předchozích podmínek. U podmínky, která obsahuje část if, jednu nebo více elseif a else se nejprve vyhodnotí podmínka u if a je-li splněna, provede se blok kódu za if a poté se pokračuje dalším příkazem za koncem celé podmínky (tj. všechny bloky elseif a else se přeskočí). Není-li podmínka splněna, vyhodnocují se postupně jednotlivé podmínky elseif v pořadí, v jakém jsou v kódu napsané a provede se první, jejíž podmínka je splněna. Není-li splněna ani jedna podmínka elseif, provede se blok else, je-li zadán.

V dalším příkladu se kromě výše uvedeného podíváme i na další užitečnou funkci, a sice date. Funkce date() vrátí složky aktuálního data a času ve formátu, jaký je určen argumentem funkce. Argument funkce je řetězec, ve kterém jsou složky data a času symbolizované písmeny. V manuálu funkce date (viz předchozí odkaz) najdete jejich přehled a význam (měl by být pochopitelný i v angličtině).

Datum a čas v českém formátu se zapíše date("j.n.Y G.i:s"); (den, tečka, měsíc, tečka, rok, mezera, hodiny, tečka, minuty, dvojtečka, sekundy, přičemž u minut a sekund mají jednociferná čísla úvodní nulu, např. 1.1.2013 0.00:00, případně date("d.m.Y H.i:s"); pro úvodní nuly i u dne, měsíce a hodin, tj. např.: 01.01.2013 00.00:00).

Ukažme si tedy kód, který vypíše denní dobu:

<?php
$hodina = date("G"); // date("G") vrátí aktuální hodinu
echo "Právě je ";
if ($hodina > 18){
  // Víc než 18, tedy 19-23
  echo "večer";
}
elseif ($hodina >= 12) {
  // Není vyšší hodina než 18, ale je 12 nebo víc, tedy 12.00-18.59
  echo "odpoledne";
}
elseif ($hodina >= 9) {
  // 9.00-11.59
  echo "dopoledne";
}
elseif ($hodina >= 5) {
  // 5.00-8.59
  echo "ráno";
}
else {
  // 0.00-4.59
  echo "noc";
}
?>

Někdy chcete testovat výčet hodnot určité proměnné.
Například:
<?php
$cislo1 = 6;
$cislo2 = 2;
$vysledek = 0;
$operace = "+";

if ($operace === "+") {
  $vysledek = $cislo1 + $cislo2;
}
elseif ($operace === "-") {
  $vysledek = $cislo1 - $cislo2;
}
elseif ($operace === "*") {
  $vysledek = $cislo1 * $cislo2;
}
elseif ($operace === "/") {
  $vysledek = $cislo1 / $cislo2;
}
?>

To může být při větším počtu podmínek nepřehledné a náročné na údržbu. Proto existuje příkaz switch, kterým lze tento druh podmínek zjednodušit a zpřehlednit. Níže uvedený příklad dělá to samé jako výše uvedený.
<?php
$cislo1 = 6;
$cislo2 = 2;
$vysledek = 0;
$operace = "+";

switch ($operace) {
  case "+":
    $vysledek = $cislo1 + $cislo2;
    break;
  case "-":
    $vysledek = $cislo1 - $cislo2;
    break;
  case "*":
    $vysledek = $cislo1 * $cislo2;
    break;
  case "/":
    $vysledek = $cislo1 / $cislo2;
    break;
}
?>

Podmínka se zapisuje klíčovým slovem switch, za kterým se uvede proměnná či výraz. Oproti příkazu if zde výsledek výrazu není bool, ale obvykle číslo nebo řetězec. Následují složené závorky a v nich seznam bloků case. Ty začínají klíčovým slovem case, za kterým následuje hodnota a dvojtečka. Tomu se říká tzv. návěští. Za ním je pak blok kódu, zakončený příkazem break. Vyhodnocení probíhá tak, že se vezme hodnota u switch a porovnává se s hodnotami jednotlivých case. V případě rozdílných datových typů se přetypuje (tedy jako by mezi oběma hodnotami byl operátor rovnosti ==), ale měli byste se snažit, aby hodnoty měly stejný datový typ. Zpracuje se první blok case, který má stejnou hodnotu jako je u switch.

A tak jako if...elseif lze zakončit blokem else, který se provede v případě, kdy žádná z podmínek není splněna, v případě switch má stejnou úlohu blok default. Zde se samozřejmě nepíše hodnota (nemá význam), jen návěští default, dvojtečka a blok kódu, který se provede, jestliže hodnota u switch neodpovídá žádné z hodnot case. PHP také umožňuje na konci bloku case vynechat příkaz break, čímž zpracování pokračuje sekvenčně dál, tedy "propadne" do následujícího case. Toto je v některých případech užitečné, ale pro začátečníka může být velice nepříjemné v případě, že příkaz break vynechá omylem. Proto dodržujte pravidlo, že pokud v bloku case záměrně vynecháváte příkaz break, vždy místo něj napište komentář, že byl vynechán záměrně.

Příklad "propadávání" a použití bloku default:

<?php
$cislo = 50;
$modulo = $cislo % 4; // operátor modulo, tedy zbytek po dělení
// číslo modulo 4 může být 0, 1, 2 nebo 3
switch ($modulo) {
  case 0 : echo "Číslo je dělitelné 4<br>";
    // break vynechán, propadává do další větve
  case 2 : // Číslo je dělitelné 2, pokud zbytek po dělení 4 je 0 nebo 2
    echo "Číslo je dělitelné 2<br>";
    break; // zde skončí case 0 a case 2
  default: // Pokud neplatí ani jedno z předchozího
    echo "Číslo není dělitelné 2 ani 4<br>";
    // na konci default break být nemusí
}
?>
Vyzkoušejte pro různé hodnoty proměnné $cislo.

Kromě podmínek existuje ještě další nástroj pro řízení toku skriptu, a sice cykly, o kterých si povíme v příští kapitole.

Vyzkoušejte si

1. Definujte proměnnou obsahující číslo a napište podmínku, která vypíše, zda její hodnota je větší, menší nebo rovna 10.

2. Napište skript, který zjistí, kolik celých hodin zbývá do konce dne a vypíše "Do konce dne zbývá x hodin", přičemž slovo "hodin" bude ve správném tvaru (1 hodina, 2 hodiny atd.) Pokud nezbývá ani jedna celá hodina, napíše se "Méně než 1 hodina".
Nápověda: Pro určení počtu hodin do konce dne můžete použít 23 - date("G").

3. Napište skript, který vypíše aktuální datum (den a měsíc), ale měsíc bude slovy (tj. "ledna", "února", atd.)
Nápověda: Číslo měsíce získáte přes date("n"); a číslo dne přes date("j");
Zkuste navrhnout řešení pomocí switch a, s využitím minulé kapitoly, řešení pomocí pole.
Porovnejte, které řešení je lepší.

Řešení

1. Jedno z možných řešení je například:

<?php
$cislo = 12;
echo "Číslo je ";
if ($cislo == 10) {
  echo "rovno 10";
}
elseif ($cislo > 10) {
  echo "větší než 10";
}
else {
  echo "menší než 10";
}
?>

Šlo by také použít dva vnořené podmíněné operátory a podmínku tím zhustit na jediný řádek. Je to ovšem na úkor přehlednosti kódu, takové řešení je náchylné k zanesení chyb a špatně se udržuje, proto ho nelze doporučit.
Pro ilustraci:
<?php
$cislo = 12;
echo "Číslo je ";
echo ($cislo == 10) ? "rovno 10" : (($cislo > 10) ? "větší než 10" : "menší než 10");
?>

Druhý podmíněný operátor je vnořený do větve při nesplnění podmínky prvního podmíněného operátoru. Stačí ale např. zapomenout závorky kolem druhého podmíněného operátoru a kód nebude fungovat správně (vyzkoušejte pro číslo 10).

2. Možné řešení:

<?php
$zbyva = 23 - date("G");
$hlaska = "Do konce dne ";
switch ($zbyva) {
  case 0:
     $hlaska = $hlaska. "zbývá méně než 1 hodina";
     break;
  case 1:
     $hlaska = $hlaska. "zbývá 1 hodina";
     break;
  case 2: // propadne do case 4
  case 3: // propadne do case 4
  case 4:
    $hlaska = $hlaska."zbývají ".$zbyva." hodiny";
    break;
  default:
    $hlaska = $hlaska."zbývá ".$zbyva." hodin";
}
echo $hlaska;
?>
Samozřejmě by šlo použít if-elseif-else.

3. Možné řešení pomocí switch:

<?php
$mesic = date("n");

echo date("j");
switch ($mesic) {
  case 1:
    echo ". ledna";
    break;
  case 2:
    echo ". února";
    break;
  case 3:
    echo ". března";
    break;
  case 4:
    echo ". dubna";
    break;
  case 5:
    echo ". května";
    break;
  case 6:
    echo ". června";
    break;
  case 7:
    echo ". července";
    break;
  case 8:
    echo ". srpna";
    break;
  case 9:
    echo ". září";
    break;
  case 10:
    echo ". října";
    break;
  case 11:
    echo ". listopadu";
    break;
  case 12:
    echo ". prosince";
    break;
}
?>

Možné řešení pomocí pole:
<?php
// Začneme od indexu 1 (výchozí je 0), aby číslo měsíce odpovídalo indexu v poli
$mesice = array(1 => "ledna", "února", "března", "dubna", "května", "června",
          "července", "srpna", "září", "října", "listopadu", "prosince");
echo date("j").". ".$mesice[date("n")];
?>

Řešit takovéto výčty (měsíce, dny v týdnu a podobně) využitím pole je obvykle výhodnější. Jednak je kód kratší, ale hlavně lze pole definovat jen jednou a pak ho používat opakovaně na více místech.

Máte návrh na vylepšení či doplnění článku? Obsahuje článek nepřesné informace, nebo v něm chybí něco důležitého?
Tento článek má diskusní vlákno na diskusi Jak Psát Web, kam můžete náměty a připomínky napsat.


Správcem webu Péhápko.cz je Joker, mail zavináč it-joker tečka cz. Informace o autorských právech a možnostech použití obsahu viz Autorská práva
Přihlášení