Az Adattípusok PHP-ben című leírásban végignéztük, hogy egy-egy kifejezés milyen típusú lehet; néhány helyen jeleztem, hogy majd lesz szó a típus-konverzióról is, mert ez egy fontos – ám sajnos szintén gyakran elfeledett – témakör.
A PHP nem teszi lehetőve a kifejezések típusának explicit meghatározását. Akinek ez a mondat most nem teljesen egyértelmű egy egyszerű C program példáján elmagyaráznám:
1 2 3 4 5 6 7 8 | #include <iostream> using namespace std; int main() { int szam = 5; cout << "Helo Vilag!" << szam; return 0; } |
A kód részletezésébe nem mennék bele, össz-vissz a „Helo Vilag!5” szöveget írja ki a képernyőre. Két – számunkra most fontos – dologra hívnám fel a figyelmet:
1 2 | int main() int szam = 5; |
Mindkét sorban ismétlődik az int kifejezés. Ez határozza meg a kifejezés (itt: függvény (main) és változó (szam)) lehetséges értékkészletét: egész számok.
A fenti kódban például a main() nem térhet vissza csak egész számmal, a szam változóban pedig nem tárolhatunk pl. karakterláncot. Egy kicsit módosítva a kódot azonban igen.
1 | char szam[12] = "Helo Vilag!"; |
Ekkor már a szam változónk a „Helo Vilag!” karakterláncot tartalmazza, így a „Helo Vilag!” kifejezés már kétszer kerül a képernyőre. Ehhez azonban meg kellett változtatnunk a változó típusát int-ről char-ra.
Nos, ilyen nincs a PHP-ben, mivel az nem támogatja az explicit típus-meghatározást: a változóink típusa legtöbbször azok tartalmától függ. Ez persze azt eredményezi, hogy állandóan ellenőrizgetnünk kell, hogy adott változó tartalma bizonyos típusba tartozik-e. Másik megoldásként átalakíthatjuk a megfelelő típusra, ha ismerjük a konverziók következményeit.
A különböző típusok között több módszerrel is konvertálhatunk, ezek közül a legegyszerűbb, hogy a kívánt típus nevét zárójelben a kifejezés elé tesszük. Például:
1 2 | $foo = 12; $foo = (string)$foo; /* foo mostantól karakterlánc típusú, értéke "12" */ |
Használhatunk továbbá átalakító függvényeket (intval, floatval, strval, settype), és jónéhány egyéb megoldást (pl.: idézőjelben használt érték karakterlánccá alakul)
A PHP rengeteg esetben automatikusan elvégzi ezeket a konverziókat (pl.: érték-összehasonlító operátorok használatakor (== és !=), függvények paraméterezésekor, …), így még fontosabb, hogy tisztában legyünk ezzekkel a szabályokkal.
Mondok egy példát:
1 | ('http://www.tutorial.hu/node/989' == true); |
Ez a kifejezés igaz-ként fog kiértékelődni, annak ellenére, hogy ezen cikk url-e nem igazán egyezik meg a bool igaz értékkel. Azonban – az automatikus konverzió miatt – a nem üres karakterlánc (lásd később!) igazként értékelődik ki, így az egyenlőség igaz lesz. Amennyiben ez nem az elvárt működés, használhatjuk az érték és típus-összehasonlító operátorokat (=== vagy !==).
Összehasonlító operátorok a PHP kézikönyvben
Nézzük is végig típusonként, hogy mi miként értékelődik ki, bizonyos típussá alakítva!
A leírás során az adatok hitelességét ellenőrizendő mindig a következő szkriptet használom, gyorsan lehet vele ellenőrizni az állításaim:
1 2 3 4 5 6 7 8 | <?php $foo = ; var_dump((bool)$foo); var_dump((int)$foo); var_dump((float)$foo); var_dump((string)$foo); var_dump((array)$foo); var_dump((object)$foo); |
A $foo változó természetesen mindig a vizsgálni kívánt típusba tartozó értéket kell, hogy hordozzon.
Tartalomjegyzék
Null
A null érték bármivé alakítva hamis-ként (0 v. üres sztring, tömb, objektum) kerül kiértékelésre, mivel ez ugye egy adat hiányát jelöli.
Bool
Az adattípus egyszerűségét tekintve a konverzió sem túl bonyolult. A True (igaz) érték 1-ként, a False (hamis) érték 0-ként értékelődik ki.
Skalár értékké (egész szám, lebegő pontos szám, karakterlánc) alakításkor 0-val ill. 1-el megegyező értéket vesz fel (az új adattípussal).
Tömbbé alakításkor a 0. indexű tömbelem egy boolean false v. true értéket fog tárolni. Objektummá alakításkor egy StdClass osztályba tartozó objektum jön létre, melynek scalar tulajdonsága lesz – továbbra is boolean típusú – igaz v. hamis.
Integer
Szintén egyszerű az átalakítás folyamat. Boolean típussá alakításakor a 0 értékű integer értékelődik ki false-ként, minden más esetben igaz-ként értékelődik ki.
1 2 3 4 | var_dump((bool)515); /* bool(true) */ var_dump((bool)1); /* bool(true) */ var_dump((bool)-13); /* bool(true) */ var_dump((bool)0); /* bool(false) */ |
Lebegőpontos és karakterlánc értékké alakításakor értéke ugyanaz az egész szám marad, csak típusa változik.
Tömbbé és objektummá alakításkor szintén a 0. indexű elem és az StdClass osztály scalar tulajdonsága tartalmazza az int típusú értéket.
Lebegőpontos számok
A bool típussá alakítás mikéntje megegyezik az integerével. Integer típussá alakításakor az érték egész részét veszi fel.
1 2 3 | var_dump((int)515.12); /* int(515) */ var_dump((int)1.1); /* int(1) */ var_dump((int)-13.24); /* int(-13) */ |
Karakterláncá alakításkor csak a típusa változik, tömbbé és objektummá alakításkor szintén a 0. indexű elem és a scalar tulajdonság tárolja az értéket (amely itt float típusú lesz)
Karakterláncok
A karakterláncok átalakítása már kissé problémásabb…
Boolean értékké kovertáláskor az üres karakterlánc („”) értékelődik ki hamisként, a nem üres karakterláncok pedig igazként.
Integerré és lebefőpontos számmá alakítás egy fokkal bonyolultabb.
Először minden láthatatlan (whitespace) karaktert eltüntet a karakterlánc elejéről (mint az ltrim függvény). Ezután a karakterlánc elejétől sorra végigveszi a karaktereket, az első nem szám és pont (jelen esetben a tizedesvessző) karakterig, majd ezt az értéket adja vissza (Egész számnál csak szám).
Bonyolultan hangzik, talán egy példán keresztül jobban érthető lesz:
1 2 3 | $szoveg = "\t\t\r\n\t12.432Akkor itt még legyen egy kis 3l33t sz0v3g"; var_dump((int)$szoveg); /* int(12) */ var_dump((float)$szoveg); /* float(12.432) */ |
A \t, \r és \n a tabulátor és újsor karakterek, amelyek whitespace karakternek minősülnek, így nem kerülnek értelmezésre. Ezután jön a 12, majd egy pont. A pont megszakítja az egészszámmá alakítást, így lesz ez az érték 12. Mikor lebegőpontos számmá alakítjuk tovább halad és az első nem-numerikus értéknél (A) áll meg. A tizedes-vessző után már újabb pontnál szintén megszakad az alakítás, például:
1 2 | $szoveg = "12.432.423"; var_dump((float)$szoveg); /* float(12.432); */ |
Tömbbé és objektummá konvertáláskor szintén a 0. indexű elem és scalar tulajdonság tartalmazza az értéket.
Tömb
A tömbök átalakítása sem túl probléma-mentes.
Bool típussá alakításkor az üres tömb hamis, míg a nem üres tömb igaz értékké alakul. Integerré és egész számmá alakításkor az elv hasonló, csak az értékeknek megfelelő 0 ill. 1 értékekkel. Karakterlánccá alakításkor mindig az Array karakterláncként fog kiértékelődni.
A probléma az objektummá alakításkor van. Ilyenkor szintén az StdClass egy példányává alakul, ám a korábbiaktól eltérően nem egy tulajdonság tartalmazza az értéket, hanem a kulcs => érték párosok alakulnak tulajdonság => érték párosokká. Például:
1 2 3 | $auto = array('company' => 'Ford', 'modell' => 'T-Modell', 'year' => 1908); $auto = (object)$auto; print 'A ' . $auto->company . ' ' . $auto->modell . ' gyártása ' . $auto->year . '-ben kezdődött el.'; |
Ez – ahogy elvárjuk – az „A Ford T-Modell gyártása 1908-ben kezdodött el.” szöveget fogja kiírni. A probléma az indexelt tömböknél jön elő: a PHP-ben az $objektum->0 szintaxis hibát generál, így nem használható. Azonban – egy kerülő megoldással – ezeket is elérhetjük:
1 2 3 4 | $autok = array('T-modell', 'Galaxie'); $autok = (object)$autok; $x = 0; print 'Ford ' . $autok->$x; |
Folytonos számozás esetén ismeretlen számú elemet is végignézhetünk:
1 2 3 4 5 6 7 8 | $autok = array('T-modell', 'Galaxie'); $autok = (object)$autok; $x = 0; while(IsSet($autok->$x)) { print 'Ford ' . $autok->$x; $x++; } |
(Ez a probléma a PHP 5-ben az objektumokra is elérhetővé tett foreach-el megszűnik)
Objektumok
Objektumokra is érvényesek a tömböknél leírt szabályok, ám ezek átalakítása már problémamentesebb.
Üres – tulajdonságokkal nem rendelkező – objektumok false-ként (ill. 0-ként) értékelődnek ki bool, int valamint float konverziókor. Azonban lássuk be, ez ritka eset.
1 2 3 4 5 6 7 8 9 10 | class foo { } $foo = new foo(); var_dump((bool)$foo); /* bool(false) */ class fooBar extends foo { var $bar; } $foo = new fooBar(); var_dump((bool)$foo); /* bool(true) */ $foo = new stdClass(); var_dump((bool)$foo); /* bool(false) */ $foo->bar = 'FooBar'; var_dump((bool)$foo); /* bool(true) */ |
Mint látható amint van valamilyen tulajdonság (a második esetben a fooBar osztály $bar tulajdonsága, a negyedikben az StdClass osztályú objektumhoz rendelt bar tulajdonság) igazként (1) értékelődik ki az objektum átalakítás után.
Karakterlánccá alakításkor az „Object” szöveggé alakulnak az objektumok.
A PHP 5 bevezetett egy mágikus metódust, a __toString()-et. Ha közvetlenül print-el vagy echo-val használjuk az objektumot, akkor a __toString meghívásra kerül, és a visszaadott érték kerül a kimenetre. Fontos azonban megjegyezni – mint ahogy a kézikönyvben is kifejtik a példákkal -, hogy csak közvetlenül meghívva, és csak az echo ill. print nyelvi elemnél működik ez a mágikus metódus.
1 2 3 4 5 6 7 8 9 10 | class foo() { function __toString() { return 'Bar'; } } $foo = new foo(); print $foo; print (string)$foo; |
Ez PHP 4 alatt az „ObjectObject” szöveget írja ki, míg PHP 5 alatt a „BarObject id #1” kimenetet adja. Itt is használhatunk kerülő megoldásokat, ezek közül egy:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function oOutPut() { $array = func_get_args(); while(list($key, $value) = each($array)) { if(!Is_object($value)) { print (string)$value; } else { if(method_exists($value, '__toString')) { print $value->__toString(); } else { print "Object"; } } } } |
Ez a kód PHP 4 és 5 alatt egyaránt működik, és mindenhol ugyanazt fogja visszaadni (nincs több Object id! :P ). Ráadásul a sima print-es ill. echo-s megoldásnál – bár lassabb – jobban használható.
1 2 3 4 5 6 7 8 9 10 | class ize { function __toString() { return 'Én egy izé vagyok'; } } $foo = new ize(); $bar = new StadClass(); oOutPut('$foo mondja:', $foo, '$bar mondja:', $bar); |
A kimenete: „$foo mondja:Én egy izé vagyok <br>$bar mondja:Object” lesz PHP 4 és 5 alatt egyaránt.
Az objektumok tömbbé alakítása már jóval egyszerűbb, a tulajdonság = érték párosok kulcs => érték párosokká alakulnak.
Ezzel rövid (?) összefoglalónk végére értünk, a következőben kifejtem, hogy ezeket hol és mire tudjuk kihasználni, és hol érdemes figyelni az automatikus konverzió eredményeire…