Autor Zpráva
Joker
Profil
Autor: Joker
Odkaz na aktuální verzi textu: Základní kurz 7: Přetypování
zveřejněno
Odkud se vede diskuse k aktuálnímu stavu textu:
První příspěvek po poslední revizi textu
Joker
Profil
První verze textu
PHP - základní kurz

Přetypování


V [odkaz]předchozí kapitole[/odkaz] jsme se seznámili s proměnnými a datovými typy. V této kapitole si ukážeme, jak
lze hodnoty převádět na jiné datové typy. V některých situacích se hodnoty mohou přetypovat automaticky aniž by si to
programátor vyžádal nějakým zvláštním příkazem, je tedy potřeba znát, za jaké situace a podle jakých pravidel se hodnoty
přetypují.

Vynucené přetypování

Ukažme si nejdříve přetypování vynucené programátorem, které je vcelku přímočaré.
Prostě se to, na co chceme přetypovat, napíše do závorky před hodnotu nebo proměnnou, kterou chceme přetypovat.
Možnosti přetypování jsou tyto:
- (string) přetypuje na řetězec
- (boolean) nebo (bool) přetypuje na logickou hodnotu
- (integer) nebo (int) přetypuje na celé číslo
- (real), (float) nebo (double) přetypuje na reálné číslo
- (array) přetypuje na pole. O polích si povíme později
- (object) přetypuje na objekt. Objekty jsou nad rámec tohoto základního kurzu, ale po jeho dokončení si
o nich můžete přečíst v sekci o objektovém programování.
- (unset) přetypuje na NULL. Prakticky zbytečné, místo [pre]$nic = (unset)"něco";[pre] můžete psát rovnou
$nic = NULL;. Jen si pamatujte, že (unset) je něco jiného, než funkce unset() z minulé
kapitoly. Zatímco unset($promenna); zruší danou proměnnou, (unset)$promenna jen vrátí NULL
a proměnná zůstane beze změny.

Ukázka přetypování:
<?php
$cislo = 1; // číslo
$retezec = (string) 1; // číslo přetypované na řetězec
 // Necháme si vypsat hodnoty proměnných:
echo 'hodnota $cislo: ';
var_dump($cislo);
echo 'hodnota $retezec: ';
var_dump($retezec);
?>

Vypíše, že hodnota $cislo je 1 typu int (celé číslo) a hodnota $retezec je '1' typu string (řetězec).
Přetypování proměnné jen vrátí hodnotu nového datového typu, ale nezmění samotnou proměnnou. Chcete-li na nový
datový typ změnit hodnotu proměnné, musíte pak výsledek přiřadit zpátky do té proměnné.
<?php
$cislo = 1; // číslo
$retezec = (string) $cislo; // $retezec bude řetězec "1", ale $cislo bude pořád číslo
$cislo = (float)$cislo; // Takto se změní datový typ samotné proměnné

Jak se co přetypuje?

Možná vás teď napadlo, že ne všechna přetypování mají intuitivní výsledek, například:
$hodnota = (int)"Ahoj!";

Pravidla přetypování jsou tato:
- Přetypování na řetězec (string):
Číslo se jednoduše převede na odpovídající řetězec (třeba 15 na "15"). Dejte jen pozor, že u reálných čísel nemusí
být výsledekem číslo "hezky formátované". Jednak desetinná tečka versus čárka a jednak velmi vysoká nebo velmi malá
čísla jsou zapsána v tzv. exponenciálním tvaru: Jde vlastně o zápis používaný třeba ve fyzice, kdy se např. číslo
1999000000000 zapíše jako 1,999 x 10^12. zde se jen vynechá to "krát deset na" a místo toho je písmeno "E".
Čili například (string)100000000000000 vrátí řetězec "1.0E14", nebo (string)0.000036 vrátí
řetězec "3.6E-5".

- Přetypování na celé číslo (int):
Z reálného čísla se bere celá část (takže se nezaokrouhluje), například 17.9 se převede na 17.
Při konverzi výsledku nějakého výpočtu ale nezapomeňte na specifika ukládání reálných čísel, jak je zmíněno v minulé
kapitole. Výsledek výpočtu s reálnými čísly bude v paměti uložen jako nejbližší uložitelné reálné číslo, které se tedy
může nepatrně lišit od skutečného výsledku. To ale znamená, že přesně celočíselný výsledek výpočtu může být uložený jako
nepatrně nižší reálné číslo. A jelikož převod na celé číslo bere celou část, může pak výsledkem být číslo o 1 nižší.
Příklad:
<?php
$vysledek = (0.7 + 0.1) * 10;
var_dump($vysledek); // $vysledek je 8 typu float
$vysledek = (int)$vysledek; // přetypujeme na celé číslo
var_dump($vysledek); // $vysledek není 8, ale 7 typu int
?>
Naopak logické hodnoty jsou nezáludné, true se převede na 1 a false na 0.
U řetězce se jde od jeho začátku tak dlouho, dokud ho lze interpretovat jako číslo a případný zbytek řetězce se ignoruje.
Pokud začátek řetězce vůbec nejde převést na číslo (nebo je řetězec prázdný), je výsledkem 0.
Například (int)"-15" je -15, (int)"10Kč" je 10, (int)"Vlak s 10 vagóny" je 0.
Rozpozná i výše zmíněný exponenciální tvar, tedy např. "1.2e3" se převede na 1200.

- Přetypování na reálné číslo (real/double/float)
Analogické k přetypování na celé číslo, jen méně záludné.

- Přetypování na logickou hodnotu (bool)
Pokud jde o číslo, převede se 0 na false, cokoli jiného na true. V případě řetězce se prázdný řetězec převede na false,
všechno ostatní na true. Pozor, znamená to, že i řetězec "false" se převede na true!
Neosvojujte si proto zvyk některých začátečníků psát uvozovky kolem všech hodnot, nejen řetězců.

- Krátká poznámka k polím a objektům
Jak už je napsáno výše, polím se bude věnovat pozdější kapitola a objektům až další část tohoto webu po skončení
základního kurzu. Když je ale řeč o přetypování, pro úplnost zmiňme, že přetypování polí či objektů na jiné typy, ani
přetypování jiných datových typů na pole či objekty obvykle nedává valný smysl.
Pokus o přetypování objektu na jiný datový typ dokonce může skončit chybovou hláškou.

Skryté (automatické) přetypování

Kromě přetypování vynuceného programátorem jsou v PHP i situace, kdy se datový typ hodnoty změní automaticky důsledkem
nějakého výpočtu.
I když to není úplně přetypování, o kterém jsme mluvili výše, připomeňme si z minula, že datový typ proměnné je určený
její hodnotou. Přiřazení hodnoty jiného typu tedy změní datový typ proměnné.
<?php
$promenna = 1; // $promenna je typu integer (celé číslo)
$promenna = "ahoj"; // $promenna je nyní typu string (řetězec)
?>

Z předchozí kapitoly o datových typech dále víte, že celé číslo, které je příliš vysoké (nebo příliš nízké záporné),
než aby šlo uložit do typu integer, se automaticky převede na typ float (a pokud se nevejde ani do jeho rozsahu, změní
se na speciální hodnotu INF, reprezentující nekonečno).
Příklad:
var_dump(99*99);
var_dump(9999999999*9999999999);
Zatímco výsledkem prvního výpočtu bude normálně 9801 typu int, výsledkem druhého výpočtu je 9.999999998E+19 typu float,
což znamená 9.999999998 * 10^19, neboli 99999999980000000000. Všimněte si, že ve skutečnosti má výsledek toho
součinu být 99999999980000000001. Opět jde o nepřesnost kvůli způsobu uložení reálných čísel, o které jsme už mluvili.
Důležitý poznatek tedy je, že nepřesnost při uložení reálných čísel není pevná, ale závisí na velikosti uloženého čísla.
Přesně se uloží prvních 15 cifer čísla.
To může vést k paradoxním situacím:
$soucin = 99999999980000000000;
$soucet = $soucin + 100;
var_dump($soucin);
var_dump($soucet)
Všimněte si, že ačkoli proměnná $soucet vznikla přičtením 100 k proměnné $soucin, obsahují obě proměnné stejné číslo!
A to dokonce přesto, že ve skriptu máme pouze celá čísla. Je to způsobené mechanismem popsaným výše, tedy příliš
vysoké celé číslo se automaticky převedlo na reálné. Protože řád prvního čísla je 10^19, jakékoli změny menšího řádu
než 10^4 (tedy desetitisíce) nemusejí dávat přesné výsledky.

Také operandy ve výrazech se automaticky přetypují na datový typ, který je vyžadován použitým operátorem.
<?php
$vysledek = "10" + "1"; // $vysledek bude 11
?>
V příkladu výše je použit operátor sčítání (+), který očekává čísla, ale operandy jsou řetězece. Proto se převedou
na čísla, aby bylo možné spočítat výsledek.
Zároveň si zapamatujte, že operátorem + nejdou v PHP "sčítat", tedy spojovat, řetězce (jak ukazuje i příklad výše).
U proměnných opět platí, že se přetypuje pouze hodnota použitá ve výrazu, ale ne hodnota samotné proměnné:
<?php
$vstup = "5";
$vysledek = $vstup + 1;
// $vysledek je číslo (integer) 6, v $vstup zůstane řetězec "5"
?>

Také příkazy jako echo, pokud dostanou hodnotu jiného datového typu než očekávají, ji přetypují.
Příklad:
<?php
$logicka = true;
echo $logicka;
?>

Vypíše 1, protože echo očekává řetězec a logická hodnota se přetypuje na řetězec "1".
Když v příkladu použijete false místo true, nevypíše se nic (false se převádí na prázdný řetězec, vzpomínáte?)

Jiné techniky převodu datových typů

Poměrně často se stává, že potřebujete změnit datový typ, ale podle jiných pravidel, než těch výše uvedených.
Například potřebujete převádět mezi logickou hodnotou a řetězcem tak, aby hodnota true odpovídala řetězci "ano"
a hodnota false řetězci "ne". Tato situace je možná častější, než použití standardního přetypování. Takovéto podmínky
je ovšem potřeba odprogramovat přímo v kódu.
Některá možná řešení si ukážeme v následujících kapitolách.

Shrnutí

Datový typ hodnoty může změnit sám programátor příkazem pro přetypování, nebo se datový typ může změnit automaticky
během výpočtu, pokud operátor nebo příkaz vyžaduje jiný datový typ, než hodnota má.
Také funkce většinou u svých argumentů počítají s nějakým datovým typem, ačkoliv se při definici argumentů datový typ
nespecifikuje (PHP ostatně ani u proměnných nemá pevné datové typy). Při práci s argumentem uvnitř funkce se hodnota
obvykle přetypuje. Některé funkce si typ argumentů kontrolují a je-li předaná hodnota špatného typu (nebo nejde
přetypovat na ten správný), mohou vyhodit chybovou hlášku

Jak již víme z předchozí kapitoly, celá čísla vyšší než dokáže pojmout datový typ integer se automaticky přetypují na
reálná čísla (float), která trpí určitou nepřesností vlivem způsobu uložení reálných čísel v paměti. Přitom tato
nepřesnost roste s hodnotou uloženého čísla, takže je dobré na to dát pozor, pokud byste například sčítali velmi
vysoká čísla s velmi nízkými.
Často je potřeba převést datové typy mezi sebou podle jiných pravidel, než standardní PHP přetypování. Toto je nutné
odprogramovat ve skriptu a některé příklady si ukážeme později v tomto se
Alphard
Profil
Máte někde zmíněné funkce jako is_numeric()? U proměnných je vyhledávání nenašlo, tady taky nejsou. Nebo se nebudou uvádět?

Větu „Pokus o přetypování objektu na jiný datový typ dokonce může skončit chybovou hláškou.“ bych možná formuloval jinak. Tohle může působit tak, že skrytí hlášky (nebo když se kvůli odlišnému nastavení neobjeví) řeší problém.
Jan Tvrdík
Profil
Doplnit za první výskyt slova „přetypovat“ jeho význam. Možná by se hodilo stručně uvést funkce gettype a settype. Možná ale zase ne, v praxi se moc nepoužívají.
Joker
Profil
Alphard:
Jo, dobrá poznámka, já myslel, že to je už u proměnných a datových typů, ale není.

Větu ‚Pokus o přetypování objektu na jiný datový typ dokonce může skončit chybovou hláškou.‘ bych možná formuloval jinak.
Mno, upřímně řečeno, já si sám nejsem úplně jistý, jak to vlastně je.
Přetypovávat objekty na něco jiného nemá moc smysl, snad nikdy jsem to v reálném programu nedělal a popis v manuálu nejspíš neodpovídá realitě.
Na to, že některá ta přetypování nelze provést (přestože manuál tvrdí že lze) jsem přišel jen díky tomu, že jsem si to pro jistotu ještě ověřoval na reálných kódech.
Takže manévrovací prostor pro správný popis situace nemám velký :)

Jan Tvrdík:
Díky, ještě to zvážím
panther
Profil
Jan Tvrdík:
Možná by se hodilo stručně uvést funkce gettype a settype.
o těchto bych se asi nezmiňoval, tak často se nepoužívají a nemyslím, že je nutné je v základním kurzu zmiňovat.

Doplnit za první výskyt slova ‚přetypovat‘ jeho význam
význam je uveden před ním - „V této kapitole si ukážeme, jak lze hodnoty převádět na jiné datové typy. V některých situacích se hodnoty mohou přetypovat“. Více to asi není třeba rozepisovat, co se přetypování jako slova týče.
Norman
Profil
Velice pěkně sepsáno ...


Tak jestli můžu dodat pár poznámek (jestli ne smažte to..):

- [odkaz]předchozí kapitole[/odkaz] // napravil bych :)
- [pre]$nic = (unset)"něco";[pre] // taktéž

- I když nevím jestli to sem tak úplně patří, nicméně určitě je dle mého vhodné s OOP seznámit hned od začátku
Pěkný příklad má na webu Jakub (http://php.vrana.cz/objektove-literaly-v-php.php)

    $obj = (object) array("vlastnost1" => "hodnota", "vlastnost2" => "hodnota");
  
panther
Profil
Norman:
- [odkaz]předchozí kapitole[/odkaz] // napravil bych :)
to je jen symbolický zápis, později z toho budou skutečné odkazy a obarvený kód.

určitě je dle mého vhodné s OOP seznámit hned od začátku
to si nemyslím. Začátečník v této fázi nemá pořádně pojem o tom, co je proměnná.

$obj = (object) array("vlastnost1" => "hodnota", "vlastnost2" => "hodnota");
zrovna tento kousek řádku, který jsi odcitovat, smysl nedává.

Objektům musí být věnována samostatná kapitola, nejde o nich psát jen tak mimochodem.
Norman
Profil
ok, omlouvám se, moje chyba :)
Joker
Profil
Norman:
Ty značky v hranatých závorkách se upraví před zveřejněním článku.

určitě je dle mého vhodné s OOP seznámit hned od začátku
V základním kurzu jde hlavně o pozorumění syntaxi a hlavním pravidlům tvorby efektivního a přehledného kódu, což se samozřejmě pak hodí i v kurzu OOP. Bude ale vhodné ty základy udržet co nejjednodušší, takže bych do toho nemotal ještě výuku teorie objektově-orientovaného programování.

Navíc přetypování mezi objektem a jiným datovým typem je něco tak exotického, že to v základním kurzu snad ani nemá cenu rozebírat. Uvedený příklad je užitečný jen v hodně specifických situacích, do kterých se začátečník nejspíš nedostane.
Joker
Profil
Přidal jsem na Péhápko.
Tři články zveřejněné během jednoho týdne, to se snad ještě nepovedlo :-)

Vaše odpověď

Mohlo by se hodit

Ostrá verze učebnice běží na www.pehapko.cz.

Odkud se sem odkazuje


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: