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

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ă:

<form action="http://www.example.ro/post.php" method="post">
    Email: <br />
    <input type="text" name="email" />
    Email: <br />   
    <input type="password" name="pass" /><br />
    <input type="submit" value="Log In" />
</form>

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

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.

Trigger-i în MySQL

Un trigger reprezintă o “subrutină” stocată în baza de date care conţine cod executabil, al cărei execuţii se va declanşa automat la întâmplarea unui anume eveniment. Spre deosebire de procedurile stocate sau funcţiile definite de utilizator care sunt definite global într-o bază de date, trigger-ii sunt strict legaţi de tabele, ei se definesc în relaţie cu acestea iar acţiunile lor sunt menite să aibă sens asupra lor sau pornind de la fiecare în parte.

Există două tipuri de trigger-i, cei care afectează toate înregistrările dintr-o tabelă şi cei care afecteză doar o anume înregistrare. Ambele însă au efect(se declanşează) atunci când una din următoarele operaţii este lansată asupra tabelei: INSERT, UPDATE, DELETE, iar pentru aceste operaţii se vor defini trigger-i care au efect ÎNAINTE sau DUPĂ ce instrucţiunea a fost executată pe baza de date.

CREATE
    [DEFINER = { user | CURRENT_USER }]
    TRIGGER trigger_name trigger_time trigger_event
    ON tbl_name FOR EACH ROW trigger_stmt

În MySQL trigger-ii au fost introduşi în versiunea 5.0.2 iar pentru crearea unuia este necesară deţinerea de drepturi corespunzătoare. Clauza DEFINER va stabili nivelul de drepturi necesare pentru a se putea executa trigger-ul;trigger_name reprezintă numele trigger-ului, iar trigger_time şi trigger_event definesc situaţia în care trigger-ului se va executa.

trigger_time reprezintă momentul în care se va executa trigger-ul relativ la operaţia ce s-a lansat pe acea tabela, şi poate avea următoarele valori: BEFORE şi AFTER. trigger_event reprezintă însuşi acţiunea la care se face referire, iar acest parametru va avea una din următoarele valori:

  • INSERT caz în care trigger-ul va fi lansat la execuţia unei instrucţiuni INSERT, LOAD DATA sau REPLACE pe baza de date;
  • UPDATE caz în care trigger-ul se va lansa la apelul unei instrucţiuni UPDATE
  • DELETE caz în care trigger-ul va fi lansat la ştergerea de înregistrări folosind instrucţiunea DELETE; trigger-ul nu va fi lansat la execuţia instrucţiunii TRUCATE sau DROP TABLE

tbl_name reprezintă numele tabelei la care trigger-ul va reacţiona iar trigger_stmt reprezintă instrucţiunea SQL ce va fi lansată; în caz că se doresc execuţia mai multor instrucţiuni acestea vor fi introduse intre cuvintele BEGIN şi END. Toate restricţiile discutate în cadrul articolului Subrutine în MySQL sunt valabile şi pentru trigger-i.

/*
trigger ce va fi lansat inaintea unei instructiuni INSERT
*/

CREATE TRIGGER trTestInsert BEFORE INSERT ON products
  FOR EACH ROW BEGIN
    SET NEW.products_guid = UUID();
    INSERT INTO audit (user_id, products_guid, audit_action) VALUES (NEW.users_id, NEW.products_guid, ‘insert’);
    UPDATE categories SET categories_count = categories_count + 1 WHERE categories_id = NEW.categories_id;
  END;
/*
trigger ce va fi lansat dupa o instructiune DELETE
*/

CREATE TRIGGER trTestDelete AFTER DELETE ON products
  FOR EACH ROW BEGIN
    SET NEW.products_guid = UUID();
    INSERT INTO audit (products_guid, audit_action) VALUES (OLD.products_guid, ‘delete’);
    UPDATE categories SET categories_count = categories_count - 1 WHERE categories_id = OLD.categories_id;
  END;

În definirea celor 2 trigger-e am folosit două cuvinte rezervate MySQL, şi anume: OLD şi NEW. Prin aceste două referinţe vă puteţi adresa la câmpurile din înregistrările care au fost sau urmează a fi procesate. Astfel la o inserare în baza de date NEW va conţine toate câmpurile din înregistrarea pentru care a fost declanşat acel trigger, pe când la un trigger pe o instrucţiune UPDATE, în OLD veţi găsi toate informaţiile legate de înregistrarea ce a fost modificată.

Trigger-ii ajută la creşterea gradului de automatizare a unor procese, pot fi folosiţi foarte bine la logarea de date sau informaţii, ori folosit la crearea datelor de audit. Nu în ultimul rând pot fi folosiţi şi la aplicarea unor restricţii suplimentare asupra operaţiunilor făcute asupra bazelor de date.

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 (<conditie-if>)
    <instructiune-if>
[elseif (<conditie-elseif1>)
    <instructiune-elseif1>
elseif (<conditie-elseifn>)
    <instructiune-elseifn>
else
    <instructiune-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 (<variabila-sau-expresie>)
{
    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 (<expresie>)
    <instructiune>;

do
{
    <instructiuni>;
} while (<expresie>);

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

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

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.

Ghilimele si apostroafe

Am observat de-a lungul timpului că o bună parte a începătorilor în ale programării PHP au o mare problemă în a înţelege şi a folosi corect facilităţile oferite de… ghilimele şi apostroafe.

Diferenţa esenţială între cele două modalităţi de a afişa stringu-uri este la interpretarea conţinutului. Vom considera următoarele exemple:

Exemplul 1

echo ‘foo’;
echo "bar";

Exemplul 2

$str = ‘foo bar’ . "\r\n";
echo "Un string frumos afisat este: $str";

Exemplul 3

echo ‘<a href="http://www.foo.bar/">link html</a>’;
echo "<a href=\"http://www.foo.bar/\">link html</a>";

Exemplul 4

$mysql = "select id, name, age from people where name like ‘george%’";
$mysql = ’select id, name, age from people where name like \’george%\’;

Aşadar, diferenţa esenţială este la modul de interpretare a conţinutului aflat între cele două simboluri grafice şi anume în cazul ghilimelelor se interpretează, iar în cazul apostroafelor nu se interpretează. Astfel, orice string delimitat de apostroafe care va conţine numele unei variabile va printa numele variabilei incluzând semnul dolar din faţa acestia, pe cand un acelaşi string delimitat de ghilimele va printa valoarea acelei variabile şi nu numele ei. De aici se poate deduce uşor ca în cazul printării unui string simplu se preferă utilizarea apostroafelor metodă care va fi mai rapidă decât cea cu ghilimele, în care interpretorul va încerca să gasească şi să înlocuiască posibilele variabile inserate de către programator (deci, timp mai mare de execuţie)
Este de observat şi faptul că în anumite cazuri este de păreferat folosirea ghilimelelor în detrimentul apostroafelor, sau invers, lucru care ar duce la o lizibilitate şi o coerenţă mai marită a codului sursă, plus că poate elimina câteva erori de parsare în cazul unor stringuri mai complexe. În exemplul 3 se poate observa cum folosirea de apostroafe pentru a printa un cod HTML este mai convenabil decat folosirea ghilimelelor, fapt ce ar determina ca oricăror atribute din tag-urile HTML să li se aplice metoda escape. Deasemeni, caz contrar, exemplu 4 arată cum un string ce ar reprezenta o interogare SQL ar fi mai uşor de citit şi înţeles dacă s-ar folosi ghilimelele.

Ghilimele magice

Ghilimelele magice, sau magic quotes, reprezină o funcţionlitate a limbajului PHP care presupune introducerea unui backslash (\) înaintea oricarui caracter apostrof, ghilimea, backslash şi NULL, procedeu cunoscut sub numele escape. Motivaţia pentru care s-a introdus această funcţionalitate a fost aceea de a ajuta programatorii incepători, sau neexperimentaţi de a preveni aşa zisele sql injections.
Controlul acestei facilităţi se face cu ajutorul unor directive din cadrul php.ini

  1. magic_quotes_gpc are efect asupra variabilelor provenite prin GET, POST, COOKIE, este setata implicit pe on şi nu poate fi modificată la în timpul excuţiei.
  2. magic_quotes_runtime în caz că este activată va avea efect asupra tuturor funcţiilor care returnează date din surse externe, provenite de la utilizator sau chiar şi din baze de date sau fişiereş implicit are valoarea off şi poate fi modificată ân timpul execuţiei.
  3. magic_quotes_sybase daca este activată va dubla orice apostrof cu un alt apostrof, în loc de backslashş totodată va suprascrie directiva magic_quotes_gpc; atenţie aşadar, având ambele directive activate, acestea vor determina ca doar apostroafele sa fie dublate de un alt rand de apostroafe, restul caracterelor (ghilimele, backslash, NULL) vor rămâne nemodificate.

Ghilimelele magice sunt activate implicit cu o instalare nouă de PHP şi cum efectul lor nu este vizibil întotdeauna, unii programatori nici nu sunt conştienţi de existenţa lor si de posibilele probleme care le pot cauza. De-a lungul timpului au existat mai multe critici aduse acestei facilităţi, lucru care a dus la considerarea ei ca fiind invechită, dintre care enumerăm:

  1. nu toate datele furnizate de catre utilizatorului unei aplicaţii web sunt menite a fi introduse într-o bază de date; ele pot fi stocate într-o sesiune sau afişate pe ecran, iar introducerea unui bachslash ar altera considerabil conţinutul acestor date
  2. nu toate datele introduse de către un utilizator sunt obţinute din surse aflate sub protecţia ghilimelelor magice; de exemplu o valoare poate fi obţinută de la utilizator, stocată în baza de date(deci sub influenţa ghilimelelor magice), apoi citită şi folosită intr-o alta operaţie sql unde ghilimelele magice nu au efect, aspect care ignorat de un programator ar putea avea consecinţe grave
  3. efectul de ghilimele magice se bazează pe folosirea funcţiei addslashes() care nu funcţionează corect pe string-urile Unicode, deci posibilitatea de a obţine un sql injection este ridicată (se recomandă în cazul inserărilor in baza de date folosirea funcţiilor de escape specifice, gen mysql_real_escape_string())
  4. adaugarea de ghilimele magice şi scoaterea lor atunci cand se cere poate fi, în funcţie de aplicaţie, o operaţie costisitoare ca timp, deci preferabil de evitat

În versiunea 5 a PHP ghilimelele magice au fost considerate depăşite si au fost marcate pentru eliminare odata cu versiunea 6 a limbajului de programare.

Vectorii

Vectorii, numiţi in engleză array, reprezintă cea mai avansată metodă de stocare a datelor în PHP. Sunt cele mai flexibile structuri de date: ele permint indecşi numerici sau alfanumerici, dar şi combinaţii; pot avea indecşi auto-incrementabili şi pot fi folosiţi ca stive, cozi, mulţimi, vectori uni, bi sau multi-dimenisonali şi pot stoca practic orice tip de dată. Este şi motivul pentru care în nucleul php există aproximativ 70 de funcţii definite pentru a uşura lucrul cu vectorii.

Aşadar aceşti vectori sunt o colecţie ordonată de obiecte numite generic elemente. Oricare dintre aceste elemente are o cheie unică în vector precum şi o valoare. Vectorii pot fi creaţi in 2 feluri, fie apeland functia array simplu, sau cu parametrii caz în care aceştia sunt atribuiţi vectorului.

$arr = array();
$arr = array(‘a’ => 1, ‘b’ => 2, ‘c’ => 3);
$arr = array(1 => ‘a’, 2 => ‘b’, 3 => ‘c’);
$arr = array(1, 2, 3);

Primul apel va crea un vector gol, cel de-al doilea un vector cu indecşi alfanumerici, al 3-lea cu indecşi numerici, iar pentru cel din urmă am furnizat doar valorile elementelor, pentru care indecşii de vor atribui automat prin autoincrementare.

Afisarea vectorilor

Spre deosebire de tipurile de date scalare(int, string, float) care pot fi afişate folosind funcţia echo, pentru vectori aceasta prezintă cateva neajunsuri la afişarea datelor. De exemplu, în timpul unui debug poate fi esenţial să avem informaţii nu doar despre datele aflate în elemnte, dar şi tipul lor de date, pe oricare din nivelele existente. Pentru a preîntâmpina această problemă PHP furnizează 2 funcţii de afişare recursivă a informaţiilor dintr-un array:

$array = array(1, 2, ‘foo’);
print_r($array);
var_dump($array);

Acest cod va produce următorul text:

Array
(
[0] => 1
[1] => 2
[2] => foo
)
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
string(3) “foo”
}

Observaţi cum, deşi ambele afişează elementele vectorilor, doar var_dump afişează şi tipul de dată al elementului respectiv. Mai mult, doar var_dump este capabil sa afişeze informaţiile despre mai mult de o variabiă, iar print_r are capacitatea de a trimite rezultatul către o variabilă prin specificarea celui de-al doilea parametru optional.

Standarde de codare

Începem prin a propune ceva usor şi … teoretic.

Standardele de codare, sau coding styles, reprezintă un set de reguli care trebuiesc aplicate atunci când un programator îşi scrie propriul cod. Aceste reguli sunt făcute pentru a uşura comunicarea într-o echipă relativ mare de programatori care lucrează la acelaşi proiect, dar totodată asigură că orice viitor programator nou va putea înţelege intr-un timp cât mai scurt ideile codului.

Majoritatea companiilor mari au deja un standard de codare stabilit pe care îl impun noilor angajaţi. De altfel pentru fiecare limbaj de programare exista un set de astfel de reguli oarecum standardizate, cum ar fi C++, PHP, C#, Java, Python.

Chiar şi ca free-lancer este bine să-ţi creezi propriul stil de codare, pentru a preveni neplacerile cauzate de revenirile la un cod pe care nu ai mai lucrat un timp îndelungat.

Preferaţi un anume standard? De ce?