Objekty v PHP

Pre webové aplikácie je PHP (Hypertext Preprocessor) mocný moduláry systém. Tento skriptovací jazyk sa vykonáva na strane servra. S PHP vieme zhromažďovať dáta v databázach (MySQL, Oracle, atď.). Tvorba súborov nie je zložitá (PDF, XLS, DOC, atď.). Môžeme generovať a upravovať obrázky. Mnoho projektov funguje na PHP (Facebook, Wordpress, atď.).

1. Prečo Objekty?

1.1 Tvorba aplikácií

Pri procedurálnom programovaním je tvorba veľkých web aplikácií problém. Ak má na aplikácii pracovať viacero ľudí, pri procedurálnom programovaní klesá prehľadnosť kódu, duplikuje sa funkcionalita a dáta.

1.2 Udržiavanie a rozšírovanie aplikácie

Ak už je aplikácia hotová, je potrebné ju uržiavať, opravovať chyby, a niekedy rozširovať jej funkcionalitu. Pri procedurálnom programovaní jedna zmena zvyčajne vyžaduje aktualizáciu kódu na iných miestach. Potom sú zdojové kódy neprehľadné, a orientuje sa v nich len hŕstka nadšencov (hlavný problém pri Wordpress aplikácii).

1.3 Prínos OOP

Preto sa v PHP5 objavuje plná podpora objektovo orientovaného programovania, čím sa umožnilo:

2. Základy PHP

PHP je dynamicky typovaný procedurálny skriptovací jazyk. Jeho syntax je podobná s jazykmi Perl, Python, alebo C.

2.1 Vkladania PHP kódov

PHP kód môže byť uvádzaný niekoľkými sposobmi:

  <?php phpinfo(); ?>     // štandardný spôsob
  <? phpinfo(); ?>        // skrátený spôsob
  <% phpinfo(); %>        // Štýl ASP
  <script language="PHP"> // HTML štandard
    phpinfo();
  </script>

2.2 Definícia funkcií

Telo funkcií môže obsahovať akýkoľvek platný PHP kód. Vo funkcii môžeme definovať aj ďalšie funkcie. Predefinovanie alebo oddefinovanie takýchto funkcií nie je podľa štandardov PHP možné.

  function [&] meno([trieda1] [&] parameter1 [= hodnota1]
                    [trieda2] [&] parameter2 [= hodnota2]
                    ...){
    telo funkcie
    [return vysledna_hodnota;]
  }

2.3 Definícia premenných

Pri definovaí premenných prvým znakom musí byť podtržítko, alebo písmeno abecedy. Až potom môže nasledovať aj čísla. Veľké a malé písmená rozlišujú rôzne premenné. Pri deklarovaní nie je potrebné úvádzať dátový typ, deklarácia prebehne automaticky.

Superglobálne
premenné, sú dostupné všade v kóde PHP. Nemôžu byť vytvárané, existujú len ako preddefinované. Sú to polia $_COOKIE, $_ENV, $_FILES, $_GET, $_POST, $_REQUEST, $_SERVER, $_SESSION, $GLOBALS.
Globálne
premenné, sú všetky premenné, ktoré sú definované mimo funckií, alebo deklarované pomocou slova global vo vnútri funkcií. Všetky globálne premenné sú dostupné v $GLOBALS.
Lokálne
premenné definované iba v tele funkcie, a len v tejto funkcii sú dostupné.
 $premenna1 [= hodnota | & smernik];
 global $premenna2;

3 Triedy a objekty

V jazyku PHP je objekt inštanciou triedy. Vzťah je tu rovnaký ako v iných programovacích jazykoch (C++, Java, a pod.) Hoci je OOP podporované už od PHP4, o objektovom programovaní sa hovorí až od PHP5, pretože objektový model bol vo verzii 5 úplne zmenený.

3.1 Deklarácia triedy

V triede si môžeme deklarovať premenné, konštanty, funckie. Trieda môže rozšírovať svoju rodičovskú triedu, môže implementovať interface.

  [final|abstract] class Meno_triedy [extends trieda] [implements interface]{
    telo triedy
  }
  class myClass{/* Zatial tu nic nie je*/}
  
  $obj1 = new myClass();
  $obj2 = & $obj1; // toto je smernik na moj objekt
  $obj3 = new myClass();
 
  var_dump($obj1 instanceof myClass); //vrati true
  var_dump($obj2 instanceof myClass); //vrati true
 
  var_dump($obj1==$obj2);  // vrati true, lebo je to rovnaka trieda
  var_dump($obj1==$obj3);  // vrati true, lebo je to rovnaka trieda

  var_dump($obj1===$obj2); // vrati true, lebo je to rovnaky objekt
  var_dump($obj1===$obj3); // vrati false, lebo je to iny objekt

3.2 Deklarácia funkcií a premenných v triede

Funkcie a premenné v objektoch sú definované podobne ako klasické premenné, ale pri definícii môžeme uviesť aj typ prístupu k funkcii a premennej.

  [final|abstract] class meno_triedy [extends trieda] [implements interface]{
    const konstanta = hodnota1;
    [private|protected|public] [static] $premenna [= hodnota2];
    [private|protected|public] [static] [final] function funkcia(...){
      telo funkcie
      [return vysledna_hodnota;]
    }
  }
  class Osoba{
    protected $meno = null;
    protected $priezvisko = null;
    
    public function Osoba($meno=null, $priezvisko=null){
        $this->meno=$meno;
        $this->priezvisko=$priezvisko;
    }

    public function setMeno($meno){$this->meno = $meno;}
    public function getMeno($meno){return $this->meno;}
  
    public function setPriezvisko($priezv){$this->priezvisko = $priezv;}
    public function getPriezvisko($priezv){return $this->priezv;}
  }

  $vlado = new Osoba();
  $vlado->setMeno('vlado');
  $vlado->setPriezvisko('chabal');

3.3 Namespace v PHP

Od verzie PHP 5.3 je pridaná možnosť vytvárať triedy v namespace. Ohlasy sú na túto funkcionalitu rôzne. Niektorí vývojári píšu, že namespaceing len sťažuje robotu. Iní zase neuznávaju projekt, ak nebol vytvorený s namespace.

  namespace [meno]{
    funkcie, triedy, PHP kód
  }

Pre definovanie viacerých namespace-ov sa používa spätné lomítko. Ak chceme vytvoriť nový objekt, musíme uviesť celý namespace.

  namespace Kontakty\Zaklad{
    class Osoba{
      protected $meno = null;
      protected $priezvisko = null;

      public function setMeno($meno){$this->meno = $meno;}
      public function getMeno($meno){return $this->meno;}
  
      public function setPriezvisko($priezv){$this->priezvisko = $priezv;}
      public function getPriezvisko($priezv){return $this->priezv;}
    }
  }
  
  namespace MojProjekt{
    $vlado1 = new \Kontakty\Zaklad\Osoba();
    $vlado1->setMeno('vlado');
    $vlado1->setPriezvisko('chabal');
    
    use \Kontakty\Zaklad\Osoba as MojaOsoba;
    $vlado2 = new MojaOsoba();
    
    use \Kontakty\Zaklad\Osoba;
    $vlado2 = new Osoba();
  }

Poznámka: pri objektoch v namespace sa funkcia s rovnakým menom ako je názov triedy nespráva ako konštruktor! Ako vytvoriť konštruktor si pozri v 3.4 Magické funkcie .

3.4 Magické funkcie

Ak si vyskúšate definovať konštruktor predchádzajúcim spôsobom, tak zistíte že pôvodné definovanie konštruktora sa nebude správať ako konštruktor. Ako potom definovať konštruktor?

V PHP pre OOP existujú viaceré tzv. magické funkcie. Niektoré majú dôležitý význam, iné sú len syntaktický cukor. Pri používaní týchto funkcií sa nezlepšuje výkon.

__autoload(string $nazov_triedy)
Pri vytváraní objektu spôsobom new NazovTriedy() sa najprv zisťuje, či v aktuálnom kontexte existuje trieda NazovTriedy. Ak nie, volá sa táto funkcia.
__call($meno_funkcie, $pole_argumentov)
__callStatic($meno_funkcie, $pole_argumentov)
Pri každom volaní statickej/nestatickej funkcie nad objektom je volaná táto funkcia.
__clone()
Pri volaní clone($objekt); je volaná táto funkcia nad novým objektom
__construct($parametre, ...)
__destruct($parametre, ...)
Je volaná pri vytváraní nového objektu, resp. pri volaní unset($object);
__get($parameter)
__set($parameter, $hodnota)
Metody volané pri čítaní, a zapisovaní premenných objektu.
__isset($parameter)
__unset($parameter)
Tieto funkcie objektu sú volané pri isset($objekt->premenna); a pri volaní funkcie unset($objekt->premenna);.
__toString()
Je volaná pri konverzii objektu na reťazec;
  namespace Kontakty\Zaklad{
    class Osoba{
      protected static $_MENO = 'meno';
      protected static $_PRIEZVISKO = 'priezvisko'; 
      protected $data = array();

      public function __construct($meno=null,$priezvisko=null){
      	$this->data[self::$_MENO] = $meno;
      	$this->data[self::$_PRIEZVISKO] = $priezvisko;
      }
      
      public function __get($meno){
      	$this->_check($meno);
      	return $this->data[$meno];
      }
      
      public function __set($meno, $hodnota){
      	$this->_check($meno);
      	$this->data[$meno] = $hodnota;
      }
      
      public function __isset($meno){
      	return array_key_exists($meno, $this->data);
      }
      
      private function _check($meno){
      	if(!isset($this->$meno))
      		throw new \Exception("Premenna ".$meno." neexistuje!");
      }
    }
    use Kontakty\Zaklad\Osoba;	 
    
    $vlado = new Osoba();
    $vlado->meno='vlado';
    $vlado->priezvisko='chabal';
    
    var_dump($vlado);
  }

4. MVC návrhový vzor

Dizajnový vzor Model-View-Controller je známy takmer každému vývojárovi webových aplikácii. Ako správne používať MVC je neustály proces vzdelávania. Ale hlavná myšlienka ostáva vždy rovnaká: Oddelenie dát, html, a PHP kódu.

4.1 Model

Model reprezentuje nositeľa dát aplikácie. Obsahuje premenné, ktoré reprezentujú dáta, základnú logiku pre validáciu dát, metódy pre orientáciu a manipuláciu v dátach.

4.2 View

View je zodpovedný za prezentovanie dát. Snahou je, aby súbor obsahoval HTML kód (čo najmenej PHP kódu), a aby aplikácia používala čiastočné HTML súbory pre zobrazenie celej stránky. Môže pristupovať priamo k metódam modelu, avšak malo by sa to diať minimálne, a systematicky.

View je zlé navrhnutý ak, vykonáva SQL dopyty, ak pristupuje k dátam superglobálnych premenných.

4.3 Controller

Úlohou Controllera je spájať Model a View, spracovávať požiadavky používateľa, môže pristupovať k superglobálnym premenným.

Controller nesmie obsahovať SQL dopyty a HTML kódy.

4.4 Ideálna MVC aplikácia

V ideálne navrhnutej aplikácii Controller obsahuje opakujúce sa malé časti kódov. Najlepšie prepracovaný, a najobsiahlejší by mal byť Model, pretože obsahuje základnú bussiness logiku dát, ktorá je navrhnutá špeciálne pre webaplikáciu.

5. Úlohy

5.1 Úloha1

Vytvorte jednoduchý MVC objektovo orientovaný framework pre webové aplikácie.

5.2 Úloha2

Vytvorte stránku s nápisom 'Hello World!' a odsekom textu v tomto frameworku.
Vytvorte stránku s nápisom 'Page Info' a s informáciou v odseku hostname:port v tomto frameworku.

Výsledok:

Stiahnite a vyskúšajte si výsledný MVC framework v PHP. Je tam stále čo vymyslieť a vylepšiť.