Responsabil laborator: Andrei Avram (andrei.avram@gmail.com)

Data postare: 29.03.2009
Data ultimei modificări: 29.03.2009 (20:15) - laboratorul este în curs de actualizare

Autoîncărcarea obiectelor


O aplicație Web, indiferent de limbajul în care este scrisă, poate conține un număr mare de clase. Acceptând convenția că fiecare clasă este implementată într-un fișier diferit, se ajunge la un număr mare de fișiere, care trebuie incluse în toate scripturile care au nevoie de acele clase.


Pentru că PHP nu suportă pachete (ca în Java) sau alte mecanisme de includere automată a unui număr arbitrar de fișiere, scripturile PHP trebuie să includă fișier de fișier fiecare clasă de care este nevoie pentru un anumit script. Pentru că includerea inutilă a unui număr mare de clase ar duce la o performanță scăzută a serverului web, includerea tuturor claselor posibile (indiferent dacă scriptul curent are sau nu nevoie de ele) nu este o soluție.


Una din soluțiile aplicabile serverelor dedicate este modificarea include-path-ului PHP pentru a include clasele de care este nevoie. Totuși, această soluție nu se poate aplica serverelor web care găzduiesc un număr mare de aplicații, eventual pentru clienți diferiți.


Începând de la versiunea 5, PHP oferă o metodă comodă de includere a fișierelor care conțin codul pentru clase, prin funcția __autoload($class_name). În cazul în care o funcție cu această semnătură există definită în scriptul care este executat, această funcție se apelează ori de câte ori o clasă este necesară și codul pentru ea nu a fost inclus. Funcția __autoload() dă programatorului posibilitatea de a include fișierul cu clasa cerută înainte ca scriptul să eșueze.


Avantajul utilizării unei funcții __autoload() este că există un control centralizat al tuturor include-urilor care se fac într-un anumit script, lucru util, spre exemplu, în cazul în care se dorește la trecerea de la o versiune la alta a aceleiași aplicații.


Metode magice în clase


Clasele în PHP suportă o serie de metode, numite metode magice (magic methods), care au o funcționalitate specială în limbaj, și sunt de obicei apelate automat de interpretorul PHP când se îndeplinesc anumite condiții. Toate metodele magice încep cu __ (două caractere underscore), și manualul PHP recomandă să se evite folosirea numelor de funcții care încep cu __ pentru orice alte funcții.


În prezent, metodele magice documentate sunt:


__construct() și __destruct() se apelează automat atunci când un obiect este creeat (cu new), repectiv când un obiect este distrus (la sfârșitul execuției unui script sau în mod explicit undeva în cod).


mixed __call ( string $name , array $arguments )

mixed __callStatic ( string $name , array $arguments )


__call() și __callStatic() sunt apelate atunci când se încearcă apelul unei metode a unei clase și clasa nu are definită sau nu este accesibilă acea metodă (este declarată private). Prin utilizarea __call() și __callStatic() se poate implementa overloading pentru metode în PHP, dar pot fi folosite și în alte scopuri.


void __set ( string $name , mixed $value )

mixed __get ( string $name )

bool __isset ( string $name )

void __unset ( string $name )



Analog __get(), __set(), __isset(), __unset() sunt metode pentru realizarea overloading-ului pentru membrii. Ele sunt apelate ori de câte ori se încearcă accesul la o variabilă membru care nu există sau la care nu există acces (declarată private), astfel:



Un exemplu unde utilizarea acestor metode este util este la accesul mai comod la variabile al căror nume se determină la runtime, nu în timpul execuției codului.


Aceste metode nu pot fi folosite decât în context obiect, și nu pot fi folosite în contexte statice.


__sleep() și __wakeup() se apeleaza automat la serializarea / deserializarea (funcțiile serialize() și unserialize()) obiectelor, și sunt utilizate pentru eliberarea / re-creearea resurselor neserializabile (spre exemplu, conexiunea la baza de date este o resursă neserializabilă).


__toString() are comportamentul pe care îl are și în alte limbaje de programare, și anume este apelat automat la încercarea de transformare în String a unei instanțe a obiectului (spre exemplu, la un apel echo $obiect)


__clone() este apelat în cazul unei clonări a unui obiect (cu cuvântul cheie clone), după ce a fost făcută o copiere “de suprafață” a conținutului obiectului, și oferă posibilitatea modificării referințelor (referințele sunt copiate ca atare, și câteodată se dorește ca obiectul nou să creeze noi instanțe ale obiectelor pe care le referențiază).


Utilizarea template-urilor pentru separarea codului PHP de pagini HTML


O practică utilă pentru programarea web (și nu numai) este separarea interfeței (în cazul aplicațiilor WEB, a paginilor HTML) de codul aplicației.


O metodă comună pentru realizarea acestei separări este folosirea unui sistem de template-uri. Template-urile sunt fișiere HTML cu marcaje adiționale care pot fi regăsite și înlocuite cu conținut dinamic. Există implementări pentru asemenea sisteme, un exemplu pentru PHP este Smarty (http://www.smarty.net/).


Modelul de utilizarea a unui sistem de template-uri (sau template engine) este următorul:

  1. Se scrie / creează într-un editor WYSIWYG (what you see is whay you get) pagina HTML 
  2. Se stabilesc zonele în care se dorește conținut dinamic, și se marchează în HTML aceste zone 
  3. În script-ul PHP, se parsează fișierul template rezultat din (2), se găsesc zonele marcate anterior și se înlocuiesc cu conținutul dinamic (spre exemplu, dintr-o bază de date) și rezultatul este construit într-o variabilă temporară 
  4. Se afișează variabila temporară 


Desigur, se poate folosi o abordare asemănătoare pentru template-uri de afișare a anumitor obiecte, care rezultă într-o secvență de cod HTML, dar nu o pagină întreagă, care poate fi introdus apoi ca și conținut dinamic într-un alt template.


Task-uri


Aveți la dispoziție în Resursele laboratorului 3 clase, Template, TemplateTag și Page.


Template și TemplateTag implementează un template engine rudimentar, care caută anumite marcaje într-un text și le înlocuiește apoi cu valori primite în program, și dă la ieșire un text cu marcajele înlocuite cu valorile lor.


Mai concret, Clasa Template, în constructor, parsează un fișier de input și caută în el orice tag de forma {id:nume_tag}. Aceste tag-uri (marcaje) sunt păstrate în obiecte de tip TemplateTag (care păstrează numele, poziția de start și poziția de sfârșit al marcajului respectiv), în array-ul tags. Ele vor putea primi o valoare (în câmpul value) care va înlocui {id:nume_tag} în string-ul rezultat.


Pentru a obține string-ul final, clasa Template are metoda render();


Task 1 - 3p

Adăugați în clasa Template implementarea pentru metodele magice __get(), __set(), __unset(), __isset() astfel încât să fie accesibilă valoarea tag-urilor, direct din variabile Template, într-o manieră de forma


$template = new Template(“template.htm”);

$template->tag_name = “valoare”; // exemplu pentru __set();


Testați folosind test.php


Task 2 – 6p


În resursele laboratorului găsiți clasa Page, care intermediază accesul către un tabel din baza de date care conține informații despre pagini (paginile au titlu, autor si continut). Clasa Page functionează asemănător cu clasele User și Categorie din laboratoarele precedente.


În page.htm există o machetă a unei pagini HTML, care trebuie transformată într-un template (prin introducerea de marcaje {id:nume_marcaj}), astfel încât prin rularea index.php, cu transmiterea ID-ului paginii printr-un parametru GET, următoarele elemente să fie dinamice (încărcate la runtime din cod):



Notă: Pentru utilizarea templateurilor și a clasei Template, observați cum funcționează Page::getAllPagesLinks().


Task 3 – 1p


Scrieți o funcție __autoload(), într-un fișier PHP separat, care să încarce toate clasele necesare rulării scriptului index.php. În index.php trebuie să aveți un singur include, cel pentru fișierul cu funcția __autoload().


Task 4 – Bonus – 3p


Folosind unul sau mai multe template-uri, afișați o listă cu titlul, autorul și primele 100 de caractere din textul fiecărei pagini în momentul în care nu se primește nici un parametru id prin GET.