Most egy érdekességről fogok írni, aminek igazán gyakorlati hasznát nem látom, viszont jó tudni. A switch elágazás nem egyedi a php-ben. Más nyelvek is megvalósítják, mint például a java, C vagy a pascal, csak más néven és/vagy egy kicsit másképp. Bizonyos esetekben az IF elágazás felváltására szolgál. Két érték pedig többnyire akkor lesz egyenlő, ha a típus is egyezik. Ám PHP-ben az értelmező mondhatni, kénye kedvére változtathatja a típusokat egy-egy ellenőrzés alkalmával. Persze adott, hogy mit mire tud konvertálni. De ha biztosak akarunk lenni a típusok egyezésében is, akkor a == helyett a === -re van szükség. Switch esetén viszont mindig az első verzió, azaz a típusegyeztetés nélküli ellenőrzés történik. Ez egy részt lehet probléma. Más részt ha ez probléma, akkor az if-eket se nehezebb megírni sok esetben. Mégis bizonyos speciális helyzetekben igen csak el kéne gondolkozni, hogy ugyanazt a működést egy IF hogy váltja fel. Ez pedig a break utasítások használatával és a default ág elhelyezésével függ össze.
Első körben nézzünk egy egyszerű switch elágazást.
switch ($a)
{
case 1:
print "Ez egy 1-es";
break;
case 2:
print "Ez egy 2-es";
break;
case 12:
print "Ez a 12-es";
break;
default:
print "A megadott érték: ".$a;
}
Ugyanez if-ekkel.
{
print "Ez egy 1-es";
}
else if ($a == 2)
{
print "Ez egy kettes";
}
else if ($a == 12)
{
print "Ez a 12-es";
}
else
{
print "A megadott érték: ".$a;
}
Tehát kiírja, hogy Ez egy 2-es. Gondolom látható, hogy azok az ágak, amik egy case-el kezdődnek a switch-ben és a végén van egy break, az if-es megoldásnál megfelelő sorrendben bekerülnek egy-egy if avagy else if ágba. A default pedig az if elágazás else ágának felel meg. De mi a helyzet, ha nem teszek valahova break-et? Amint megtalálja az igaz feltételt, az aktuális ágtól kezdve az összes ágba bele fog futni amíg nem talál egy break-et.
switch ($a)
{
case 1:
print "Ez egy 1-es";
break;
case 2:
print "Ez egy 2-es";
case 12:
print "Ez a 12-es";
default:
print "A megadott érték: ".$a;
}
Kiírja, hogy Ez egy2-esEz a 12-esA megadott érték: 2
Na ugye ezt már nem is olyan kézenfekvő átírni. Leszámítva azt a megoldást, ahol minden lehetőségnek készítünk egy új ágat. Ráadásul ha a default ágat valahova a két case közé tesszük, vagy az összes case elé, az megint nehezít a dolgon. Mert ugye ha egyik feltétel sem teljesül, akkor a default lesz a nyerő. De akkor ha nincs a végén break, azok is lefutnak, amik feltételtől függöttek.
Erre talán jó példa lehet, ha elképzelünk egy diagnosztikai programot. Ahol meg lehet adni, milyen szintű diagnosztikát végezzen. A szinteket az egyes case ágak jelentik. De azt akarjuk, hogy ha nem adják meg a szintet, akkor a legalaposabb vizsgálat történjen. És erről még figyelmeztessen is.
switch ($level)
{
default:
print "Definiálatlan szint! Minden ellenőrzést elvégzek.<br />";
case 3:
print "3-es szintű diagnosztika!<br />";
case 2:
print "2-es szintű diagnosztika!<br />";
case 1:
print "1-es szintű diagnosztika!<br />";
}
Sehol sincs break, tehát a 3-as választásakor a 2-es és az 1-es is lefut. Ha pedig nincs szint megadva, akkor maximális szintet választ, de azért figyelmeztet, hogy ez nem a mi döntésünk volt. Nem túl életszerű, de ez van.
Szép és jó tehát a switch, de térjünk vissza a típusokhoz. A példa maradjon a régi, de most a 2-es case ágába nem a számot írom, hanem magát a szöveget, hogy 2-es
switch ($a)
{
case 1:
print "Ez egy 1-es";
break;
case '2-es':
print "Ez egy 2-es";
break;
case 12:
print "Ez a 12-es";
break;
default:
print "A megadott érték: ".$a;
}
Minden gond nélkül kiírja a következőt: Ez egy 2-es. Pedig abban a case ágban csak az első két karakter egyezik meg a számmal. Nézzük hogy néz ki ugyanez if-ekkel.
if ($a == 1)
{
print "Ez egy 1-es";
}
else if ($a == '2-es')
{
print "Ez egy kettes";
}
else if ($a == 12)
{
print "Ez a 12-es";
}
else
{
print "A megadott érték: ".$a;
}
Mivel a '2-es' egy string, úgy hasonlítja össze az $a változóval, hogy az első karaktertől addig nézi, amíg talál szám karaktert. A tizedespontot is beleértve. De itt a 12 után már kötőjel van. Azzal nem tud mit kezdeni, tehát nem is foglalkozik vele. Számként kezeli. A feltétel teljesül.
Na és itt jön a trükk. PHP-ben a case után nem csak egyszerű értékeket szabad írni. Lehet egy összetettebb feltétel is. Ebben az esetben ez az összetett feltétel kerül az őt helyettesítő IF-ben a vizsgált érték helyére. Ami persze egy logikai értéket fog adni, de minden olyan értékre helyes lesz az eredmény, ami logikai igazzá konvertálható. Jöjjön egy példa a típusfüggő switch-re
switch ($a)
{
case $a === 1: //$a == ($a === 1) --> 2 == (2 === 1) --> 2 == false --> false
print "Ez egy 1-es";
break;
case $a === '2-es': //$a == ($a === '2-es') --> 2 == (2 === '2-es') --> 2 == false --> false
print "Ez egy '2-es' string";
break;
case $a === 2: //$a == ($a === 2) --> 2 == (2 === 2) --> 2 == true --> true
print "Ez a 2-es";
break;
default:
print "A megadott érték: ".$a;
}
Hogy ez miért működik? A case-ben egy logikai érték szerepel: $a === '2-es'. Ami persze nem igaz, mert itt már figyeltünk a típusra. Így az $a változó ezzel a false értékkel lesz összehasonlítva: $a == false. Ez szintén nem igaz, mert az $a változó értéke 2 volt. Azt pedig a legnagyobb rosszindulattal sem tudja az értelmező hamissá konvertálni. Ha már ennyire mondom, mondjam azt is, mit tudna. Ugye? Például a 0 számot, a null értéket, az üres stringet ( $a == "" ), az üres tömböt ( array() ). És persze a logikai false értéket már nem is kell konvertálnia. A fenti példában most kezdőértéknek null-t adok.
switch ($a)
{
case $a === 1: //$a == ($a === null) --> null == (null === 1) --> null == false --> true
print "Ez egy 1-es";
break;
case $a === '2-es': //$a == ($a === '2-es') --> null == (null === '2-es') --> null == false --> true
print "Ez egy '2-es' string";
break;
case $a === 2: //$a == ($a === 2) --> null == (null === 2) --> null == false --> true
print "Ez a 2-es";
break;
default:
print "A megadott érték: ".$a;
}
Így pedig már látszólag borul az egész elmélet. De nem teljesen. Tudjuk, hogy hamissá konvertálható érték nem kerülhet ebbe a switch-be. De írható olyan inverz switch, ahova viszont csak azok kerülhetnek.
switch ($a)
{
case $a !== 1: //$a == ($a !== 1) --> null == (null !== 1) --> null == true --> false
print "Ez egy 1-es";
break;
case $a !== '2-es': //$a == ($a !== '2-es') --> null == (null !== '2-es') --> null == true
print "Ez egy '2-es'";
break;
case $a !== null: // $a == ($a !== null) --> null == (null !== null) --> null == false --> true
print "Ez a null érték.";
break;
default:
print "A megadott érték: ".$a;
}
Első ránézésre lehet, hogy marhaságnak tűnik. De végignézve a kiértékelés alakulását ( a megjegyzésekben ), belátható, hogy ez tényleg működik. Tehát ha olyan switch-re van szükség, ami mindkettőt jól kezeli, két switch kell és egy IF. A következőképpen:
if ($a) : switch ($a)
{
case $a === 1:
print "Ez egy 1-es";
break;
case $a === '2-es':
print "Ez egy '2-es'";
break;
case $a === 2:
print "Ez a 2-es";
break;
default:
print "A megadott érték: ".$a;
}
else: switch ($a)
{
case $a !== 0:
print "Ez egy 0";
break;
case $a !== "":
print "Ez egy üres string";
break;
case $a !== null:
print "Ez a null érték";
break;
case $a !== false:
print "Ez a false érték";
break;
default:
print "A megadott érték: ".$a;
}
endif;
A lényeg tehát, hogy hamissá konvertálható értékeknél pont a feltétel nem teljesülését kell vizsgálni a case-ben. Ami végeredményül pont az elvárt működést adja. Elég nyakatekert megoldása ez a switch használatának, de ki tudja. Lehet, hogy egyszer még szükség lesz rá.
Végül egy bónusz példa, ha már végképp nem tudod, mi olyant írhatnál, amire senki sem gondol switch-ekkel:
$b = 9;
switch (true)
{
case $a === 1:
print "\$a egy 1-es";
break;
case $b === 2:
print "\$b egy 2-es";
break;
}
Bármilyen feltétel írható tehát a case-ek után és ráadásként még a switch paramétere is lehet logikai igaz. Így visszakapjuk az IF-et switch formában. Annyi extrával, hogy továbbra is játszhatunk a break-ekkel és a default ág helyzetével.
Ez a példa egyébként kapásból kiváltja az if+switch-es megoldást. Köszönet Suttogó-nak, hogy felhívta rá a figyelmem (2011. január 8.). Tehát:
switch (true)
{
case $a === 0: //true == (0 === 0) --> true == true
print "Ez nulla";
break;
case $a === 1: //true == ($a === 1) --> true == (0 === 1) --> true == false --> false
print "Ez egy 1-es";
break;
case $a === '2-es': //true == ($a === '2-es') --> true == (0 === '2-es') --> true == false --> false
print "Ez egy '2-es' string";
break;
case $a === 2: //true == ($a === 2) --> true == (0 === 2) --> true == false --> false
print "Ez a 2-es";
break;
default:
print "A megadott érték: ".$a;
}
Őrült vagyok, hogy ilyeneken jár az agyam. Tudom. De az őrült gondolatokból születnek sokszor a zseniális ötletek. Remélem valakinek hasznos lesz ez a cikk. Ha másra nem is, altató helyett :)