PHP OOP - Clase extinse si Mostenire

Mostenirea este unul din cele mai utile instrumente ale Programarii Orientate pe Obiect - OOP.
Prin mostenire se intelege transmiterea proprietatilor, constanttelor si a functiilor de la o clasa la alta, intr-o structura ierarhica. Prima clasa este clasa de baza, denumita "parinte", legata de aceasta se poate crea o sub-clasa, denumita "copil"; sub-clasa 'copil' mosteneste proprietatile si metodele clasei 'parinte' (cele care sunt cu atribut "public" sau "protected"), le poate folosi in instructiunile din propriul cod si le transmite cand se creaza o instanta de obiect la ea. Precum in natura copii mostenesc genele parintilor.

1. Creare clasa copil

Sub-clasa "copil" se creaza cu formula:
                class ClasaCopil extends ClasaParinte
Iata cum se aplica acest procedeu in programarea PHP OOP.
In exemplu prezentat aici se construieste intai o clasa de baza, denumita Site, cu doua proprietati: una cu atribut private (site), alta cu atribut public (categorie), constructor si o metoda getPag().
<?php
// Clasa Site
class Site {
  // Definire proprietati, private si public
  private $site;
  public $categorie = 'php-mysql';

  // Constructor
  public function __construct($site) {
    // Daca parametru $site e un Sir cu cel putin 5 caractere, atribuie valoarea la proprietatea "site"
    // Altfel, seteaza un mesaj de eroare
    if(is_string($site) && strlen($site)>4) $this->site = $site;
    else throw new Exception('Valoare incorecta pt. site');
  }

  // Metoda getPag()
  public function getPag($pag) {
    // Defineste si returneaza adresa URL a paginii
    $url = $this->site. '/'. $this->categorie. '/'. $pag;
    return $url;
  }
}
?>
- Metoda Constructor atribuie valoarea din parametru $site la proprietatea "site".
- Metoda getPag() returneaza o adresa URL formata din proprietatile clasei si parametru ei.
Salvam aceasta clasa intr-un fisier "class.Site.php".
• Aceasta clasa poate fi utilizata pentru afisarea unui sir cu o adresa URL, prin apelarea metodei getPag(). Daca vrem sa obtinem aceasta adresa intr-un tag HTML tip link (<a>) se poate crea o noua metoda, pentru alta facilitate alta metoda, si tot asa se aglomereaza codul clasei, fapt ce devine o problema in cazul unor clase mari. O alta varianta este crearea unei sub-clase extinse din aceasta. Sub-clasa (copil) are abilitatea de a mosteni /utiliza toate proprietatile si metodele definite ca publice si protected in clasa de baza (parinte) ca si cum ar fi create in ea (fara sa mai fie scrise inca o data). Astfel, in clasa copil se creaza doar instructiunile cu noua functie pe care o dorim, si nici nu mai trebuie modificata clasa parinte.
In continuarea acestui exemplu se creaza o alta clasa, denumita LinkSite, ca extindere a clasei Site. Ea contine doar o metoda getLink() pentru afisarea unui link.
<?php
include('class.Site.php');        // Include clasa parinte (Site)

// Creare clasa copil LinkSite, extinsa din Site
class LinkSite extends Site {
  // Metoda getLink()
  public function getLink($pag) {
    // Afiseaza un link format din getPag() si "categorie" din clasa parinte
    echo '<a href="http://'. $this->getPag($pag). '" title="'. $this->categorie. '">Link</a><br />';
  }
}
?>
Salvam aceasta clasa, cu denumirea "class.LinkSite.php" in acelasi director unde e salvata si clasa Site.
Acum, atentie la explicatii:
  - Pentru a avea acces la definitiile din clasa principala Site, deoarece se afla intr-un fisier extern, o includem cu include().
  - Cuvantul special "extends" stabileste identitatea clasei LinkSite ca fiind o extindere a clasei Site.
  - In metoda getLink() se folosesc proprietatea "categorie" si metoda "getPag()" din clasa parinte ca si cum ar fi fost definite in aceasta clasa.
        Aceste elemente pot fi accesate in clasa copil deoarece au atribut "public" (functioneaza si cu "protected"). Dar proprietatea "site", care are atribut "private" in clasa parinte, nu poate fi accesata in cea copil.
  - Desi aceasta clasa nu are o metoda constructor, ea mosteneste constructorul clasei parinte, astfel, cand se creaza o instanta de obiect la clasa LinkSite trebuie adaugat si un argument de tip Sir, deoarece asa e definita metoda "__construct($site)" in clasa de baza (Site).

Sa testam aceasta clasa copil, cu scriptul urmator.
<?php
include('class.LinkSite.php');      // Include fisierul cu subclasa LinkSite

// Creare instanta la clasa copil LinkSite
$objLink = new LinkSite('www.marplo.net');

// Apeleaza metoda getLink() (definita in clasa copil);
$objLink->getLink('oop-clase-extinse-mostenire.html');

// Modifica valoarea proprietatii "categorie" (aflata in clasa parinte)
$objLink->categorie = 'ajax';

// Afiseaza rezultatul dat de metoda getPag() (creata in clasa parinte)
echo $objLink->getPag('introducere.html');
?>
- Deoarece clasa LinkSite se afla intr-un fisier PHP extern, o includem cu include().
- La crearea instantei de obiect se adauga si argumentul necesar, un sir ('www.marplo.net'), cerut de metoda constructor a clasei de baza, care e si ea mostenita.
- Observati cum sunt accesate proprietatea "categorie" si metoda "getPag()" (care se afla in clasa parinte), prin instanta de obiect la clasa copil (LinkSite) ca si cum ar fi create in ea. Acesta este efectul mostenirii, atat de util in OOP.
In browser va afisa urmatorul rezultat:
Link
www.marplo.net/ajax/introducere.html

2. Redefinire metode

In exemplu de mai sus, clasa copil LinkSite a mostenit si metoda "__construct()" din clasa parinte Site, fapt ce determina executarea codului din acel Constructor si cand e creata instanta la clasa copil. Uneori aceasta functie nu e adecvata in sub-clasa. Pentru a evita acest lucru, metodele inadecvate pot fi rescrise prin adaugarea uneia cu acelasi nume in clasa copil, astfel, la crearea instantei, va fi utilizata metoda, cu acelasi nume, din clasa copil.
- Rescrierea unei metode nu afecteaza cu nimic pe cea originala, modificarile efectuate sunt valabile doar in clasa copil in care se face rescrierea, respectiv, in alte sub-clase ale ei.


Iata o alta sub-clasa (PagSite) extinsa din Site. Aici se adauga si o metoda "__construct()" care o va anula pe cea din clasa parinte, se rescrie si metoda getPag().
<?php
include('class.Site.php');        // Include clasa parinte (Site)

// Creare clasa copil PagSite, extinsa din Site
class PagSite extends Site {
  public $domeniu = 'marplo.net';       // Proprietate

  // Constructor, rescrie pe cel din parinte (Site)
  function __construct() {
    // Fara instructiuni, executa nimic
  }

  // Metoda getPag(), rescrisa
  public function getPag($pag) {
    // Preia functionalitatea metodei getPag() din clasa parinte
    $url = parent::getPag($pag);

    // ... alte instructiuni pt. metoda getPag()

    // Adauga in $url si valoarea proprietatii "domeniu"
    $url = $this->domeniu. $url;

    return 'Pagina: '. $url;
  }
}
?>
- Prin crearea in aceasta sub-clasa a metodei "__construct()" se anuleaza efectul celei din clasa parinte, si neavand parametru, nici la creare instantei nu se mai adauga vreun argument.
- Pentru a pastra si functionalitatea initiala (din clasa parinte) a metodei rescrise, in corpul functie care face rescrierea, se adauga cu formula parent::nume_metoda() (tehnica folosita si in exemplu de sus, la getPag()).
In scriptul urmator se foloseste aceasta a doua sub-clasa.
<?php
include('class.PagSite.php');      // Include fisierul cu aub-clasa PagSite

// Creare instanta la clasa copil PagSite
$objPag = new PagSite();

// Modifica valoarea proprietatii "categorie" (aflata in clasa parinte)
$objPag->categorie = 'html';

// Afiseaza rezultatul dat de metoda getPag() (rescrisa)
echo $objPag->getPag('introducere.html');
?>
- Instructiunea $objPag->getPag('introducere.html') apeleaza metoda "getPag()" din clasa la care s-a creat instanta de obiect (PagSite) deoarece exista cu aceasta denumire in ea. Se observa si din rezultatul afisat:
Pagina: marplo.net/html/introducere.html

3. Metode finale

In exemplu prezentat mai sus se vede cum o metoda din clasa de baza poate fi rescrisa in sub-clasa copil, dandu-i alta functionalitate. Sunt situatii in care nu se doreste ca o metoda sa poata fi rescrisa, in acest caz, prin adaugarea cuvantului final la inceputul declararii metodei se blocheaaaza posibilitatea rescrierii ei.
- De exemplu:

<?php
// Clasa Baza
class Baza {
  // Metoda final
  final public function numeMet() {
    // Instructiunile functiei
  }
}

// Definire clasa-copil din Baza
class CopilBaza extends Baza {
  // Rescriere metoda numeMet()
  public function numeMet() {
    // Alte Instructiuni
  }
}
?>
- Va genera eroarea:
Fatal error: Cannot override final method Baza::numeMet() in ...
- Deoarece metoda "numeMet()" este declarata cu atribut "final".

4. Clase finale

Precum metodele declarate cu "final" nu mai pot fi modificate in sub-clasa copil, asa se pot crea si clase care sa ramana fixe, fara sa se poata crea din ele alta sub-clasa. Acest lucru se obtine prin adaugarea cuvantului final inainte de class.
- De exemplu:

<?php
// Clasa Baza
final class Baza {
  // ... Instructiuni
}

// Definire clasa-copil din Baza
class CopilBaza extends Baza {
  // ... Alte Instructiuni
}
?>
- Va genera eroarea:
Fatal error: Class CopilBaza may not inherit from final class (Baza) in ...
OOP - Constante, Proprietati ... <<-- Anterior --- Urmator -->> Metode magice __get, __set, ...

Un Test simplu in fiecare zi

HTML
CSS
JavaScript
PHP-MySQL
Engleza
Spaniola
Ce tag se foloseste pentru a adauga liste in elemente <ul> si <ol>?
<dt> <dd> <li>
<ul>
 <li>http://coursesweb.net/html/</li>
 <li>http://www.marplo.net/html/</li>
</ul>
Care valoare a proprietatii "display" seteaza elementul ca tip bloc si afiseaza un punct in fata?
block list-item inline-block
.some_class {
  display: list-item;
}
Care instructiune JavaScript transforma un obiect in sir JSON.
JSON.parse() JSON.stringify eval()
var obj = {
 "courses": ["php", "javascript", "ajax"]
};
var jsonstr = JSON.stringify(obj);
alert(jsonstr);    // {"courses":["php","javascript","ajax"]}
Indicati clasa PHP folosita pentru a lucra cu elemente HTML si XML in PHP.
stdClass PDO DOMDocument
$strhtml = '<body><div id="dv1">CoursesWeb.net</div></body>';
$dochtml = new DOMDocument();
$dochtml->loadHTML($strhtml);
$elm = $dochtml->getElementById("dv1");
echo $elm->nodeValue;    // CoursesWeb.net
Indicati forma de Prezent Continuu a verbului "to live" (a trai /a locui)
lived living liveing
I`m living here.
- Traiesc /Locuiesc aici.
Care este forma de Gerunziu (sau Participiu Prezent) a verbului "vivir" (a trai /a locui)?
viviĆ³ vivido viviendo
Estoy viviendo aquĆ­.
- Traiesc /Locuiesc aici.
PHP OOP - Clase extinse si Mostenire - Tutorial PHP