Arhitectura client-server

Una din primele întrebări pe care le adresăm unui programator începător dornic de cunoaştere aprofundată a programării web este: “Ce este web-ul?”. Deşi o întrebare cu un răspuns aparent simplu, rar ni se întamplă să primim un acelaşi răspuns. Pentru fiecare dintre noi web-ul înseamnă altceva, şi se referă întotdeauna la motivele pentru care îl utilizăm. Poate fi un mediu de socializat, un bun suport de promovare, de comunicare, de făcut marketing şi implicit bani. Pentru unii însă, web-ul este o reţea interconectată de calculatoare, de maşini care interacţionează între ele pe principiul client-server.

Arhitectura client-server este o arhitectură de bază în funcţionare internetului, şi neînţeleasă de la început, poate face ca aprofundarea programării web şi a conceptelor din aceasta sa fie foarte greu de înţeles şi poate duce la confuzii care pot avea efecte devastatoare asupra unor aplicaţii lansate publice.

Client

Clientul este partea din această arhitectură care va iniţia comunicarea. Este cel care va trimite o cerere către un server pentru a primi un răspuns, nişte informaţii peronalizate pentru cererea care tocmai a făcut-o, date in general. Clientul poate fi un browser de web care se conectează la un server web, poate fi un client de e-mail(gen Thunderbird sau Microsoft Outlook) care se conectează la un server de email, trimite datele de autentificare pentru un cont de e-mail si cererea de primire a mesajelor noi, poate fi un client de FTP(gen Smart FTP sau Cute FTP) care va trimite unui server de FTP cererea de stocare pe acel server a unui fişier, urmat de fişierul în sine şi va primi ca răspuns confirmarea primirii acestuia sau un mesaj de eroare corespunzător. Aşadar clientul este cel care va acţiona şi va determina un întreg lanţ de acţiuni din partea server-ului: iniţiază cererea către server, aşteaptă raspunsul de la server, primeşte răspunsul de la server şi în final îl returnează utilizatorului posibil într-un mod formatat.

Server

Un server, în general, este probabil cea mai pasivă, indolentă şi comodă invenţie de pe internet. Serverul nu are niciodată iniţiativă, principala lui activitate este de a sta şi de a aştepta. Serverul nu va acţiona niciodată din cont propriu, nu va transmite date decât dacă este întrebat şi decât dacă sunt urmate anumite reguli de comunicare. Cu toate acestea, odată “deranjat” un server va face tot posibilul să mulţumească cererea clientului. Când este pornit, un server va lua poziţia de aşteptare de conexiuni (numită mai tehnic: listening state), de regulă acesta ascultă pe un anume port primirea conexiunilor. La primirea unei astfel de conexiuni, deci implicit a unei cereri, el va face toate demersurile necesare pentru a returna rezultatul aşteptat. Dacă este un server web, va întoarce clientului (browserul web) codul html al paginii care a fost cerută, dacă este un server de e-mail va returna clientului o listă cu toate email-urile pe care le-a primit de la ultima cerere, daca este un server de MySQL va prelua interogarea SQL primită o va executa şi va returna setul de date rezultat. Aşadar un server stă şi aşteaptă conexiuni pe care le va servi cererile de îndată ce au fost primite.

Diferenţe

Nu trebuie înţeles că toate calculatoarele personale, laptopurile sau orice maşină folosită în mod frecvent de o fiinţă umană este un client şi restul sunt servere. Noţiunea de client-server este întâlnită între oricare două maşini între care exista o cale de acces, o conexiune directă sau indirectă. De exemplu, să presupunem ca avem o maşină pe care ţinem o bază de date MySQL, o maşină pe care o folosim ca web server (deci unde stocăm fişierele unui web site) şi o maşină care va fi folosită drept client. Astfel, iniţiem o cerere de la client către serverul web pentru a primi o anumită pagina în care vom vizualiza, de exemplu, produsele de pe un magazin online. Serverul web primeşte cererea şi va acţiona întocmai, însă datele necesare generării acestei pagini HTML care va conţine informaţiile despre produse, se află în baza de date, deci pe un alt server. Serverul web, prin intermediul librariilor din PHP va iniţia acum o cerere către serverul de MySQL pentru a întoarce informaţiile despre produse, pe care cel din urmă le va furniza. În această ultimă situaţie serverul web este client pentru serverul de MySQL pentru că el este cel care iniţiază o cerere de date iar baza de date va furniza raspunsul la această cerere. În final, cu datele primite se va furniza pagina HTML complet formatată.

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()

“Best practice”

Vom începe scrierea unei serii de articole menite să promoveze diverse practici de programare sau utilizare corectă a limbajului PHP, MySQL, Javascript sau a framework-ului de la Zend. Ne vom adresa prin acestea în special începătorilor şi vom încerca să arătăm consecintele unei folosiri incorecte a diverselor facilităţi oferite de limbajele de mai sus.

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.

Controlul ieşirii

Utilizarea unei zone tampon pentru returnarea rezultatelor este o tehnica de programare care poate aduce avantaje aplicatiei în diverse cazuri. Controlul iesirii (sau Output Control ori Output Buffering) se refera la controlul informatiilor trimise de la server catre client. Este util de cele mai multe ori sa putem controla cum se vor trimite informatiile(de regula codul HTML generat) astfel încât pe parcursul generarii lui sa putem trimite alte diverse header-e.

Protocolul HTTP este un protocol de comunicare pe internet foarte des întalnit, mai ales în cadrul serverelor web. Apache este unul care implementeaza acest protocol la comunicarea cu clientii care fac diverse cereri acestuia. Anatomia sa presupune, atat la cerere cât si la raspuns, trimiterea unor header-e(prin care se vor specifica în ce fel sa fie interpretat mesajul ce urmeaza a fi trimis) si mesajul propriu-zis, care poate fi o cerere sau informatii formatate. Astfel, presupunând ca vrem accesarea site-ului google.com, browser-ul va trimite un mesaj de cerere catre webserver-ul respectiv cerând accesul la o pagina de pe server, în cazul nostru cea implicita. Serverul web va raspunde clientului cu un set de header-e prin care se va specifica ce tip de formatare va avea informatia ce urmeaza sa o primeasca, ce lungime are, ce codare de caractere trebuie sa foloseasca pentru a putea fi interpretata corect etc.

În cazul unui server pe care ruleaza PHP si în cazul în care serverul primeste o cerere pentru un fisier ce contine un script PHP, serverul va începe sa trimita catre client informatiile odata cu:

  • întâlnirea unui tag HTML
  • la orice apel al functiei echo, print, var_dump sau print_r
  • chiar la o linie goala întâlnita în orice fisier php, chiar si între blocurile de cod php


echo "Serverul Apache va incepe trimiterea de informatii din acest moment.";
?>



Corpul fisierului HTML generat pentru IP-ul


În exemplul de mai sus interpretorul de PHP va transmite serverului Apache sa înceapa trimiterea datelor catre client chiar de la instructiunea echo, si nu de la întâlnirea tagului de marcarea a începutului de document HTML cum ar fi fost normal. Sa presupunem însa ca, pentru fiecare accesare a acestui fisier, dorim sa retinem continutul returnat utilizatorului într-un fisier pe server, desi pentru exemplul furnizat, motivatia acestei operatiuni este nu mai mult decât didactica. Vom folosi asadar functiile PHP pentru controlul iesirii, functii fare pot fi recunoscute usor dupa prefixul ob_.


ob_start();
echo "Serverul Apache va incepe trimiterea de informatii din acest moment.";
?>



Corpul fisierului HTML pentru IP-ul


$html = ob_get_contents();
$f = fopen('log.txt', 'w');
fwrite($f, $html);
fclose($f);
ob_end_flush()
?>

Functia ob_start marcheaza începerea memorarii informatiilor generate de scriptul PHP fara însa a le trimite catre client. În orice moment al scriptului, înainte de trimiterea codului HTML generat, se pot accesa informatiile din zona tampon folosind functia ob_get_contents, lucru pe care l-am facut si noi pentru a stoca informatiile pe server. La final functia ob_end_flush va trimite informatiile generate catre client si va opri stocarea lor în zona tampon.

Serverul web are totusi niste header-e standard pe care le trimite odata cu fiecare cerere pe care o primeste. Din PHP aceste header-e se pot modifica sau se pot adauga folosind functia header. Cum aceasta functie va trimite direct header-ul catre client, ea trebuie apelata înainte ca orice continut sa fie trimis catre client, nerespectarea acestei reguli va genera un avertisment. Pentru a evita acest lucru se foloseste controlul iesirii.

Iesirea comprimata

Un avantaj al stocarii unei pagini web la server pâna aceasta a fost generata complet este acela ca, în cazul unui volum mare de date transferat, acestea pot fi comprimate pentru a facilita transportul lui peste internet catre clientul care a facut cererea, obtinând astfel un timp mai bun de raspuns. Functia ob_start poate primi ca prim parametru numele unei functii care va fi aplicata asupra codului generat dupa ce acesta a fost generat complet. Astfel, functia ob_gzhandler a fost special creata pentru a comprima datele generate folosind formatul gzip. Urmatorul exemplu va dispune trimiterea de date comprimate catre client:


ob_start("ob_gzhandler");