6. diel - Referenčnej a primitívne dátové typy v PHP
V predchádzajúcom kvíze, Kvíz - Úvod, triedy a zapuzdrenie v PHP OOP, sme si overili nadobudnuté skúsenosti z predchádzajúcich lekcií.
V minulej lekcii, Kvíz - Úvod, triedy a zapuzdrenie v PHP OOP , sme si vytvorili prvú objektovú komponent, jednalo sa o galériu obrázkov. Začíname pracovať s objektmi a objekty sú referenčnými dátovými typy, ktoré sa v niektorých ohľadoch správajú inak, než tzv. Typy primitívne. Je dôležité, aby sme presne vedeli, čo sa vo vnútri programu deje, inak by nás v budúcnosti mohlo všeličo prekvapiť. Tejto problematike sa budeme v dnešnom PHP tutoriálu venovať.
Primitívne dátové typy
Premenné primitívneho dátového typu sme používali doteraz, ide napr. O číslo alebo o pole. Aj keď primitívne typy v iných jazykoch označujú iba jednoduché štruktúry, ako sú napr. Čísla alebo znaky (od toho názov primitívne), v PHP je primitívnym typom aj polia alebo textový reťazec. Primitívne typy sú vlastne všetky dátové typy okrem objektov.
Kopírovanie hodnoty
Ak niekam odovzdáme primitívne typ, jeho hodnota sa vždy skopíruje. Hoci to určite viete, skúsme si to. Založme si bokom nejaký skript a vytvorme v ňom nové pole s jednou hodnotou 56:
$a = array(56);
Pole $ a teraz pridelené do poľa $ b:
$b = $a;
Do poľa $ b pridajme ešte ďalšie prvok:
$b[] = 28;
Nakoniec Vypíšme obe polia:
{PHP}
$a = array(56);
$b = $a;
$b[] = 28;
print_r($a);
print_r($b);
{/PHP}
Keď si teraz zobrazíme zdrojový kód výslednej stránky, výstup bude pravdepodobne tak, ako ste ho očakávali:
Hoci sme zmenili obsah premennej $ b, jej pôvodná hodnota zostala skopírovaná v premennej $ a. Výsledkom sú teda 2 rôzne polia, prvé len s jednou hodnotou, druhé obsahujúci pôvodnú hodnotu + tú novú.
Referenčnej dátové typy
S referenčnými dátovými typmi, alebo ak chcete s objektmi, je pri odovzdávaní pracované iným spôsobom. Skúsme si vytvoriť podobnú situáciu, ale namiesto čísel ukladajte objekty.
K tomu nám perfektne poslúži inštancie našich ľudí. Vytvorme si analogicky premennú $ as inštancií človeka:
require_once('tridy/Clovek.php'); $a = new Clovek('Karel', 'Novák', 30);
Teraz opäť rozdelia do premennej $ b obsah premennej $ a:
$b = $a;
Opäť po priradení zmeňme hodnotu premennej $ b, tu tak, že užívateľovi v premennej $ b zmeníme vek:
$b->vek = 50;
Opäť vypíšeme obe premenné:
{PHP} require_once('tridy/Clovek.php'); $a = new Clovek('Karel', 'Novák', 30); $b = $a; $b->vek = 50; print_r($a); print_r($b); {/PHP}
{PHP} class Clovek { public $jmeno; public $prijmeni; public $vek; private $unava = 0; public function __construct($jmeno, $prijmeni, $vek) { $this->jmeno = $jmeno; $this->prijmeni = $prijmeni; $this->vek = $vek; } public function spi($doba) { $this->unava -= $doba * 10; if ($this->unava < 0) $this->unava = 0; } public function behej($vzdalenost) { if ($this->unava + $vzdalenost <= 20) $this->unava += $vzdalenost; else echo('Jsem příliš unavený.'); } public function pozdrav() { echo('Ahoj, já jsem ' . $this->jmeno); } public function __toString() { return $this->jmeno; } }
A hľa, niečo je inak. Mali by ste vidieť takýto výsledok:
Rekapitulácia
Vytvorili sme si inštanciu človeka a tú sme uložili do premennej $ a. Inštancie a premenná sú však 2 rozdielne veci a to z toho dôvodu, že inštancia nie je priamo uložená v premennej.
Instance objektov sú uložené v pamäti, ktoré sa hovorí halda (anglicky heap). Táto pamäť nie je veľkostne obmedzená (zmestí sa do nej koľko povolíte alebo koľko máte RAM). Túto pamäť si môžeme jednoducho predstaviť ako veľkú haldu objektov. V premennej $ a je potom uložená iba tzv. Referencie, ktorá ukazuje na inštanciu na halde.
Akonáhle sme teda do premennej $ b priradili premennú $ a, povedali sme, že $ b ukazuje na tú istú inštanciu, ako $ a. Celú situáciu som pre vás vizualizoval, aby bola lepšia k pochopeniu:
Na obrázku je znázornená halda s objektmi a premenné, ktoré obsahujú referenciu na tieto objekty. Vidíme, že premenné $ ai $ b ukazujú na ten istý objekt. Z toho dôvodu sa zmenil obsah premennej $ a vo chvíli, keď sme zmenili $ b. Človeka tam máme na rozdiel od predchádzajúcej situácie s poľami iba jedného.
Práca s referenciami je prirodzenejší a mnohokrát aj rýchlejší. Objekt stačí vytvoriť raz a potom ho referencií odovzdávať tam, kde je potreba. Keď chcete v PHP použiť poľa na viacerých miestach, vždy sa jeho obsah kopíruje, čo môže byť pri väčších dátach nevýhodné. Odovzdanie obrovského objektu je úplne nenáročná operácia, pretože sa odovzdáva len referencie. Časom určite uznáte aj to, že je to pre človeka prirodzenejšie a aplikácie sa potom aj lepšie navrhuje. V realite sa nám totiž veci tiež neklonují (väčšinou ).
Pozn .: Väčšina programovacích jazykov pracuje Referenčné is poli a textovými reťazcami. Je to hlavne z toho dôvodu, že primitívne typy sú ukladané v pamäti zvanej zásobník (stack). Tá je veľmi rýchla, ale jej veľkosť je tiež veľmi obmedzená. Zložitejšie dátové typy (obyčajne s neobmedzenou dĺžkou) sú vždy ukladané do haldy a vo stacku je umiestnená len referencie na ne. PHP tento fakt pravdepodobne vnútorne obchádza a ako interpreter zachováva správanie primitívnych dátových typov iu polí, aj keď sú fyzicky tiež uložené na halde.
Vynútenie referencie
Ako už bolo povedané, PHP pracuje so všetkým okrem objektov ako s primitívnym typom a to tak, že v prípade odovzdania hodnotu vždy skopíruje. To je niekedy výhodné, ale niekedy zase nevýhodné. Z toho dôvodu existuje možnosť, ako odovzdať premenné primitívneho typu (napr. Pole) referencií. Hneď na úvod by som chcel povedať, aby ste sa použitie týchto hack skôr vyvarovali, pretože mení princíp akým PHP funguje v predvolenom stave a keď na to zabudnete, môže vás to niekedy veľa prekvapiť a spôsobiť veľa nepríjemností. Uvádzam je tu teda skôr pre úplnosť.
Odovzdanie premenné parametrom
Predstavte si, že máte funkciu, po ktorej chcete, aby niečo pridala do poľa, ktoré odovzdáte v jej parametra. Naivné implementácia by mohla vyzerať takto:
// Tento kód nefunguje function pridej($pole, $prvek) { $pole[] = $prvek; }
Už pre vás však nebude prekvapením, že tento kód do poľa $ a nič nepridá:
{PHP}
// Tento kód nefunguje
function pridej($pole, $prvek)
{
$pole[] = $prvek;
}
$a = array(1, 2, 3);
pridej($a, 4);
print_r($a);
{/PHP}
Je to samozrejme z toho dôvodu, že pole $ a sa skopíruje do premennej $ poľa a do tej sa pridá ďalší prvok. Pôvodný premenná $ a sa však vôbec nezmení. U funkcie môžeme vynútiť referenčnú prístup (ako u objektov) pomocou operátora "&":
{PHP}
function pridej(&$pole, $prvek)
{
$pole[] = $prvek;
}
$a = array(1, 2, 3);
pridej($a, 4);
print_r($a);
{/PHP}
Kód bude teraz fungovať, avšak je to skôr taký hack a nie je to príliš pekné. Keby sme napísali kód objektovo (všimnite si, že je to funkcia a nie metóda), tak by sme takéto veci nepotrebovali. Pole by totiž bolo atribútom tohto objektu a nebol by žiadny problém s tým ho modifikovať. Navyše by sme si ušetrili atribút:
public function pridej($prvek) { $this->pole[] = $prvek; }
Vidíme, ako objekty zjednodušujú situáciu a predchádza neprehľadnému kódu. Neobjektovým riešením by mohlo byť vrátiť modifikované pole pomocou return a potom ho priradiť späť do premennej $ a. Celé pole sa nám tak ale už 2x skopíruje, takže to nie je najvýhodnejšia.
Modifikácie poľa v cykle
Vynútiť referenciu sa nám môže hodiť v prípade, keď prechádzame poľa foreach cyklom a potrebujeme meniť hodnoty. Tento kód nebude fungovať:
{PHP}
// Tento kód nebude fungovat
$a = array(1, 2, 3);
foreach ($a as $prvek)
{
$prvek++;
}
print_r($a);
{/PHP}
Prvok poľa sa skopíruje (ak to nie je objekt) do premennej $ prvok a my meníme až tú, k samotnému prvku sa nedostaneme. S vynútením referencie by to vyzeralo takto:
{PHP}
$a = array(1, 2, 3);
foreach ($a as &$prvek)
{
$prvek++;
}
print_r($a);
{/PHP}
Prvok teraz ukazuje na pôvodnú prvok v poli a mení sa teda pole, nie len skopírovaný prvok. Riešenie je už menej mätúce než u funkcie a párkrát som ho použil, stále však ide obísť cez for cyklus alebo vstavané funkcie, ktoré nám PHP pre prácu s poľami ponúka.
V budúcej lekcii, Riešené úlohy k 4.-6. lekciu OOP v PHP , si uvedieme dedičnosť a polymorfizmus, ktoré patria spolu so skompletovaním k základným pilierom OOP.
V nasledujúcom cvičení, Riešené úlohy k 4.-6. lekciu OOP v PHP, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.