Migrarea către PHP 5.3.0

În timp ce programatorii PHP încep să descarce si să testeze cea mai mare modifcare adusă limbajului de programare din ultimii 7 ani, mulţi se întreabă nu despre ce le-a adus Moş Crăciun în sacul lui plin de jucării pentru a le folosi în dezvoltarea aplicaţiilor de mâine ci despre modificările aduse în limbajul de programare care vor afecta aplicaţiile de ieri.

Vestea cea buna este că dacă v-aţi ţinut aplicaţia în ton cu modificările aduse limbajului sunt puţine modificări care vă vor afecta. Vestea cea rea este că pe măsura ce vă îndepărataţi de versiunea curentă portarea aplicatiilor devine din ce în ce mai grea.

Ceea ce urmează nu este o listă de noi funcţionalităţi din PHP 5.3, puteţi găsi destule referinţe bune in spaţiul web pentru informare. Acest articol este o sinteză a manualului de migrare la PHP 5.3. Vom acoperi acele subiecte care afectează continuitatea ramurii 5.x

Functii de procesare a vectorilor

Inainte de PHP 5.3, multe din funcţiile folosite la procesarea vectorilor puteau primi ca parametrii un obiect sau un vector. Din PHP 5.3 multe din aceste funcţii vor putea primi doar vectori. Daca doriţi accesarea unei proprietati din obiecte cu una din următoarele funcţii, va trebui să le convertiţi la array în prealabil.

Modificari în funcţiile magice

Înainte de 5.3 metodele magince puteau fi declarate cu orice modificator de vizibilitate.

  • __get()
  • __set()
  • __isset()
  • __unset()
  • __call()

Din PHP 5.3 aceste metode trebuiesc declarate publice, si nu pot fi statice.

Considerate depăşite

PHP are o lista de funcţii care au fost marcate pentru a fi eliminate din limbaj. Majoritatea acestor funcţii nu au o folosire comună, dar ar fi utilă o verificare a lor. Funcţiile marcate pentru eliminare sunt:

În plus, căteva din directivele php.ini au fost marcate pentru eliminare. Daca sunt activate ele vor emite un avertisment de nivel E_DEPRECATED:

  • define_syslog_variables
  • register_globals
  • register_long_arrays
  • safe_mode
  • magic_quotes_gpc
  • magic_quotes_runtime
  • magic_quotes_sybase

Cum orice emite un mesaj E_DEPRECATED va fi eliminat la urmatoarea versiune majora a limbajului, acestea sunt niste indicii pentru programatori la ce sa se uite pentru trecerea la PHP 6.

Reconsiderare

În PHP 5.0, funcţia is_a() a fost marcată pentru eliminare in favoarea operatorului instanceof, şi totuşi nu a fost eliminată din limbaj. În PHP 5.3, această decizie a fost reconsiderată, iar apelul către funcţia is_a() nu mai emite un mesaj E_DEPRECATED.

Cuvinte rezervate

Au fost adăugate două noi cuvinte rezervate

Având în vedere natura acestor cuvinte este puţin probabil ca ele să existe în codul provenit din versiunile vechi ale limbajului. Totuşi este o idee buna să va scanaţi codul pentru aceste cuvinte rezervate. Dacă există, ele vor genera erori de parsare. Nu pot fi folosite ca nume de funcţii, clase etc.

Concluzie

Ghidul migrarii către PHP 5.3 poate fi găsit în documentaţia de pe php.net. Nu sunt multe elemente din PHP 5.3 care să împiedice un cod bine scris in PHP 5.x sa funcţioneze în versiunea 5.3. Aceasta nouă versiune vine mai mult cu adaugiri la limbaj.

Multumiri

De cele mai multe ori se trece peste aprecierea si multumirea celor care au contribuit la dezvoltarea PHP-ului. Nu cred ca vom trece mai departe fără a face si noi acest lucru. Multumim întregii echipe care si-a dedicat timpul pentru crearea şi îmbunătăţirea PHP-uluiâ

Acest articol este o traducere a articolului Migrating to PHP 5.3.0 aparut in techPortal.

Cache-uri

Cache-ul este o zonă temporară de stocare de informatii, care duplică niste date considerate originale, creat pentru acces direct si rapid asupra acestor date. Aceasta mapare a datelor faţă de locul lor de stocare iniţial, se face pentru  acele date care sunt greu de accesat în mod direct, care se afla în zone partajate, care ar avea un timp de prelucrare ridicat si care ar fi solicitate în mod frecvent, iar rezultatul ar fi de fiecare dată acelaşi.

Sisteme de cache se întalnesc implementate în microprocesoare(exemplul fiind cache-ul de nivel 2), în hard disk-uri(pentru a micşora timpul de citire al datelor), în sisteme de management al bazei de date(MySQL va ţine în cache rezultatul unei interogari, iar la primirea aceleiaşi interogări va returna rezultatul din cache, făra a interoga tabelele în sine), in browser-ele pe car ele folosim zi de zi(Firefox de exemplu îşi va face cache la imagini, ca la un refresh sa nu fie nevoit sa le preia de pe server din nou), chiar şi Google deţine propriul cache din care poate furniza conţinutul paginilor web.

Aşadar, scopul declarat al cache-ului este de a economisi timp.

Teoria Cache-ului

Există cateva concepte cheie în teoria cache-ului care trebuiesc respectate în implementarea unui astfel de sistem

  1. identificatorul unic – care va fi folosit la identificarea elementului în cache
  2. durata de viaţă – defineşte cât timp un element din cache va fi considerat valid
  3. preluarea condiţionată – astfel încat părţile din cod care ar accesa informaţiile originale să fie evitate, dar să si permită reîmprospătarea
  4. resetarea la cerere – pentru păstrarea consistenţei informaţiilor din cache cu cele din locaţia originală, este necesară posibilitatea ca la o modificare a datelor din această locaţie, cache-ul sa fie marcat ca invalid şi reconstruit

Astfel, un algoritm general de folosire a cache-urilor ar fi:

  • dacă elementul din cache cerut de aplicaţie există atunci el va fi returnat intocmai
  • dacă elementul din cache cerut nu există atunci datele acelui cache se vor aduce din locaţia originală, se va crea elementul corespunzător în cache, iar datele vor fi returnate aplicaţiei

Aplicare

Aşadar, vom presupune o apicaţie web, pentru care avem un număr mare de accesări atât din partea vizitatorilor dar şi din partea celor care administrează respectivul website. Pentru o şi mai buna exemplificare, vom considera cazul standard al unui magazin online, în care avem listări de categorii, listări de produse din fiecare categorie şi afisări detaliate de produse(preţ, descriere, detalii tehnice etc). În spatele site-ului, respectiv în aplicaţia de administrare a acestuia, avem un număr de operatori care lucrează necontenit la imbunătăţirea informaţiilor prezentate pe acel site. Mai mult decât atât, să mai luam în calcul existenţa unor aplicaţii care periodic sincronizează preţurile şi stocurile produselor cu cele existente la furnizorii direcţi. Pentru a îmbunătăţi imaginea de ansamblu să considerăm ca magazinul are câteva zeci de mii de produse. În cuvinte mult mai simple şi mai tehnice: o mulţime de interogări sql de tip insert, update dar mai ales select.

Dezavantajul unui astfel de scenariu este evident cel al supraîncărcării bazei de date cu interogări care de cele mai multe ori se vor repeta şi vor furniza acelaşi set de date. Cu toate că, de exemplu, MySQL deţine un cache propriu din care returnează un set de date al unei interogari la o repetare a acesteia, aplicaţia PHP care interogheză baza de date va trebui sa realizeze tot protocolul de comunicare, să furnizeze interogarea şi să primească datele, deci nişte timpi deşi mici, deloc de neglijat în contextul unui volum de trafic ridicat.

Continuând scenariul nostru de “groază” mai trebuie luat în considerare faptul ca la o interogare de tip insert sau update, cele cauzate de aplicaţia de administrare, pot apărea lock-uri pe câmpurile, înregistrările sau chiar tabelele din baza de date, deci până la terminarea execuţiei şi scrierea ori modificarea cu succes a datelor în bază, o instrucţiune select, nu va putea citi baza de date pentru preluarea informaţiilor şi va fi pusă în aşteptare până la terminarea tranzacţiei. Rezultă un timp mort şi mai mare. Cache-ul ar trebui sa intervină în astfel de momente, când putem spune ca majoritatea interogarilor vor furniza acelaşi set de date pentru perioade definite de timp,  iar rularea lor nu ne-ar aduce decât dezavantaje.

Interogând baza de date pentru a obţine informaţiile despre produsele din categoria “Monitoare LCD” vom obţine unset de date reprezentat printr-un vector cu produsele din acea categorie. Datele din acest vector pot fi introduse în cache. Conform algoritmului general descris mai sus putem scrie urmatorul cod PHP

if (!($data = loadFromCache('cache_for_category_id_' . $categoryId)))
{
    $data = loadDataFromDatabase($categoryId);
    saveToCache('cache_for_category_id_' . $categoryId, $data, 3600);
}

Plecăm astfel de la premisa ca informaţia căutată se afla în cache şi chiar încercăm să o preluăm. Dacă functia loadFromCache() va întoarce o valoare nulă atunci înseamnă ca datele nu se află în cache şi ele vor trebui aduse din baza de date, lucru ce se va face prin functia loadDataFromDatabase() iar apoi salvate în cache cu ajutorul funcţiei saveToCache(), cache valabil o ora. Chiar dacă presupunerea noastră iniţială referitoare la existenţa datelor în cache este adevărată sau nu, după executarea acestei porţiuni de cod vom avea în variabila $data informaţiile necesare.

Trebuie avut în vedere faptul că în tot acest timp datele considerate valide sunt cele din baza de date, cache-ul fiind doar o copie locala a acesteia. Deşi sistemul ne va reseta automat cache-ul după expirarea perioadei de viaţă, vor exista situaţii când cache-ul va deveni inconsistent, adică nu va mai reflecta realitatea din baza de date. Deci, la adăugarea unui produs nou în baza de date în categoria “Monitoare LCD”, cache-ul construit mai devreme nu mai este consistent(nu conţine si acest nou produs). Cum varianta în care aşteptăm trecerea celor 3600 de secunde pentru a se recrea cache-ul nu ne multumeşte(perioada putând fi mult mai mare), aplicaţia de administrare va trebui sa intervină asupra cache-ului şî să invalideze înregistrarea ce conţine datele din această categorie. În acest mod vom forţa recreerea cache-ului cu noile informaţii la următoarea accesare a categoriei respective.

resetCache('cache_for_category_id_' . $categoryId);

În tot scenariul de mai sus am considerat crearea de cache-uri pe categorii şi nu unul global care să conţină toate categoriile existente pe site, din considerente de acces si de resetare. Este mai simplu sa alegem direct cache-ul categoriei pe care dorim să o afişăm decât sa încărcăm toate categoriile prin care să o căutăm pe cea dorită, precum este mai normal ca la introducerea produsului nou în categorie să resetăm doar cache-ul categoriei respective şi nu cel al tuturor produselor.

Unelte

PHP nu deţine nativ funcţii de lucru cu cache-ul, însă există extensii PECL care pot fi instalate şi cu care se pot lucra, printre care enumerăm Memcache şi APC.

Folosirea extensiei Memcache:

$cache = new Memcache();
$cache->addServer('localhost');

if(!($data = $cache->get('cache_id'))
{
    $data = getData();
    $cache->add('cache_id', $data);
}
$cache->delete('cache_id');

Folosirea extensiei APC:

if(!($data = apc_fetch('cache_id'))
{
    $data = getData();
    apc_add('cache_id', $data);
}
apc_delete('cache_id');

Diferenţa dintre cele 2 extensii este aceea că Memcache va stoca informaţiile în memoria RAM a serverului, pe când APC le va stoca in fişiere pe hard disc.

UPDATE:

A Practical Guide to Data Caching with Zend Server scrisă de Shahar Evron, Product Manager la Zend Technologies, Inc., este o lucrare apărută recent şi pe care o recomandăm celor interesati de acest subiect.

Până la un subiect mai serios, care se află deja în scriere, vin cu o problemă, ce se adresează celor care se considera începători. Mi s-a întamplat sa dau această “problemă” unora de nivelul celui menţionat mai devreme, iar răspunsurile să  mă surprindă de fiecare dată.

Aşadar, dându-se un array (vector) cu 2 milioane de elemente, indexate corespunzător de la 0 la 1.999.999 cu valori de tip string, să zicem sub 1 KB ca dimensiune. 

Scrieţi o funcţie care să determine dacă vectorul în cauză este sau nu mulţime. Evident, se cere un timp de execuţie al funcţiei minim

Un vector este considerat mulţime dacă are toate valorile distincte

Citeam zilele tecute pe The PHP Benchmark un articol  care compară diversele metode prin care se poate parcurge un vector asociativ. În mod normal, un foreach simplu care ne-ar funriza cheia şi valoarea aflată la cel index ar fi de ajuns la fel şi soluţia unui for cu indice de la zero la numarul maxim de elemente, în cazul în care cheile vectorului ar fi numere întregi. Cu toate acestea însă, în cazul unui volum mare de date utilizarea unui foreach, uşoară şi comodă, poate duce la un timp de execuţie foarte mare


reset($aHash);
foreach($aHash as $key => $val)
{
$aHash[$key] .= "a";
}

Exemplul de mai sus, are timp de executie 409 µs pe un vector de 100 de elemente.


$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++)
{
$aHash[$key[$i]] .= "a";
}

Deşi cel de-al 2-lea exemplu presupune mai multe opreaţii decât primul, timpul de execuţie în conditiile în care furnizăm acelaşi vector de 100 de elemente este de 38 µs ... de 10 ori mai putin.

Sesiuni în PHP

Protocolul HTTP este poate unul dintre cele mai simple protocoale. Este un protocol care tăieşte în prezent, nu are amintiri şi nici vise care speră să i se îndeplinească în viitor. Tradus în limbaj tehnic acest lucru înseamnă că un server web (care se foloseşte de protocolul HTTP) nu va şti şi nici nu-i va păsa dacă o cerere va veni de la acelaşi utilizator precum precedenta sau de la un utilizator nou. Acesta tratează fiecare cerere ca şi cum ar fi unică şi ca şi cum nu ar avea nici o legătură cu posibile cereri precedente sau viitoare. Acest lucru ar fi un dezavantaj evident, dacă nu ar exista diverse căi de a suplini acest neajuns. O astfel de cale se numeşte sesiune, iar limbajul PHP dispune de o serie de funcţii pentru a uşura lucrul cu sesiuni.

Sesiunile sunt folosite aşadar pentru a crea continuitate între accesările utilizatorilor unui site, chiar dacă aceste au loc la intervale relativ ridicate de timp. Sesiunile sunt stocate pe server, iar la cel mai jos nivel de reprezentare ele sunt nişte fişiere ce sunt stocate într-o zonă sigură a serverului de unde PHP-ul poate avea acces pentru citire, modificare sau creare. Ele sunt menţinute în relaţie cu clientul printr-un identificator de sesiune care va fi stocat la client de regulă într-un cookie(poate fi transmis şi printr-un parametru din GET).

Sesiunile de deschid în două modalităţi: imiplicit şi explicit. Implicit se face prin directiva de configurare aflată în fişierul php.ini numită session.auto_start, care setată pe valoarea 1 va porni implicit sesiunea la începutul rulării oricărui script PHP. Explicit, aceasta se realizează la cererea programatorului folosind funcţia session_start() de regulă la începutul fiecărui script. Avantaje si dezavantaje există de ambele parţi, prin pornirea implicită se evită apelarea în fiecare script al aplicaţiei a funcţiei session_start() însă datorită faptului că o sesiunea se va crea înainte de încărcarea fişierelor auxiliare folosite într-o aplicaţie, cum ar fi cele de definire a claselor, facând astfel imposibilă stocarea unor obiecte în sesiuni. Pornită explicit, funcţia session_start() va trebui apelată întotdeauna înainte de a trimite orice informaţie către client, deoarece aceasta va încerca stabilirea cookie-ului de sesiune, care va fi transmis printr-un header la client.

Informaţiile stocate pe sesiunie se pot regăsi în variabila superglobala $_SESSION care va fi creată automat la pronirea sesiunii şi care va conţine toate informaţiile deja existente pe sesiune. Se pot scrie, citi şi sterge informaţii din această variabilă, care beneficiază de toate avantajele oferite de vectorii limbajului PHP.


session_start();
$_SESSION['isLogged'] = true;


session_start();
if ($_SESSION['isLogged'])
{
echo "Utilizatorul este autentificat";
}

Presupunând că cele 2 coduri reprezintă fişiere diferite, apelate în ordine vor genera afişarea mesajului de autentificare iar variabila se va păstra pe sesiune până la ştergerea ei sau pănă la distrugerea sesiunii.

O sesiune este persistentă până când aceasta este ştearsă sau până când ea expiră. Timpul de expirare este dat de către timpul de expirarea al cookie-ului păstrat în browser-ul clientului. Directiva session.cookie_lifetime din php.ini este setată implicit pe valoarea zero, şi înseamnă că sesiunea va expira în momentul în care utilizatorul va închide browser-ul. În cazul în care se doreşte forţarea unei sesiuni persistente se poate folosi funcţia session_set_cookie_params(). Distrugerea manuală a datelor existente într-o sesiune se poate face prin functia session_destroy()

cURL

cURL este un utilitar din linie de comandă folosit pentru transferul de fişiere ce pot fi accesate printr-un URL. Principalul scop şi utilizare a acestui program este automatizarea transferului de fişiere sau secvenţelor de operaţii, fiind o unealtă excelentă de a simula un client web şi acţiunile pe care le poate întreprinde un utilizator în browser-ul lui. cURL suportă transferul de date folosind mai multe protocoale cum ar fi: HTTP, HTTPS, FTP, FTPS, TFTP, Telnet, SCP precum şi altele.

Pentru ca această unealtă să poată fi folosita în diverse aplicaţii, a fost dezvoltată o librărie numită libcurl care poate fi inglobată în diverse aplicaţii şi care oferă aceeaşi funcţionlitate ca şi utilitarul în sine. Limbajul PHP deţine şi el o astfel de librărie, scrisă de către Daniel Stenberg, şi care oferă conectarea cu diverse servere folosind următoarele protocoale: http, https, ftp, gopher, telnet, dict, file şi ldap. Deasemenea libcurl suportă certificate de securitate, POST si PUT prin HTTP, încărcări de fişiere prin FTP sau din formularele HTTP, proxy-uri, cookie-uri sau autentificări folosind nume de utilizator şi parolă.

Un exemplu de folosire simplă este următorul:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.google.com');
curl_exec($ch);
curl_close($ch);

PHP are implementate o serie de funcţii pentru lucru cu această librărie. Un algoritm general de funcţionare este descris şi în exemplul de mai sus care nu face altceva decât să returneze acceseze pagina google.com. Astfel, la început am creat o nouă sesiune de cURL folosind funcţia curl_init, şi am stocat-o în variabila $ch. Acesteia i se pot adăuga diverse opţiuni care să personalizeze sesiunea respectivă. În final sesiunea se execută, adică cererea către google.com este făcută, iar la final se eliberează resursa creată.

Într-o sesiune de cURL se pot adăuga mai mulţi parametrii care vor determina comportamentul acesteia. Iată câteva dintre cele mai uzuale opţiuni, care vor fi setate folosind funcţia curl_setopt:

  • CURLOPT_URL – reprezintă URL-ul pentru care se crează sesiunea şi cu care se va încerca trimiterea/primirea de date
  • CURLOPT_USERAGENT – va seta semnătura pe care o va lăsa accesarea la server
  • CURLOPT_HTTPHEADER – reprezintă un vector în care se pot defini header-ele de HTTP care se vor folosi la accesarea serverului
  • CURLOPT_REFERER – reprezintă URL-ul de pe care vine cererea curentă, cel care a referit acest nou URL
  • CURLOPT_ENCODING – reprezintă tipul de arhivare aplicat transferului curent, de regula gzip
  • CURLOPT_RETURNTRANSFER – va face ca funcţia curl_exec să nu afişeze aceste rezultatul sesiunii ci sa-l returneze într-o variabilă.
  • CURLOPT_HTTPGET – indică faptul că se modalitatea de transmitere a datelor este HTTP GET; aceasta este modalitatea implicită de transmitere a dateleor, astfel că setarea ei este necesară doar dacă într-o aceeaşi sesiune a fost făcută anterior o conexiune diferită
  • CURLOPT_POST – va determina trimiterea unei cereri de tip HTTP POST, cea implicită fiind GET
  • CURLOPT_POSTFIELDS – poate fi folosit în cazul unei cereri de tip POST şi aici se vor specifica variabilele pentru POST
  • CURLOPT_TIMEOUT – reprezintă numărul de secunde pentru care se va reîncerca o conexiune în caz ca cea iniţială a eşuat
  • CURLOPT_PORT – este portul pe care se va face cererea şi este implicit 80 pentru o conexiune folosind HTTP şi 21 folosind FTP
  • CURLOPT_PROXY – este numele serverului de proxy prin care se va face conexiunea
  • CURLOPT_PROXYUSERPWD – numele de utilizator şi parola pentru acces la serverul proxy, dacă sunte necesare
  • CURLOPT_FOLLOWLOCATION – va transmite librăriei de cURL să urmeze un redirect în cazul în care serverul o va dicta
  • CURLOPT_SSL_VERIFYPEER – verifică valabilitatea certificatului de securitate în cazul unei conexiui folosind SSL
  • CURLOPT_SSL_VERIFYHOST – indică faptul că în cazul unei conexiuni securizate, certificatul SSL trebuie să fie emis pentru host-ul care a fost contactat de catre cURL
  • CURLOPT_UPLOAD – va fi folosit în cazul unui upload
  • CURLOPT_FRESH_CONNECT – indică faptul că se va folosi o conexiune nouă în loc de cea existentă în cache
  • CURLOPT_HTTP_VERSION – indică versiunea protocolului HTTP care va fi folosită pentru comunicare
  • CURLOPT_MAXREDIRS – numărul maxim de redirectări pe care conexiunea le va urma
  • CURLOPT_COOKIE – reprezintă cookie-urile care vor exista în momentul cererii către server
  • CURLOPT_COOKIEFILE – reprezintă un fişier în care sunt stocate unul sau mai multe cookie-uri care vor insoţi cererea către server
  • CURLOPT_COOKIEJAR – reprezintă un fişier în care se vor stoca cookie-urile provenite de la server

Pe lângă aceşti parametrii care pot fi aplicaţi asupra cererii, există şi diverşi parametri care pot furniza informaţii referitoare la răspunsul venit din partea serverului în urma executării cererii de către cURL. Acestea pot fi acceste folosind funcţia curl_getinfo:

  • CURLINFO_HTTP_CODE – va furniza codul de răspuns al cererii
  • CURLINFO_TOTAL_TIME – timpul total afecatat sesiunii
  • CURLINFO_EFFECTIVE_URL – reprezintă ultimul URL care a fost folosit in sesiunea cURL
  • CURLINFO_REDIRECT_TIME – timpul total afectat redirectărilor dictate de server
  • CURLINFO_SIZE_UPLOAD – numărul de bytes încărcaţi către server
  • CURLINFO_SIZE_DOWNLOAD – numărul de bytes primitţi de la server
  • CURLINFO_CONTENT_TYPE – tipul de conţinut primit de la server; este util pentru a distinge între un conţinut HTML şi unul care în mod normal ar fi oferit către download

O atenţie sporită trebuie avută la aceste opţiuni referitor la versiunea de PHP pe care o folosiţi, întru-cât ele au fost introduse de-a lungul timpului sau chiar recent şi nu pot exista în versiunea pe care o utilizaţi.

Trimitere de POST cu cURL

Vom încerca în cele ce urmeză să oferim câteva exemple de folosire a cURL, pentru a exemplifica puterea de lucru care o poate oferi acesta.

Să presupunem, aşadar, existenţa unui formular de autentificare pe un site care va cere utilizatorului adresa de email cu care acesta s-a înregistrat şi parola lui, după cum urmează:

Email: Password:

Putem crea astfel un script care să simuleze o autentificare întocmai precum un utilizator s-ar fi autentificat direct pe site.

$ch = curl_init("http://www.example.ro/post.php");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, "email=blog@inphpwetrust.com&pass=foobar");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION , true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.3");
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
curl_close($ch);

Am început prin a crea o nouă sesiune care se va conecta la adresa http://www.example.ro/post.php şi pentru care am stabilit că va fi de tip HTTP POST, prin optiunea CURLOPT_POST, am mai setat o pseudo semnătură a “browser-ului” care se va conecta la server prin opţiunea CURLOPT_USERAGENT ca fiind Mozilla Firefox versiunea 3.0.3, varianta pentru Microsoft Windows XP, am indicat ca orice redirecţionare indicată de către server să fie urmărită şi de cURL prin optiunea CURLOPT_FOLLOWLOCATION, CURLOPT_HEADER indică faptul că nici un header primit ca răspuns nu va fi stocat, iar CURLOPT_RETURNTRANSFER va detemina ca doar corpul mesajului să fie preluat, iar în final după execuţia sesiunii de cURL va fi afişat în browser. Opţiunea CURLOPT_POSTFIELDS este cea mai importantă din aceast exemplu şi este cea care va determina autentificarea efectivă, deci într-un final returnarea unui conţinut care ar putea fi văzut doar de o persoană care s-ar autentifica pe un site. Valorile pentru elementele HTML de tip input email şi pass au fost setate aici, ele fiind credenţialele pe care aplicaţia de pe server va efectua autentificarea. Evident acesta este un exemplu simplu şi uşor de “hack-uit”, în ziua de azi multe din site-uri verifică veridicitatea utilizatorului folosindu-se de mai multe elemente sau afişează o imagine cu nişte caractere de recunoscut care pot fi mai greu sau chiar imposibil de nimerit folosind un script PHP.

Upload prin FTP folosind cURL

Deşi sunt şi alte metode de a încărca un fişier pe un server folosind protocolul FTP, sau simplu pentru a citi conţinutul unui director FTP, chiar şi limbajul PHP deţine niste funcţii de lucru cu FTP, cURL poate fi de ajutor atunci când acestea nu sunt dispobilie sau când se doreşte o mai mare flexibilitate în lucrul cu acest protocol. Următorul exemplu va folosi un handler de fişier creat cu funcţia fopen care va fi pasat parametrului CURLOPT_INFILE.

$fp = fopen('image.jpg', 'r');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'ftp://ftp.domain.com/');
curl_setopt($ch, CURLOPT_USERPWD, "ftpuser:ftppass");
curl_setopt($ch, CURLOPT_UPLOAD, true);
curl_setopt($ch, CURLOPT_INFILE, $fp);
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localfile));
curl_exec ($ch);
curl_close ($ch);
fclose($fp);

Design patterns

Un punct de reper pentru a descoperi un programator bun, indiferent de limbajul de programare pe care îl utilizează, este abilitatea acestuia de a aplica tehnici cunoscute de design al aplicaţiei. Design patterns(sau proiectarea modelelor) sunt un un set de soluţii ce pot fi aplicate la diverse probleme ce se întâlnesc frecvent în cadrul programării orientate pe obiect.

În teorie, aceste modele nu au legătură cu codul scris de un programator. Sunt niște soluţii de eficientizare a problemelor des întâlnite, nişte indicaţii pe care programatorul trebuie să le transpună în limbajul de programare pe care îl foloseşte. Ne vom referi în continuare la aceste modele legat de limbajul PHP, modele care deşi pot fi implementate folosind şi programarea procedurală, ele sunt evidenţiate mai bine cu ajutorul programării orientate pe obiect.

Singleton

Modelul Singleton este probabil cel mai simplu model. El este făcut pentru a permite accesul la o singură resursă care nu va fi niciodată duplicată, dar care trebuie făcută disponibilă în orice moment al execuţiei aplicaţiei. Implementarea unui Singleton trebuie să satisfacă cele două condiţii de acces global şi instanţiere singulară a resursei respective. Are nevoie de un mecanism de acces la class Singleton fără a instanţia un obiect şi un mecanism de a păstra informaţia asupra resursei ce urmează a fi folosită. Astfel acest model poate fi cel mai uşor implementat prin crearea unei clase ce va conţine o metodă care va crea o nouă instanţă a clasei, dacă aceasta nu exista deja, iar dacă există o va returna pe aceasta. Pentru a fi siguri că acea clasă nu va fi instanţiată în alt fel, constructorul este declarat protejat. Exemplul cel mai simplu este acela în care se pune problema existenţei unei singure conexiuni la baza de date:


class database
{
private static $_singleton;
private $_connection;

protected function __construct()
{
$this->_connection = mysql_connect(SERVER, USER, PASSWORD);
}

public static function getInstance()
{
if (is_null(self::$_singleton))
{
self::$_singleton = new database();
}

return self::$_singleton;
}
}

$cnx = database::getInstance();

Aşadar constructorul a fost declarat protejat pentru a controla accesul la el şi de a da posibilitatea apelării lui doar din interiorul clasei. Acest lucru este făcut cu ajutorul metodei statice getInstance, care verifică la fiecare apel al ei dacă există definit membrul static $_singleton, caz în care îl va returna, sau va încerca să creeze unul nou. Odată creat, metoda getInstance() nu va mai încerca să creeze unul nou ci-l va retuna pe cel creat tot timpul.

Factory

Modelul Factory se foloseşte în scenariile în care există o clasă generică (fabrica) care are posibilitatea de a crea o instanţă a una sau mai multe clase specializate în rezolvarea unei aceleaşi probleme dar în diferite moduri. Un astfel de exemplu îl reprezintă stocarea datelor de configurare într-o aplicaţie. Datele de configurare, precum credenţialele pentru conectarea la baza de date, sau căile de acces la diverse directoare, pot fi stocate în mai multe feluri: în fişiere XML, in fişiere INI sau chiar în baza de date. Astfel putem crea un mecanism care să returneze un obiect ce va şti să lucreze cu un anume mod de stocare a acestor date de configurare.


class configuration
{
const STORE_IN_INI = 1;
const STORE_IN_DB = 2;
const STORE_IN_XML = 3;

public static function factory($type = self::STORE_IN_INI)
{
switch ($type)
{
case self::STORE_IN_INI:
return new configuration_ini();
case self::STORE_IN_DB:
return new configuration_db();
case self::STORE_IN_XML:
return new configuration_xml();
default:
throw new Exception('Tip de stocare necunoscut');
}
}
}

Este evident faptul că toate cele 3 clase specializate de tratare a fișierelor de configurare (ini, xml, db) trebuiesc definite şi implementate, un avantaj fiind definirea lor conform unei clase abstracte. Apelarea “fabricii” se face după cum urmează:


$storage = configuration::factory(configuration::STORE_IN_XML);

Registry

Modelul Registry este un model Singleton mai evoluat, în sensul că permite stocarea mai multor resurse, ca într-un registru. Un exemplu ar fi acela în care, deşi avem conexiunea stabilită către baza de date printr-un Singleton, şi prin această conexiune se fac toate interogările, există posibilitatea ca din aplicaţie să fie nevoie de conexiunea în paralel către o alta bază de date pentru a efectua şi acolo diverse operaţiuni. Cu un simplu Singleton nu s-ar fi putut realiza aşa ceva însă cu un Registry acest lucru este posibil. Un astfel de Registry va trebui să implementeze metode de adăugare, verificare a existenţei şi de returnare a informaţiilor cerute.


class registry
{
private static $_register;

public static function add(&$element, $name)
{
$name = strtolower($name);
self::$_register[$name] = $element;
}

public static function exists($name)
{
$name = strtolower($name);
if (array_key_exists($name, self::$_register))
{
return true;
}
return false;
}

public static function &get($name)
{
$name = strtolower($name);
if (self::exists($name))
{
return self::$_register[$name];
}
else
{
throw new Exception('Elementeul cerut nu exista in registru.');
}
}

}

O folosirea a cestul model este următorul cu precizarea ca în acest exemplu clasa database nu este un Singleton:


$db = new database();
registry::add($db, 'dbCnx');
/*
verificarea conexiunii
*/
if (registry::exists('dbCnx'))
{
$db = registry::get('dbCnx');
}

Model-View-Controller

Spre deosebire de celelate modele discutate anterior, Model-View-Controller(denumit şi MVC) este un model destul de complex. Scopul lui este de a oferi o metodă de separare dintre logica aplicaţiei (model), modului de afişare (view) şi structura decizională (controller), aplicaţiile MVC fiind astfel usor de modificat atunci când se doreşte modificarea doar a modului de afişare sau a modului în care este tratată o cerere.

Logica unei astfel de aplicaţii este următoare: o cerere este făcută către aplicaţie şi este apelat controller-ul care va decide cum va trata cererea. Controller-ul va chema o clasă din Model, care efectiv va efectua interogări către baza de date, prelucrările necesare ale datelor sau orice alte operaţiuni necesare. Rezultatul, de regulă un set de date, va fi întors către Controller care va transmite aceste date părţii vizuale a aplicaţiei(View) unde deja există o structură de afişare a datelor ce tocmai au fost primite. Avantajul enorm al acestui model este separarea clară între cele 3 compomente şi uşurinţa cu care se pot extinde aplicaţiile dezvoltate pe acest model.

PHP RefCard

PHP RefCard

PHP RefCard

Am aflat zilele trecute citind feed-ul de la Zend Developer Zone cum că DZone a lansat mult aşteptatul PHP RefCard. Pentru cei care nu ştiu, acest RefCard reprezintă o broşură frumos formatată şi ordonată cu informaţii utile referitoare la un anume limbaj de programare.

PHP RefCard este realizat de către Jason Glimore, autorul a câtorva cărţi de referinţă cum ar fi Beginning PHP and MySQL: From Novice to Professional, Beginning PHP and Oracle: From Novice to Professional (Expert’s Voice) sau Beginning PHP and PostgreSQL 8: From Novice to Professional.

Recomand acest RefCard tuturor începătorilor ca un mic ghid asupra problemelor comune ce pot apărea referitor la configurare, programarea orientată obiect, stringuri, vectori, expresii regulate sau integrarea cu MySQL, precum şi diverse informaţii utile.

Structuri de control în PHP

Un script PHP este în esență o înşiruire de instrucţiuni. Instrucţiunile pot fi de mai multe feluri: atribuiri, apeluri de funcţii, structuri de control sau chiar instrucţiunea vidă. Aceste instrucţiuni sunt delimitate la finalul lor de către semnul punct şi virgulă. Totodată ele pot fi grupate într-o singură instrucţiune compusă, care la rândul ei devine o instrucţiune de sine stătătoare, înglobând mai multe instrucţiuni simple între două acolade.

Decizia

În crearea unui script, sau în general a oricărui program, vă veți întâlni cam în 100% din cazuri cu luatul de decizii. Sau mai bine zis cu scris cod care să ia decizii pe loc în momentul rulării aplicaţiei respective. Deşi rudimentară, este una din tehnicile de bază în programare (alături de iteraţie), fără de care probabil programarea dacă ar fi existat ar fi fost o meserie destul de greoaie. Se bazează pe faptul că în timpul execuţiei se poate dori execuţia unor anume instrucţinui în detrimentul altora în funcţie de, să zicem, nişte parametrii proveniţi de la utilizatori, deci de a lua o decizie: ce instrucţiune, sau set de instrucţiuni, să execut? Există două structuri de control care vă pot ajuta în luarea acestor decizii: if şi switch.

Instrucțiunea IF-ELSE-ELSEIF este o instrucţiune întâlnită în majoritatea limbajelor de programare şi mai este întâlnită sub denumirea de instrucţiunea decizională simplă. Astfel aceasta dă posibilitatea de a alege dintre două sau mai multe posibilităţi de execuţie a codului, folosind-se de niște condiţii testate în prealabil. Formatul general al acesteia este:


if ()

[elseif ()

elseif ()

else
]

Efectul acestei instrucţiuni este următorul: se evaluează condiţia din cadrul ramurei if, dacă aceasta are valoare logică de TRUE, atunci se execută instrucţiunea imediat următoare. În caz contrar se trece la prima ramură elseif(dacă există) testându-se condiţia şi executându-se instrucţiunea corespunzătoare în caz ca aceasta se evaluează la TRUE. Procesul continuă pentru toate ramurile elseif până când sunt terminate sau până la prima condiţie evaluată la TRUE. În caz ca nici una nu va fi găsită TRUE, se va executa instrucţiunea de pe ramura else, dacă există. De remarcat este faptul că se pot defini oricât de multe ramuri elseif şi doar una if sau else, şi că evaluarea la TRUE a unei condiţii face ca execuția să iasă din această structură de control. Totodată trebuie menţionat că oricare din ramurile definite suportă în corpul lor o singură instrucţiune; în cazul în care se dorește execuţia mai multor instrucțiuni acestea se pot grupa într-o instrucțiune compusă. Iată nişte exemple, pur didactice:


$number = 27;
if ($number % 2 == 0)
echo "Numarul este par";
else
echo "Numarul este impar";
/*
structura care foloseste si elseif
*/
if ($number % 3 == 0)
echo "Numarul este divizibil cu 3";
elseif ($number % 3 == 1)
echo "Numarul nu este divizibil cu 3, restul impartirii este 1";
else
echo "Numarul nu este divizibil cu 3, restul impartirii este 2";

În cazul în care o instrucțiune IF ar conține multe ramuri elseif, scrierea lor ar putea deveni greoaie, precum citirea şi înțelegerea lor. De aceea PHP dispune şi de o instrucțiune decizională multiplă: SWITCH. Switch are avantajul că permite testarea unei variabile, sau a unei aceleiași expresii cu mai multe valori şi să execute o serie de instrucțiuni în caz că acesta ar fi egal cu una din valorile conținute în ramurile case.


switch ()
{
case var1:
instructiune1;
[break;]
[case var2:
instructiune2;
[break;]
case varn:
instructiunen;
[break;]]
[default:
instructiune;]
}

Iată şi un exemplu concret:


$number = 27;
switch ($number % 3)
{
case 0:
echo "Numarul este divizibil cu 3";
break;
case 1:
echo "Numarul nu este divizibil cu 3, restul impartirii este 1";
break;
case 2:
echo "Numarul nu este divizibil cu 3, restul impartirii este 2";
break;
}

Spre deosebire de lanțul de if-uri care se pot forma, instrucțiunea switch este mult mai robustă, atât prin modul de scriere dar şi prin modul de lucru. Diferă de instrucțiunea if prin faptul ca expresia este evaluată o singură dată la început şi apoi este verificată cu toate valorile întâlnite în ramurile case până când acestea corespund. Poate exista și o ramură default, care va fi executată când nici una din valorile din case nu corespund cu valoarea evaluată. Instrucțiunea break este folosită pentru a evita executarea mai multor ramuri în cazul în care o valoarea expresiei evaluate este întâlnită în una din ramurile case. Considerăm următorul exemplu:


switch ($number % 3)
{
case 0:
echo "Numarul este divizibil cu 3";
case 1:
echo "Numarul nu este divizibil cu 3, restul impartirii este 1";
case 2:
echo "Numarul nu este divizibil cu 3, restul impartirii este 2";
}

Deşi cu valoarea nulă în practică, exemplul de mai sus dacă v-a primi un număr divizibil cu zero si va afişa mesajele (sau executa intrucţiunile) de pe toate ramurile existente sub prima ramură până când va întâlni o instrucţiune break, sau, cum e şi cazul nostru, până la finalul structurii. Totuşi, un exemplu carea ar avea ceva logică şi care ar scoate în evidenţă lipsa folosirii de break, ar fi următorul:


switch ($number % 3)
{
case 0:
echo "Numarul este divizibil cu 3";
break;
case 1:
case 2:
echo "Numarul nu este divizibil cu 3";
}

În caz că expresia se va evalua la valoarea 1 scriptul va executa instrucţiunile din ramura cu valoarea 1 (in cazul nostru nici unul) şi instrucţiunile din ramura cu valoarea 2. Procesul ar fi continuat dacă ar fi existat şi alte ramuri.

Iteraţia

Iterația este procedura de repetare a unui set de aceleaşi instrucţiuni în anumite condiţii. Astfel aceste structuri de control pot fi folosite de regulă pentru a aplica acelaşi tratament asupra unui set de date, sau chiar pe o singură informaţie, până când se obţine un rezultat dorit. Cele mai uzuale instrucţiuni iterative sunt while, do-while, for, foreach.


while ()
;

do
{
;
} while ();

Ambele structuri au rolul de a executa instrucţiunile din corpul lor până când expresiile respective ajung la valoarea TRUE. Diferenţa esenţială dintre ele este ca in cazul structurii while expresia este evaluată înainte ca orice iteraţie să aibă loc, deci corpul structurii while poate să nu fie executat niciodată, pe când în cazul instrucţiunii do-while expresia este evaluată după prima iteraţie, deci numărul minim de cicluri efectuate este de unu. Asemeni instrucţiunii decizionale simple, şi instrucţiunile iterative suportă în corpul lor doar execuţia unei singuri instrucţiuni, executarea mai mult instrucţiuni se face prin gruparea lor în una compusă.


/*
codul va afisa toate numerele de la 0 la 9
valoarea cu care $i va iesi din ciclu este 10
*/
$i = 0;
do {
echo $i;
$i++;
} while ($i < 10);
/*
codul următor are același efect cu cel de mai sus
*/
$i = 0;
while ($i < 10)
{
echo $i;
$i++;
}

În ambele cazuri, în instrucţiunile ce alcătuiesc corpul structurii, trebuie avută în vedere modificarea variabilei, sau a expresiei, care face scopul acelei expresiei, astfel se poate genera o bulcă infinită, buclă care nu se va termina într-un număr finit de paşi, ducând la o execuţie infinită a scriptului. In cazul celor doua exemple de mai sus, presupunem ca din corpul structurilor am scoate instrucţiunile de incrementare ale lui $i; acest lucru ar face ca valoarea lui $i să rămână tot timpul 0, deci mai mică decât 10, deci condiţia valabilă în orice iteraţie. Astfel de cazuri trebuiesc evitate.

Instrucţiunile for si foreach sunt tot instrucţiuni repetitive şi sunt considerate cele mai complexe structuri iterative din cadrul limbajului PHP.


for (expr-initiere; expr-testare; expr-iterare)
instructiune;

Modul de funcţionare al acestei structuri este următorul: la prima intrarea în structura se va executa necondiţionat expresia expr-initiere, care de regulă va conţine instrucţiuni de iniţializare de variabile ce vor fi folosite în interiorul for-ului. În continuare, la fiecare iteraţie se va evalua expr-testare, al cărui rezultat va decide dacă instrucţiunea din corpul for-ului va fi executată sau nu. În caz că expresia va fi evaluată la valoarea FALSE, instrucţiunea nu va fi evaluată iar execuţia programului va ieşi din această structură. În cazul evaluării la TRUE, corpul structurii va fi executat, iar la finalul acestuia se va executa şi expr-iterare. Ca şi în cazurile precedente, executarea mai multor instrucţiuni presupune gruparea lor într-una compusă şi aceeaşi atenţie trebuie acordată evitării de bucle infinite. Complexitatea aceste structuri constă în faptul că oricare din cele 3 expresii pot lipsi, dându-se astfel o mai mare libertate programatorului în controlul acestei structuri.

Structura foreach este o structură introdusă din versiunea 4 a PHP şi este creată special pentru a itera prin elementele unui vector şi poate fi folosită în 2 moduri:


foreach ($array as $valoare)

foreach ($array as $index => $valoare)

Prima variantă de utilizarea va extrage fiecare element al vectorului la fiecare iteraţie în interiorul variabilei $valoare, pe când ce-l de-al doilea exemplu va separa indexul de valoarea elementului în cele 2 valori. Micul neajuns al acestei structuri este acela că iteraţia se face pe o copie a vectorului, deci orice modificare asupra $index sau $valoare nu va avea efect în vectorul care se iterează.

Întreruperea şi continuarea

Break şi continue sunt două instrucţiuni cu care se pot controla toate structurile iterative în sensul opririi sau continuării execuţiei de la începutul buclei. Astfel, o instrucţiune break va determina ieşirea dintr-o structură repetitivă a execuţiei, pe când continue va determina întoarcerea la începutul buclei, evaluarea expresiei de testare (dacă este cazul) şi reluarea execuţiei.


for ($i = 0; $i < 10; $i++)
{
if ($i == 5)
continue;

echo $i . ',';

if ($i == 7 )
break;
}

Exemplul precedent va afişa numerele 0, 1, 2, 3, 4, 6, 7. În interiorul structurii for la atingerea valorii 5 a lui $i se va executa instrucţiunea continue care va muta execuţia la incrementarea lui $i, şi apoi la execuţia următorului ciclu. La întâlnirea lui 7, deşi va fi afişat, execuţia va paraşi structura for, datorită instrucţiunii break. Un exemplu mult mai practic de folosirea a lui break este acela în care se dispune de un set de date (numere de exemplu), şi se doreşte aflarea dacă un număr oarecare se află în acel set de date. Metoda cea mai simplă şi rudimentară este căutarea secvenţială, în care fiecare element al setului de date este comparat cu acea valoare, până când cele 2 valori conicid sau până setul de date este temrinat. Îmbunătăţirea considerabilă ar fi ca în momentul în care elementul a fost găsit, scriptul ca nu caute în elementele rămase ci să printeze un mesaj şi să încheie execuţia.


$array = array(1,4,2,8,6,3,9,0);
$searchNo = 8;
foreach ($array as $element)
{
if ($searchNo == $element)
{
echo "Numarul cautat a fost gasit";
break;
}
}

De notat este că în cazul instrucţiunilor imbricate repetitive, apelul lui continue sau break va determina continuarea sau întreruperea primului ciclu repetitiv în care instucţiunea a fost apelată. Dacă de exemplu se doreşte iesirea şi din cel de-al doilea, sau al treilea ciclu imbricat, se poate folosi următoarea sintaxă:


break 2;

Aşadar, este recomandat ca după fiecare apel a lui break să nu uitaţi terminarea instrucţiunii cu un punct şi virgulă, în caz contrar, dacă după aceasta există o instrucţiune care să returneze sau să se evalueze la o valoarea numerică, această valoare va fi pasată instrucţiunii break, lucru care poate afecta logica scriptului.

Metode magice în PHP

PHP 5 aduce un nou model al programării orientate pe obiect cu o multitudine de îmbunatăţiri şi facilităţi, spre bucuria programatorilor deja obişnuiţi cu pseudo-modelul din PHP 4. O facilitate care poate ajuta în cazul unor proiecte cu module interconectabile, este cea numită metode magice. Acestea sunt un set de metode standard, rezervate în limbajul de programare, care au o aceeaşi semnificaţie şi o aceeşi funcţionalitate în cazul oricăror clase definite de utilizator. Aceşte metode sunt autoapelate în anumite condiţii sau în cazul unor acţiuni externe.

Constructor si destructor

Metodele __construct() şi __destruct(), deşi pot fi numite magice doar prin modul de scriere, ele făcând parte din teoria programării orientate pe obiect, sunt metodele care se vor apela automat la crearea (instanţierea) respectiv distrugerea unui obiect aparţinând unei clase. De regula, în constructor se crează conexiuni, se setează variabile, se preiau variabile externe sau orice altă acţiune de iniţializare a elementelor clasei, pe când în destructor se face curăţenie printre aceste elemente, se închid eventualele conexiuni sau se şterg fişiere.


class math
{
protected $a;
protected $b;

public function __construct($x, $y)
{
$this->a = $x;
$this->b = $y;
}

public function add()
{
return $this->a + $this->b;
}

public function multiply()
{
return $this->a * $this->b;
}

public function __destruct()
{
unset($this->a);
unset($this->b);
}
}

$math = new math(3, 8);
echo $math->add();
unset($math);
?>

Constructorul va fi astfel apelat în momentul creării obiectului folosind operatorul new iar destructorul va fi apelat în la apelul lui unset() asupra obiectului în mod expicit sau în mod implicit la terminarea execuţiei scriptului, daca nu s-a făcut altfel.

Serializare şi deserializare

Serializarea este procesul de a liniariza o variabilă de orice tip într-una de tip string, cu scopul de a pastra informaţiile din interiorul ei(precum si tipul/tipurile de date) şi pentru a le transmite la distanţă fără pierdere de informaţii. Deserializarea este, evident, procesul invers. Daca în cazul unui vector rezultatul serializării este un string ce conţine în ordine şi pe nivele elementele acestuia, în cazul claselor se oferă posibilitatea de a alege ce date să se pastreze sau să se porteze. Pentru acest lucru au fost create două metode magice care vor fi apelate automat la apelul serializării sau a deserializării: __sleep() şi __wakeup().


class math
{
protected $a;
protected $b;

public function __construct($x, $y)
{
$this->a = $x;
$this->b = $y;
}

public function add()
{
return $this->a + $this->b;
}

public function multiply()
{
return $this->a * $this->b;
}

public function __sleep()
{
return array('a', 'b');
}

public function __wakeup()
{
echo 'a=' . $this->a . '; b=' . $this->b;
}

public function __destruct()
{
unset($this->a);
unset($this->b);
}
}

$mathAdd = new math(3, 8);
echo $mathAdd->add(); /* va afisa 11 */
$tmpData = serialize($mathAdd);

$mathMultiply = new math(0, 0);
$mathMultiply = unserialize($tmpData);
echo $math->multiply(); /* va afisa 24 */
?>

De menţionat este că la apelul unserialize() şi implicit a lui math::__wakeup() scriptul va printa valorile primite de la prima instanţiere a clasei.

String

Există şi o metodă simplă, numită __toString() care va fi apelată la fiecare apel al lui echo, print sau încercare de folosire a obiectului ca un string.


class hello
{
protected $foo;

public function __construct($bar)
{
$this->foo = $bar;
}

public function __toString()
{
return "Hello " . $this->foo;
}
}

$hi = new hello('george');
echo $hi; /* va tipari "Hello george" */

?>

O altă metodă de transmitere a datelor între obiecte, posibil mai convenabilă decât serializarea şi deserializarea, poate fi făcută cu ajutorul funcţiei var_export() şi a metodei magice __set_state(). var_export() va returna un string care va conţine, fie valoarea numerica (în cazul variabilelor scalare de tip numeric), fie o valoare de tip string (cazul variabilelor string), fie stringul care ar crea un vector în cazul acestora, fie valoarea returnată de metoda magica __set_state() în cazul obiectelor. Această metodă magică a fost introdusă odată cu versiunea 5.1.0 a PHP.


class A
{
public $var1;
public $var2;

public static function __set_state($an_array)
{
$obj = new A;
$obj->var1 = $an_array['var1'];
$obj->var2 = $an_array['var2'];
return $obj;
}
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';

eval('$b = ' . var_export($a, true) . ';');
// $b = A::__set_state(array(
// 'var1' => 5,
// 'var2' => 'foo',
// ));

?>

Autoîncărcarea

Deşi nu este o metodă, funcţia __autoload() poate fi de mare ajutor atunci când aplicaţia dispune de un numar considerabil de clase care nu sunt folosite toate în acelaşi timp. Astfel, aceasă funcţie se va apela şi va include, dacă este nevoie, un fişier care va conţine definiţia clasei, în momentul instanţierii unui obiect. În scenariul de faţă, este de preferat ca toate definiţiile clasei să fie situate într-un acelaşi director, iar fiecare clasă situată într-un fişier separat al cărui nume sa fie identic cu al clasei.


function __autoload($className)
{
if (!class_exists($className))
{
include("includes/classes/" . $className . ".php");
}
}

Se evită in acest fel încărcarea tuturor claselor existente în aplicaţie şi îngreunarea acesteia luând în considerare presupunerea ca nu toate clasele vor fi folosite în oricare acţiune a aplicaţiei, prin faptul ca o clasa va fi încarcată doar dacă este nevoie si doar atunci când este nevoie.