Operátorok III.
Növelő és csökkentő operátorok
A növelő és csökkentő operátorok a ++ és a --. Ha egy változót szeretnénk eggyel növelni vagy csökkenteni, akkor a változó elé írva a megfelelő operátort, az végrehajt egy értékadást, például:
$n = $n + 1;
Ezzel egyenértékű a ++$n; utasítás. Valójában mindkét operátorból kétféle van, amit úgy értek, hogy használható előtagként és utótagként egyaránt, vagyis nem csak a változó elé, hanem utána is írható. Például a ++$n helyett írhatjuk azt is, hogy $n++, a --$n helyett pedig azt, hogy $n--, a hatásuk ugyanaz lesz, $n értéke eggyel nő, illetve csökken.
Nézzük például az első házi feladat megoldásának második változatát! Az ottani while-ciklust így is írhatjuk:
while ($n < $kezdet + $hossz){
$ret .= $string{$n};
$n++;
}
Ennek ugyanaz lesz az eredménye, a harmadik sorban $n értéke eggyel nő. Jó kérdés, hogy akkor ennek most mi az értelme? A két operátor - ha csak így magában áll - ugyanazt csinálja, viszont összetett kifejezésben használva már különböznek egymástól. Ha egy összetett kifejezésen belül használjuk, akkor a változó növelése/csökkentése a kifejezés kiértékelése előtt vagy után fog végrehajtódni attól függően, hogy előtagként vagy utótagként használjuk az operátort. Tipikus példa az, amikor tömb vagy karakterlánc indexeként használjuk a növelni kívánt változót, mint a fenti ciklus magjában. Például, ha ezt írjuk:
$ret .= $string{++$n};
akkor ez azt fogja eredményezni, hogy az $n értéke növekszik eggyel, majd a $ret karakterlánchoz hozzáfűzzük a $string{$n} karaktert, ahol azonban $n értéke már a növekedett érték. Vagyis a fenti utasítás az alábbival egyenértékű:
$n = $n + 1;
$ret .= $string{$n};
Ha utótagként használjuk az operátort:
$ret .= $string{$n++};
akkor a sorrend fordított, itt először a $ret karakterlánchoz hozzáfűzzük a $string{$n} karaktert, majd ezután növekszik $n értéke. Az ennek megfelelő utasítássorozat tehát:
$ret .= $string{$n};
$n = $n + 1;
Vagyis ha sietünk (a változó növelésével), akkor előtagként kell használni, ha ráérünk, akkor lehet utótagként. Ha meg csak önmagában áll a növelő operátor, akkor azt választjuk, amelyik jobban tetszik. Minden eddig elmondott dolog pontosan ugyanígy igaz a -- csökkentő operátorra is.
Feltételes operátor
Sokszor előfordulhat, hogy egy feltételtől függően szeretnénk végrehajtani egyetlen utasítást. Például:
$honap = date("m", time());
if ($honap{0} == "0"){
print $honap{1};
}
else{
print $honap;
}
Itt ki szeretnénk írni az aktuális hónap sorszámát, de az elején lévő 0 nélkül. Így ha a hónap első karaktere ($honap{0}) nulla, akkor csak a másodikat írjuk ki. Gyakran előfordulnak hasonló elágazások, és van egy operátor, amivel egyszerűbben, egy sorban elintézhetjük az ilyeneket. Erre használható a ?: operátor, ami az egyetlen olyan, aminek három operandusa van. A kérdőjel és a kettőspont választja el őket így: op1 ? op2 : op3.
Az első operandus egy logikai érték, a másik kettő bármilyen típusú lehet. Ha az első operandus true, akkor a kifejezés értéke a második operandus lesz, ha false akkor pedig a harmadik. Például a true ? 3 : "nem" kifejezés értéke 3, míg a false ? "x" : true értéke true. Persze ezt nem ilyen formában szokták használni, hanem az első operandus rendszerint egy feltételes kifejezés, aminek az értéke alapján dől el a teljes kifejezés értéke. Ha tehát azt akarjuk megvizsgálni, hogy a $honap karakterlánc első karaktere "0", és ettől függően szeretnénk kiíratni valamit, akkor azt így is megtehetjük:
print (($honap{0} == "0") ? $honap{1} : $honap);
Itt a zárójelben lévő kifejezés értéke $honap{1} vagy $honap attól függően, hogy a feltétel teljesül-e vagy sem. Így ez a sor egyenértékű a fenti elágazással. Fontos különbség az if-else szerkezet és a ?: operátor között, hogy itt a második és harmadik operandus nem lehet utasítás, hanem csak egy kifejezés! Így ha az alábbi elágazást szeretnénk átalakítani:
if ($x > $y){
return $x." nagyobb";
}
else{
return $y." nagyobb";
}
akkor azt így lehet megcsinálni a feltételes operátorral:
return (($x > $y) ? $x." nagyobb" : $y." nagyobb");
Vagyis nem írhatjuk bele a return utasítást az operandusba. A zárójelezésre általában nem kell nagyon odafigyelni, mert a feltételes operátor viszonylag gyengén köt. Ezért nem kellett az előbb zárójelbe tenni az összefűző operátort használó operandusokat, sőt az első operandust sem kellett volna, de talán így egy kicsit áttekinthetőbb. Az értékadó operátornál viszont szorosabb (vagyis előbb hajtódik végre), ezért az alábbi sor normálisan fog működni:
$tartalom = ($jelszo == "nemtudom") ? $html : ""; print $tartalom;
Ha jó a jelszó, akkor a $html karakterlánc kerül kiírásra, ha nem, akkor pedig semmi. De ha nem vagy biztos az operátorok végrehajtási sorrendjében, érdemes megfelelően zárójelezni.
Hibaelnyomó operátor
Feltételezem, hogy láttál már hibaüzenetet, ami php kód futtatása során jelent meg (ha nem, akkor ott valami nagy probléma van). Talán azt is észrevehetted, hogy ezeknek több fajtája létezik. A három leggyakoribb a "Parse error", a "Fatal error" és a "Warning". Ezek a szavak a hibaüzenet elején vannak, utána van a hiba leírása. A "parse error" azt jelenti, hogy nem lehet értelmezni a php kódot, általában valamilyen nyelvtani hiba az oka, mondjuk lemaradt egy pontosvessző, egy zárójel, vagy hasonló. A "fatal error" oka elég sokminden lehet, például egy végtelen ciklus is ilyet produkál. Lényeg az, hogy ennél a két típusnál a php kód futtatása leáll, amint a hiba bekövetkezik. A "warning" viszont csak egy figyelmeztetés, így a program tovább fut, csak egy szép kis hibaüzenet fogja díszíteni az oldalunkat. "Warning"-ot okozhat például a 0-val való osztás, vagy néhány beépített függvény, amennyiben nem tudja elvégezni a feladatát. Ilyen lehet például a mail() függvény, amivel e-mailt tudunk küldeni. Három paramétere van, a címzett, az e-mail tárgya és az üzenet szövege. Például:
$uzenet = "Ember, nem értek az egészből semmit!!!\n\nÜdv,\nWebmester";
mail("norbqcd@gmail.com", "PHP tanfolyam", $uzenet);
Ha lefuttatod ezt a függvényt, akkor elvileg én ("PHP tanfolyam" tárggyal) az alábbi lehangoló levelet fogom kapni az e-mail címemre:
Ember, nem értek az egészből semmit!!!
Üdv,
Webmester
A wamp-on ha lefuttatod, csak egy szép kis warningot fogsz kapni, a címzett meg semmit, mivel e-mail küldéséhez kell egy e-mailküldő szerver, a wamp viszont nem tartalmaz ilyet. De ha a netre feltöltöd, és a szolgáltató listáján szerepel az e-mail küldés, akkor valószínűleg fog működni. Egyébként a függvény visszatérési értéke false, ha a levelet nem sikerült elküldeni, true ha igen. Utóbbi eset csak annyit jelent, hogy a levél elment, az hogy a címzett meg is kapta, már más kérdés (ha a szerveren jól működik az e-mailküldés akkor azért nagy valószínűséggel megkapja).
Ha például egy e-mailes regisztráció esetén szeretnénk tudatni a felhasználóval, hogy van-e esély arra, hogy megkapta a levelet, akkor ezt megtehetjük így:
$email = mail($cim, "Regisztráció", $uzenet);
if ($email == false){
print "Ez nem jött össze, úgyhogy most töltheted ki újra az egész űrlapot!";
}
else{
print "Az e-mailben lévő linkre kattintva érvényesítheted a regisztrációdat!";
}
Az egyetlen szépséghiba, hogy ha a visszatérési érték false, akkor a mi kis udvarias üzenetünk felett ott fog éktelenkedni egy "warning". Ha ezt el akarjuk kerülni, használhatjuk a hibaelnyomó operátort, ami egy @ (kukac), és az operandusa a hibát kiváltó kifejezés, jelen esetben a mail() függvény hívása. Az operátort előtagként kell használni:
$email = @mail($cim, "Regisztráció", $uzenet); ...
A mail() által kiváltott hibaüzenet így nem fog kiíródni. Mivel a függvényhívás operátora a legszorosabban köt, így fölösleges zárójelezni, de ha például a nullával való osztás okozta figyelmeztetést szeretnénk elsumákolni, akkor azt így érdemes csinálni:
$szam = 0; print @(5 / $szam);
Azért arra ne számítsunk, hogy a print ennek hatására valamilyen végtelen jelet, vagy hasonló okosságot fog kiírni :) Ha nullával való osztás veszélye áll fenn, inkább ne a hibaelnyomó operátorral kerüljük meg a problémát, hanem vizsgáljuk meg a $szam == 0 feltételt, és így eleve nem fogunk "warningot" produkálni.
Többdimenziós tömbök
Az indexelő operátorokat (a [] és {} operátorok) már valamennyire ismerjük, most azzal foglalkozunk, hogy egy tömbön belüli tömböt vagy karakterláncot hogyan tudunk elérni velük.
Egy tömb eleme maga is lehet tömb. Például, ha van egy tömb a globális hatókörben, de azt egy függvény belsejéből szeretnénk elérni, akkor a tömbre a $GLOBALS tömb elemeként hivatkozhatunk:
$napok = array("H", "K", "Sze", "Cs", "P", "Szo", "V");
function foo(){
...
$tomb = $GLOBALS["napok"];
...
}
Most a $GLOBALS tömb "napok" kulcsú eleme szintén egy tömb, aminek valamelyik elemére a [] operátorral hivatkozhatunk. Az operandusok op1[op2] formában helyezkednek el, ahol op1 a tömb neve, op2 pedig az elérni kívánt elem kulcsa/indexe. Jelen esetben a tömb neve $GLOBALS["napok"], az index meg legyen mondjuk 3, ekkor a "Cs" karakterláncot kapjuk: ($GLOBALS["napok"])[3]. Az op1 helyére beírtuk a tömb nevét. Ez olyasmi, mint például a + operátor esetén a (4+2)+6 forma, amit lehet zárójel nélkül is írni, mivel a + operátor kiértékelése balról jobbra történik. Ez az indexelő operátor esetében is így van, vagyis a kerek zárójel ott is elhagyható: a "Cs" elemre $GLOBALS["napok"][3] formában is hivatkozhatunk.
Ha a globális változó egy karakterlánc, és annak valamelyik karakterét szeretnénk elérni, akkor teljesen hasonló módon tehetjük meg, persze akkor a második operátor a {} lesz. Például:
$nev = "Valaki";
function foo(){
...
$betu = $GLOBALS["nev"]{4};
...
}
Ebben az esetben a $betu változó értéke "k" lesz. Ennek alapján, ha a korábbi példa esetében a $napok tömböt ilyenné szeretnénk alakítani:
$napok_egybetus = array("H", "K", "S", "C", "P", "S", "V");
akkor azt egy ciklussal megtehetjük például így:
$napok = array("H", "K", "Sze", "Cs", "P", "Szo", "V");
$napok_egybetus = array();
for ($i = 0; $i < 7; $i++){
$napok_egybetus[$i] = $napok[$i]{0};
}
Az olyan tömböt, aminek az elemei tömbök, általában többdimenziós tömbnek szokták hívni. Ilyet magunk is létrehozhatunk, ha épp erre van szükség. Például ha a napok neveit más nyelven is el szeretnénk tárolni egy változóban, akkor létrehozhatunk egy ilyen többdimenziós tömböt:
$napok = array(
"HU" => array("H", "K", "Sze", "Cs", "P", "Szo", "V"),
"EN" => array("M", "Tu", "W", "Th", "F", "Sa", "Su"),
"DE" => array("Mo", "Di", "Mi", "Do", "F", "Sa", "So"),
);
Most a $napok tömbnek három eleme van, melyek maguk is tömbök. Amikor egy függvény paramétereit bonyolult módon adjuk meg (mint itt a külső array() függvény esetében), akkor a fent látható modon érdemes behúzásokat és sortöréseket használni, hogy átláthatóbb legyen. Ezután például a "Th" karakterláncot a $napok["EN"][3] formában érhetjük el.
Többdimenziós tömböket sokszor számokkal indexelt formában használnak, egy ilyen adatszerkezetben jól lehet például egy táblázat adatait tárolni:
$tablazat = array( array(10, 23, 12, 34), array(62, 96, 22, 77), array(51, 72, 44, 80), );
Ha ezt valahogy ki szeretnénk íratni, akkor be kell járnunk a többdimenziós tömböt. Ebben az esetben bejárhatjuk például egy beágyazott for-ciklussal:
for ($i = 0; $i < 3; $i++){
for ($j = 0; $j < 4; $j++){
print $tablazat[$i][$j].", ";
}
print "<br />";
}
Az $i változó a táblázat sorain fut végig, a $j pedig egy soron belüli elemeken. Vigyázni kell, hogy a belső ciklus magjában nem a $tablazat[$j][$i] elemet kell kiíratni, hanem a $tablazat[$i][$j]-t, mivel a $tablazat[$i] hivatkozik a táblázat $i-edik sorára, ami a $tablazat tömb $i indexű eleme. Ilyesmikre nem kell odafigyelni, ha a foreach szerkezetet használjuk, melyet karakterláncokkal indexelt többdimenziós tömbök esetén is használhatunk. Például a korábbi példában látott $napok tömb bejárható így:
foreach ($napok as $nyelv){
foreach ($nyelv as $nap){
print $nap.", ";
}
print "<br />";
}
Ez is beágyazott ciklus lesz, mivel a külső foreach által szolgáltatott $nyelv változóba a $napok tömb egyik eleme kerül, ami viszont még mindig tömb, így azt egy belső foreach szerkezettel kell bejárnunk. Ha az indexekre is szükségünk van, akkor a szokott módon megkaphatjuk (a foreach hosszabb változatával):
foreach ($napok as $kod => $nyelv){
print $kod.": ";
foreach ($nyelv as $nap){
print $nap.", ";
}
print "<br />";
}
Ez az alábbi kimenetet eredményezi:
HU: H, K, Sze, Cs, P, Szo, V,
EN: M, Tu, W, Th, F, Sa, Su,
DE: Mo, Di, Mi, Do, F, Sa, So,
Az alapok részben már nem fogunk több operátort tanulni, mivel a kimaradt operátorok vagy csak objektumok esetén, vagy ritkán használatosak. Ha más által írt PHP kódot olvasnál, az alábbi operátorok fordulhatnak még elő, melyekről itt nem volt szó:
:: -> ~ & | ^ << >> &= |= ^= <<= >>=
Az első kettő csak objektumok esetén használható, a többiek ún. bitenkénti operátorok, és ezek értékadó megfelelői (pl. az &= az & értékadó megfelelője, úgy mint a += a + operátornak). A bitenkénti operátorok aritmetikai műveleteket végeznek egész számokkal, ami azt jelenti, hogy helyettesíthetők aritmetikai operátorokkal (+, -, *, /, %). Például az 5 << 2 egyenértékű az 5 * 4 művelettel. Emellett a használatuk viszonylag nehézkes, mert kettes számrendszerben kell gondolkodni ahhoz, hogy megértsük hogyan működnek. A PHP-ben egyébként nincs nagy jelentőségük, inkább alacsonyabb szintű nyelvekben szokták használni őket.
Házi feladat
1.) Adott az alábbi két globális változó:
$verem = array(); $meret = 0;
Hozzunk létre három függvényt: push($elem), pop() és kiir()! A push() függvény a $verem tömb végére rakja az argumentumát, a pop() kiveszi az utolsó elemet a $veremből, a kiir() pedig kiírja a benne lévő elemeket. Fontos, hogy minden művelet elvégzése után a $meret változóban a $veremben lévő elemek számának kell lennie! Próbáld meg a push() és pop() függvényeket egyetlen utasítással megvalósítani, a kiir()-ben pedig használd fel a $meret változót!
Mivel globális változók, függvényen belül csak a $GLOBALS tömbön keresztül érhetőek el.
Mivel a $meret változóban mindig a tömb mérete van, ezért a tömb utolsó eleme utáni index a $meret-tel egyenlő, amire függvényen belül a $GLOBALS["meret"] módon hivatkozhatunk. A push() függvénynek be kell raknia a $verem[$meret] változóba az argumentumát, ezután növelnie kell a $meret-et. Mivel a növelést az értékadás után kell elvégezni, a ++ operátort utótagként kell használni. A pop() függvény esetén az utolsó elemet meg kell szüntetni (pl null értéket adunk neki), és csökkenteni kell a $meret-et. Egy utasítással úgy valósítható meg, ha előtagként használjuk a -- operátort, ekkor a $meret változó az utolsó elem indexe lesz az értékadásban.
function push($elem){
$GLOBALS["verem"][$GLOBALS["meret"]++] = $elem;
}
function pop(){
$GLOBALS["verem"][--$GLOBALS["meret"]] = null;
}
function kiir(){
for ($i = 0; $i < $GLOBALS["meret"]; $i++){
print $GLOBALS["verem"][$i].", ";
}
}
A kiir() függvényben meg nincs semmi különös, egyszerűen csak be kell járni a tömböt.
2.) Módosítsd az utolsó példaprogramot, hogy ne írjon a sorok végére vesszőt, úgy hogy továbbra is a foreach szerkezeteket használod! Vagyis ilyen kimenetet kellene produkálni:
HU: H, K, Sze, Cs, P, Szo, V
EN: M, Tu, W, Th, F, Sa, Su
DE: Mo, Di, Mi, Do, F, Sa, So
Mivel mindhárom tömbnek 7 eleme van, megtehetnénk, hogy a 6-os indexű elem után nem írunk ki vesszőt, de ha ezt nem használjuk ki, akkor általánosabb esetben is használható lesz a feladat megoldása. Például gondolkodhatunk úgy, hogy nem a $nap.", "-t írjuk ki a ciklusmagban, hanem a ", ".$nap-ot. Így az elemszámtól függetlenül, mindig az első elemnél kell vizsgálatot végezni, ott nem kell a vesszőt hozzáfűzni a $nap változóhoz:
$napok = array(
"HU" => array("H", "K", "Sze", "Cs", "P", "Szo", "V"),
"EN" => array("M", "Tu", "W", "Th", "F", "Sa", "Su"),
"DE" => array("Mo", "Di", "Mi", "Do", "F", "Sa", "So"),
);
foreach ($napok as $kod => $nyelv){
print $kod.": ";
$n = 0;
foreach ($nyelv as $nap){
print ($n == 0) ? $nap : ", ".$nap;
++$n;
}
print "<br />";
}
A kiírásnál a feltételes operátort használtam annak eldöntésére, hogy mit kéne kiírni. Ha $n == 0, akkor a $nap lesz kiírva, a többi esetben a ", ".$nap.







