Documente Academic
Documente Profesional
Documente Cultură
Programarea `n PHP
Ghid practic
Inteligen]\ artificial\ [i web semantic
Traian Anghel este profesor de informaticã ºi fizicã la Grupul ªcolar Industrial Anghel Saligny
din Brãila, av^nd gradul didactic I. Este absolvent al Facultãþii de Fizicã din cadrul Universitãþii
Bucureºti ºi al Cursului Postuniversitar de Specializare în Informaticã de la Universitatea Dunãrea
de Jos din Galaþi. A mai publicat Pagini Web dinamice. Introducere în programarea Web pentru
server, Editura Scorpion, Galaþi, 2002 ºi Dezvoltarea aplicaþiilor Web folosind XHTML, PHP ºi
MySQL, Editura Polirom, Iaºi, 2005. Domeniul sãu preferat de cercetare este cel al programãrii
Web pentru server ºi client.
www.polirom.ro
Editura POLIROM
Ia[i, B-dul Carol I nr. 4, P.O. BOX 266, 700506
Bucure[ti, B-dul I.C. Br\tianu nr. 6, et. 7, ap. 33, O.P. 37; P.O. BOX 1-728, 030174
004.42 PHP
Printed in ROMANIA
Cuvânt înainte de Sabin Buraga
POLIROM
2005
Aceastã carte este dedicatã Dorinei,
iubita, prietena ºi soþia mea.
Cuprins
Scopul lucrãrii
Apariþia acestei lucrãri este un rezultat al dorinþei noastre de a veni în întâmpinarea
aºteptãrilor unor largi categorii de programatori Web. Este vorba de programatorii care
ºi-au însuºit bazele limbajului de programare Web pentru server PHP ºi doresc sã
dezvolte aplicaþii în scopul utilizãrii lor în lumea realã. Aceste categorii sunt formate din
elevii de liceu (sunt vizaþi în mod special, dar nu exclusiv, elevii claselor având
specializarea matematicã-informaticã), profesorii care predau ºtiinþele exacte în învã-
þãmântul preuniversitar (în special, informatica), studenþii din primii ani ai facultãþilor
cu profil informatic sau similar cu acesta, administratorii siturilor Web din ºcoli, licee ºi
alte instituþii, dar ºi autodidacþi.
Dezvoltarea unei aplicaþii Web presupune, în etapa de implementare a acesteia,
gãsirea rãspunsurilor la întrebãri de genul cum fac acest lucru? Cine nu s-a lovit, în
cariera sa de programator Web, de întrebãri similare? Ne-am propus ca prezenta lucrare
sã vinã în sprijinul programatorilor, oferindu-le rãspunsuri la o serie de potenþiale
întrebãri.
Iatã o parte dintre întrebãrile pe care ºi le pot pune utilizatorii ºi la care vom încerca
sã rãspundem: Cum încarc un fiºier pe server? Cum pot scrie scripturi independente de
sistemul de gestiune a bazelor de date utilizat? Cum export datele dintr-un tabel MySQL
într-un document Excel? Cum procesez documentele XML? Cum export datele dintr-un
tabel MySQL într-un document XML ºi invers? Cum generez feed-uri RSS? Cum
integrez feed-urile RSS în situl propriu? Cum scriu clienþi pentru serviciile Web? Cum
includ în situl propriu rezultatele furnizate de un motor de cãutare cunoscut (e.g.,
Yahoo!, Google)? Cum determin execuþia planificatã a scripturilor PHP? Cum încarc
într-o secþiune a unei pagini date noi de pe server, fãrã reîncãrcarea întregii pagini?
Scurtã descriere
Printre subiectele dezvoltate în lucrare menþionãm: interacþiunea programelor PHP cu
serverele de baze de date ºi cu sistemul de fiºiere, pãstrarea stãrii sesiunii, autorizarea ºi
autentificarea utilizatorilor, introducere în comerþul electronic, trimiterea mesajelor de
10 INTRODUCERE
e-mail (în diverse formate ºi cu fiºiere ataºate), procesarea documentelor XML, constru-
irea serverelor ºi clienþilor pentru serviciile Web, generarea feed-urilor RSS, precum ºi
integrarea acestora în situl propriu, remote scripting prin utilizarea obiectului
XMLHttpRequest ºi planificarea execuþiei scripturilor PHP.
Am împãrþit conþinutul lucrãrii în zece capitole, fiecare dintre acestea incluzând
scurte pãrþi introductive urmate de aplicaþii care constituie rãspunsuri la potenþiale
întrebãri ale cititorilor. Aplicaþiile prezentate nu epuizeazã toate aspectele caracteristice
categoriei din care fac parte, ci ilustreazã soluþii posibile. Lucrarea include ºi douã anexe
a cãror lecturã poate fi utilã în vederea dezvoltãrii unor aplicaþii Web complexe ºi sigure.
În primul capitol, PHP, limbaj de programare pentru server, sunt prezentate
informaþii legate de limbajele de programare Web pentru server, în general ºi despre
PHP, în particular. Capitolul al doilea, PHP ºi bazele de date, este dedicat prezentãrii
interacþiunii PHP cu sistemele de gestiune a bazelor de date, în general, ºi cu MySQL,
în special. Sunt analizate aplicaþii care utilizeazã interfaþa ODBC (Open Database
Connectivity) ºi pachetele PEAR::DB ºi PEAR::Spreadsheet_Excel_Writer (e.g.,
aplicaþia care realizeazã exportul datelor MySQL într-un document Excel).
În al treilea capitol, PHP ºi sistemul de fiºiere, se prezintã aspecte referitoare la
gestionarea fiºierelor ºi directoarelor, precum ºi la modificarea drepturilor de acces ale
acestora, prin intermediul funcþiilor incluse puse la dispoziþie de PHP. Sunt analizate
aplicaþii utilizate pentru încãrcarea fiºierelor pe server (upload), rotaþia bannerelor
într-un sit ºi afiºarea imaginilor incluse într-un subdirector al sitului (galerie de imagini).
Al patrulea capitol, PHP ºi utilizarea sesiunilor, trateazã problema pãstrãrii stãrii
sesiunii prin utilizarea variabilelor de tip sesiune. Sunt prezentate douã aplicaþii: una
pentru realizarea operaþiilor de autorizare ºi autentificare a utilizatorilor, cealaltã pentru
comercializarea online a unor produse (magazin virtual), insistându-se pe crearea ºi
gestionarea coºului de cumpãrãturi ºi pe transmiterea comenzii prin e-mail.
În capitolul al cincilea, PHP ºi poºta electronicã, sunt prezentate noþiuni legate de
utilizarea poºtei electronice în PHP ºi se analizeazã modalitãþi de trimitere a mesajelor
de e-mail cu un conþinut divers, inclusiv (X)HTML, precum ºi cu fiºiere ataºate. De
asemenea, se prezintã o aplicaþie care poate fi folositã pentru gestionarea unui newsletter
(abonarea ºi dezabonarea utilizatorilor, precum ºi trimiterea mesajelor cãtre aceºtia),
care promoveazã noile produse ale unei companii.
Capitolul al ºaselea, PHP ºi XML, este dedicat utilizãrii PHP în conjuncþie cu
XML (Extensible Markup Language). Sunt prezentate noþiuni generale despre familia
XML (e.g., reguli sintactice, mecanisme de validare ºi spaþii de nume). Tot în acest
capitol sunt prezentate modele de procesare a documentelor XML, insistându-se asupra
SAX (Simple API for XML) ºi DOM (Document Object Model). Este tratatã transformarea
XSLT (Extensible Stylesheet Language Transformation) a documentelor XML pe client
(în browser) ºi pe server (folosind procesoare de sine stãtãtoare, aºa cum este Saxon, dar
ºi scripturi PHP), prin utilizarea foilor de stil XSLT. În scopul procesãrii ºi transformãrii
documentelor XML sunt utilizate biblioteci incluse în PHP sau care pot fi instalate,
inclusiv pachetele PEAR (PHP Extension and Application Repository). În acest capitol
se prezintã modalitãþi de a realiza exportul datelor XML în MySQL ºi invers prin
utilizarea limbajului PHP.
În capitolul al ºaptelea se face o introducere în serviciile Web, prezentându-se
standardele ºi limbajele care stau la baza realizãrii acestora. Este vorba de protocoalele
INTRODUCERE 11
Convenþii utilizate
Pentru a facilita parcurgerea lucrãrii au fost utilizate urmãtoarele convenþii:
scripturile PHP, codul CSS (Cascading Style Sheets), sursele XML, comenzile
MySQL precum ºi comenzile executate din shell-ul sistemului de operare sunt scrise
cu un corp de literã monospaþiat (Courier );
numele funcþiilor PHP predefinite ºi ale comenzilor MySQL sunt scrise, de asemenea,
cu un corp de literã monospaþiat (Courier );
secvenþele de înlocuire (aºa cum sunt parametrii formali din listele de argumente
incluse în prototipurile funcþiilor) sunt scrise cu un corp de literã monospaþiat
(Courier) italic;
numele fiºierelor, directoarelor ºi cãilor de localizare, extensiile de fiºiere, precum ºi
URL-urile sunt scrise utilizând stilul italic;
numele directivelor din fiºierelor de configurare, precum ºi valorile acestor directive
sunt scrise folosind corpul de literã Arial;
unele secvenþe de cod PHP, CSS, XML ºi de comenzi MySQL, precum ºi prototipurile
funcþiilor prezentate sunt delimitate printr-o barã verticalã plasatã în partea stângã;
12 INTRODUCERE
listing-urile care conþin scripturi PHP, precum ºi cod CSS, XML, XSLT sau XSD
sunt precedate de:
Cerinþe
Pentru a utiliza pe calculatorul dumneavoastrã aplicaþiile PHP propuse în aceastã lucrare,
este necesar sã instalaþi: un server Web (e.g., Apache), serverul de aplicaþii PHP
(versiunea 5.0 sau una superioarã) ºi serverul de baze de date MySQL.
Nu este necesar sã dispuneþi de o conexiune la Internet, deoarece puteþi utiliza
aplicaþiile PHP folosind bucla localã. În acest scop, utilizaþi adresa IP 127.0.0.1 sau
numele de domeniu localhost. Astfel, în caseta de adrese a browserului, URL-ul unui
script PHP trebuie introdus folosind unul dintre urmãtoarele douã modele: http://
127.0.0.1/nume_script.php sau http://localhost/nume_script.php.
De asemenea, trebuie sã instalaþi pe calculatorul dumneavoastrã o serie de biblioteci
PHP suplimentare (e.g., pachetele PEAR, bibliotecile XMLPHPRPC, NuSOAP), pe
care le puteþi procura de la adresele indicate în lucrare.
Documentarea completã asupra subiectelor discutate în lucrare presupune utilizarea
unor surse bibliografice diverse. În Bibliografia generalã sunt indicate lucrãri impor-
tante ºi adrese Web de la care puteþi obþine o parte din documentaþia necesarã. Restul îl
puteþi cãuta singuri. Nu uitaþi cã Internetul este un rezervor uriaº de informaþii care
(numai) atunci când sunt alese cu discernãmânt pot constitui elemente importante în
susþinerea pregãtirii dumneavoastrã ca programatori.
Cartea de faþã nu ar fi ajuns la forma actualã fãrã sprijinul domnului Sabin-Corneliu
Buraga, lector dr. la Facultatea de Informaticã a Universitãþii Alexandru Ioan Cuza
din Iaºi, cãruia îi exprimãm întreaga noastrã gratitudine.
Autorul
Brãila, octombrie 2005
INTRODUCERE 13
CAPITOLUL 1
CAPITOLUL 2
Modelul relaþional
Dintre modele conceptuale enumerate anterior, cel mai utilizat este modelul relaþional.
Bazele sale au fost puse de E.F. Codd de la IBM, în articolul A Relational Model of
Data for Large Shared Data Banks (Un model relaþional al datelor pentru bãnci mari
de date partajate), publicat în anul 1970. Sistemul care utilizeazã modelul relaþional se
numeºte sistem de gestiune a bazelor de date relaþionale (SGBDR).
PHP ªI BAZELE DE DATE 17
O bazã de date relaþionalã este alcãtuitã din unul sau mai multe tabele. Un tabel
include înregistrãri sau rânduri. O înregistrare este alcãtuitã din unul sau mai multe
câmpuri, numite ºi coloane sau atribute. Dacã valoarea unui câmp se determinã pe baza
valorilor altor câmpuri, acesta se numeºte câmp calculat. Între tabelele componente ale
unei baze de date se pot stabili relaþii.
Modelul relaþional impune ca înregistrãrile dintr-un tabel sã nu se repete, ceea ce
înseamnã cã un tabel nu poate avea douã rânduri identice! O înregistrare va fi identificatã
prin valoarea coloanelor sale. O coloanã sau o combinaþie de coloane care permite
identificarea înregistrãrilor se numeºte cheie candidatã. Aceasta trebuie sã respecte
urmãtoarele reguli:
unicitate (ceea ce înseamnã cã o cheie candidatã identificã în mod unic o înregistrare
în tabel);
valori nenule (coloanele care compun cheia trebuie sã aibã valori nenule);
compoziþie minimalã (cu alte cuvinte, nici o coloanã din cheie atunci când aceasta
este compusã nu poate fi eliminatã fãrã a distruge unicitatea înregistrãrii în tabel).
Dintre cheile candidate se selecteazã o cheie primarã. Se pot utiliza drept criterii de
selecþie a acesteia lungimea ºi uºurinþa în manipulare. O cheie a unui tabel poartã
numele de cheie externã dacã este cheia primarã a unui alt tabel.
Utilizarea eficientã a unei baze de date presupune organizarea optimizatã a datelor în
tabele. În acest scop, datele sunt incluse în entitãþi (tabele) distincte, printr-un proces de
optimizare numit normalizare, reprezentat de transformãri succesive supuse unor reguli.
În acest proces trebuie respectate urmãtoarele reguli:
se utilizeazã numai valori atomice ale coloanelor (nici o coloanã nu trebuie sã conþinã
valori compuse). De exemplu, este contraindicat sã se utilizeze o singurã coloanã
care sã conþinã numele ºi prenumele. În locul ei, trebuie sã se utilizeze douã coloane
distincte: una care conþine numele ºi alta care conþine prenumele;
cheia primarã este utilizatã în totalitate (sunt interzise tabelele în care o coloanã
depinde numai de o porþiune a cheii primare);
se utilizeazã numai cheia primarã (adicã un tabel care conþine informaþii despre mai
multe categorii de obiecte trebuie divizat în tabele separate, unite printr-o cheie
externã).
În modelul relaþional, implementarea unei baze de date implicã parcurgerea obligatorie
a trei etape, acestea fiind enumerate în continuare:
proiectarea bazei de date. În acest scop este utilizat un procedeu cunoscut sub numele
de modelare entitate-relaþie (modelare E-R). Procedeul include urmãtoarele patru
subetape:
o identificarea coloanelor, în care se þine seama de datele pe care trebuie sã le
stocheze baza de date;
o gruparea coloanelor în entitãþi sau tabele, astfel încât coloanele corelate sã se
grupeze în acelaºi tabel; existã posibilitatea ca o coloanã sã fie prezentã în mai
multe tabele;
o identificarea cheilor primare; o astfel de cheie poate fi reprezentatã de o coloanã,
de mai multe coloane (în acest caz, este vorba de o cheie compusã) sau de o nouã
coloanã creatã în acest scop;
o identificarea cheilor externe (are un rol important în modelarea E-R);
18 PROGRAMAREA ÎN PHP
Pentru exploatarea unui numãr mare de tipuri de baze de date relaþionale este utilizat
SQL (Structured Query Language).
Dezvoltarea limbajului SQL a fost iniþiatã în anii 70 de cãtre IBM, sub numele
seequel. Limbajul a primit numele actual în anul 1980. SQL a devenit standard
ANSI (American National Standards Institute) în anul 1986 (versiunea ANSI
curentã este SQL 99). În anul 1987, SQL a devenit standard ISO (International Organization
for Standardization).
Limbajul SQL include instrucþiuni care permit efectuarea urmãtoarelor operaþii:
crearea bazelor de date ºi a tabelelor;
introducerea datelor în tabele;
indexarea tabelelor;
stabilirea unor relaþii între tabelele componente ale unei baze de date;
actualizarea conþinutului tabelelor componente ale bazelor de date;
PHP ªI BAZELE DE DATE 19
Interfaþa ODBC
API-ul ODBC (Open Database Connectivity) a fost creat de Microsoft în anul 1992,
pentru accesarea de cãtre aplicaþii a surselor de date (Data Sources).
Sursa de date este un ansamblu de informaþii care include:
o bazã de date de un anumit tip, în care sunt stocate datele utilizate de aplicaþie;
un driver ODBC, care furnizeazã un set de comenzi specific sistemului de baze de
date utilizat;
parametrii conexiunii care va fi stabilitã de driver cu serverul de baze de date.
ODBC este o interfaþã de acces universal la date (Universal Data Access), având
drept caracteristicã interoperabilitatea, adicã posibilitatea de a accesa diverse tipuri de
baze de date utilizând acelaºi cod-sursã. Din acest motiv, ODBC este una dintre interfeþele
standard utilizate pentru dezvoltarea aplicaþiilor independente de motorul de baze de
date.
Puteþi sã obþineþi drivere ODBC pentru un numãr mare de tipuri de baze de date
(e.g., Microsoft SQL Server, Oracle, MySQL, PostgreSQL) de la adresa http://
www.sqlsummit.com/ODBCVend.htm.
Sistemul MySQL
MySQL este cel mai des utilizat SGBDR cu sursã deschisã, având versiuni pentru
majoritatea platformelor (e.g., UNIX/Linux, Windows, Mac OS X Server). Serverul
MySQL este multi-fir ºi multi-utilizator. În plus, este stabil ºi rapid, din aceste motive
20 PROGRAMAREA ÎN PHP
Definiþiile foilor de stiluri utilizate în fiºierul odbc.php sunt incluse în fiºierul style.css
(listing-ul 2.2).
2.2. Fiºierul style.css
tr#head {
background: #d9d9d9; color: #060606;
font-family: tahoma;font-size:14px;
}
td#brand {
background: #e9e9e9; font-family: tahoma;font-size:12px;
}
td#color {
font-family: tahoma;font-size:12px;
}
td#price {
font-family: tahoma;font-size:12px;
}
td#number {
font-family: tahoma;font-size:12px;
}
În locul bazei date MySQL puteþi utiliza o bazã de date Microsoft Access (în condiþiile
în care aveþi instalat în sistem acest program, parte a pachetului Microsoft Office). Vã
propunem ca exerciþiu utilizarea scriptului anterior folosind o sursã de date DSN care
are ca suport o bazã de date Microsoft Access. În prealabil, trebuie sã creaþi în Access
baza de date car ºi tabelul info, similare cu cele create anterior în MySQL. Este necesar
sã utilizaþi un alt nume pentru noua sursã DSN pe care trebuie sã o creaþi, de exemplu
carshop_access. Singura modificare pe care trebuie sã o aduceþi scriptului din listing-ul 2.1
este schimbarea numelui sursei de date (în loc de carshop introduceþi carshop_access).
24 PROGRAMAREA ÎN PHP
}
$data = str_replace(\r,,$data);
if ($data == ) {
$data = \n Nu au fost gasite inregistrari!\n;
}
header(Content-type: application/vnd.ms-excel);
header(Content-Disposition: attachment;
filename=extraction.xls);
header(Expires: 0);
header(Cache-Control: must-revalidate,
post-check=0,pre-check=0);
header(Pragma: public);
echo numberLines(4).$title.$header.\n.$data;
?>
Exportaþi date
din MySQL în Excel
Datele MySQL pot fi exportate într-un tabel Excel utilizând pachetul PEAR::
Spreadsheet_Excel_Writer. Pentru a instala acest tabel urmaþi procedura indicatã în
anexa A. Veþi constata cã trebuie instalat ºi pachetul PEAR::OLE (utilizat pentru
30 PROGRAMAREA ÎN PHP
for($i=1;$i<=4;$i++)
$cart->write(0,$i,,$titleFormat);
// Inaltimea randului
$cart->setRow(0,40);
// Latimea coloanelor
$cart->setColumn(0,$fields-1,15);
// Stabileste formatarea pentru header
$headerFormat =& $xls->addFormat();
$headerFormat->setBold();
$headerFormat->setFontFamily(Helvetica);
$headerFormat->setBold();
$headerFormat->setSize(10);
$headerFormat->setColor(red);
$headerFormat->setAlign(center);
// Este adaugat header-ul (se lasa un rand liber dupa titlu)
$cart->writeRow(2,0,$header,$headerFormat);
// Se precizeaza randul curent
$currentRow = 4;
// Sunt adaugate datele
foreach ( $data as $item ) {
$cart->writeRow($currentRow,0,$item);
$excelRow = $currentRow + 1;
/* Se creeaza un sir PHP care contine o
formula de calcul */
$formula = =PRODUCT(C.$excelRow .:D.$excelRow .);
/* Formula creata anterior este adaugata la
randul curent, in coloana cu indicele 4 */
$cart->writeFormula($currentRow,4,$formula);
$currentRow++;
}
// Randul de pornire in Excel
$startExcelRow = 4;
// Randul final in Excel
$endExcelRow = $currentRow;
/* Se creeaza un sir care defineste formula Excel
pentru calculul sumei totale */
$totalFormula = = SUM(E.$startExcelRow.
:E.$endExcelRow.);
/* Se defineste formatarea pentru celula care va
contine totalul */
$totalFormat =& $xls->addFormat();
$totalFormat->setFontFamily(Verdana);
$totalFormat->setBold();
$totalFormat->setTop(1);
$totalFormat->setBottom(1);
/* Se adauga textul Total inaintea celulei care va
PHP ªI BAZELE DE DATE 33
În exemplul prezentat, nimic nu este scris pe server. Documentul creat este trimis
clientului prin intermediul browserului. Dacã doriþi ca datele exportate sã fie introduse
într-un fiºier Excel care va fi scris pe server, este necesar ca în scriptul anterior sã
precizaþi numele fiºierului ce va fi creat ºi sã nu trimiteþi datele browserului. Numele
fiºierului se precizeazã astfel:
$xls = & new Spreadsheet_Excel_Writer();
CAPITOLUL 3
caracterul 0 (zero). Numãrul mode este alcãtuit din trei cifre care corespund, în ordine,
proprietarului, grupului din care face parte proprietarul ºi celorlalþi utilizatori. Fiecare
dintre cele trei cifre se obþine prin însumarea a trei numere care corespund celor trei
drepturi posibile: 4 înseamnã cã fiºierul filename poate fi citit, 2 cã se poate scrie
în el, iar 1 cã poate fi executat de utilizatorul respectiv. Valorile posibile ale oricãreia
dintre cifrele numãrului mode sunt 1 , 2, 3, 4, 5, 6 ºi 7. Funcþia chmod() întoarce
valoarea logicã TRUE în caz de succes ºi valoarea FALSE în caz de insucces. Aceastã
funcþie nu poate lucra cu fiºiere aflate la distanþã. De asemenea, nu poate fi utilizatã pe
platformã Windows.
Iatã un exemplu în care se foloseºte funcþia chmod() :
chmod(/home/mihai/test.php,0777)
if(!rmdir(/home/mysite/public_html/test)) {
echo <p>Eroare la stergerea directorului.</p>;
}
}
?>
acest motiv, în cazul deschiderii fiºierelor binare Windows, trebuie sã precizaþi doi
parametri (e.g., wb). În consecinþã, pentru a face scripturile dumneavoastrã portabile
pe platforme Windows, este înþelept sã utilizaþi modificatorul b, chiar dacã, iniþial,
scrieþi cod pentru o platformã UNIX/Linux, deoarece pe aceastã platformã modi-
ficatorul nu are nici un efect.
Al treilea argument (use_include_path ) este opþional. El poate avea numai
valoarea 1 ºi, dacã este prezent, indicã PHP-ului sã caute fiºierul într-o listã de directoare,
precizatã în fiºierul php.ini ca valoare a directivei include_path (cale de includere).
În exemplul anterior, pentru citirea fiºierului se utilizeazã funcþia fgets(), al cãrei
prototip este:
string fgets(int handle [,int length])
Funcþia citeºte o linie de text; handle este identificatorul de fiºier întors de funcþia
fopen(), iar length indicã numãrul maxim de octeþi care vor fi citiþi, minus unul
(acesta corespunde caracterului de terminare a liniei). În caz de succes, funcþia fgets()
întoarce un ºir, iar în caz de eroare, întoarce valoarea logicã FALSE. Dacã programatorul
nu precizeazã argumentul length, se utilizeazã valoarea implicitã, care este de 1.024 de
octeþi.
Este important de reþinut cã PHP alocã un spaþiu de memorie de length octeþi
înainte de a citi fiºierul. Acest lucru înseamnã cã se vor aloca 1.000.000 de
octeþi (aproximativ 1 MB) chiar dacã o linie a fiºierului are numai câteva
caractere. Rezultã cã argumentul length trebuie ales cu atenþie, astfel încât sã nu se
consume resurse în mod inutil.
Pentru închiderea unui fiºier se utilizeazã funcþia fclose(), al cãrei prototip este
urmãtorul:
bool fclose (resource handle)
Funcþia fclose() încearcã sã închidã fiºierul specificat prin identificatorul handle.
În caz de succes, întoarce TRUE, iar în caz de insucces, întoarce FALSE .
În exemplul prezentat anterior este utilizatã funcþia feof(), care testeazã dacã s-a
ajuns la sfârºitul fiºierului. Prototipul acestei funcþii este:
int feof(int handle)
Funcþia feof() întoarce valoarea logicã TRUE dacã pointerul de fiºier este la sfârºitul
fiºierului al cãrui identificator este handle sau dacã a apãrut o eroare ºi valoarea logicã
FALSE în caz contrar.
Pentru citirea fiºierelor mai poate fi utilizatã funcþia fread(), al cãrei prototip este
precizat în continuare:
string fread(int handle, int length)
Funcþia fread() citeºte din fiºierul precizat prin identificatorul handle ºi întoarce
sub formã de ºir un numãr maxim de octeþi specificat prin length. Dacã înainte de
parcurgerea celor length octeþi este întâlnit indicatorul de sfârºit de fiºier (end of file),
operaþia de citire se încheie.
De asemenea, pentru citirea fiºierelor mai poate fi utilizatã funcþia file(), al cãrei
prototip este:
PHP ªI SISTEMUL DE FIªIERE 39
Scrierea în fiºiere
Pentru scrierea în fiºiere se utilizeazã funcþia fwrite() (puteþi utiliza ºi fputs(), care
este un alias pentru fwrite()):
int fwrite(int handle,string string [,int length])
Funcþia fwrite() scrie în fiºierul precizat prin identificatorul handle conþinutul
ºirului string. Dacã argumentul opþional length este furnizat funcþiei, scrierea se
opreºte dupã ce a fost scris un numãr de octeþi egal cu length, chiar dacã lungimea
ºirului string este mai mare. În caz de succes, funcþia întoarce numãrul de octeþi scriºi
în fiºier, iar în caz de eroare, 1.
Creaþi în directorul-rãdãcinã al sitului Web un subdirector pe care îl puteþi denumi
files unde veþi salva fiºierele ce conþin scripturile prezentate în acest capitol.
În listing-ul 3.1 este prezentat un script care oferã posibilitatea adãugãrii ºi, respectiv,
ºtergerii unor rânduri în fiºierul .htpasswd:
3.1. Fiºierul fileop.php
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Citirea si scrierea in fisiere</title>
</head>
<body>
<?php
$file_directory = /usr/home/mysite;
$error = Fisierul nu a putut fi deschis.;
$file_contents = file($file_directory./.htpasswd);
sort($file_contents);
$sizeof = count($file_contents);
if ($_POST[action] == add) {
if($filehandle = fopen($file_directory./.htpasswd,a)){
$encrypted_pass = crypt($_POST[pwd]);
fputs($filehandle, $_POST[user] . : .
$encrypted_pass .\n);
fclose($filehandle);
echo Utilizatorul <b> . $_POST[user] .
</b> a fost adaugat.;
} else {
40 PROGRAMAREA ÎN PHP
echo($error);
}
}
if ($_POST[action] == delete) {
if($filehandle = fopen($file_directory/.htpasswd,w)) {
for($i=0; $i < $sizeof; $i++) {
if($file_contents[$i] != $_POST[todelete]) {
fputs($filehandle, $file_contents[$i]);
}else {
$flag = 1; }
}
fclose($filehandle);
if($flag) {
echo Utilizatorul <b> . $_POST[todelete] .
</b> a fost sters.;
} else {
echo Utilizatorul . $_POST[todelete] .
nu a putut fi sters.;
}
} else {
echo($error);
}
}
$file_contents = file($file_directory./.htpasswd);
sort($file_contents);
$sizeof = count($file_contents);
?>
<form method=post
action=<?php echo $_SERVER[PHP_SELF]; ?>>
<input type=hidden name=action value=delete />
<table cellpadding=5>
<tr>
<td><select name=todelete>
<?php
for($i=0; $i < $sizeof; $i++) {
$user = explode(:, $file_contents[$i]);
echo <option value=\$file_contents[$i]\>$user[0]
</option>;
}
?>
</select></td>
<td><input type=submit value=Sterge /></td>
</tr></table>
</form>
<form method=post
action=<?php echo $_SERVER[PHP_SELF]; ?>>
PHP ªI SISTEMUL DE FIªIERE 41
Fiºiere temporare
Pentru a crea fiºiere temporare trebuie sã utilizaþi funcþia tmpfile(), al cãrei prototip este:
resource tmpfile(void)
Funcþia creeazã un fiºier temporar (având un nume generat aleatoriu) în directorul
curent ºi îl deschide pentru scriere. Acest fiºier va fi închis ºi ºters odatã cu utilizarea
42 PROGRAMAREA ÎN PHP
if($number > 0) {
echo <p style=text-align: center; font-size: 24px>
Directorul conþine: .$number. fotografii</p>;
echo <table align=\center\ cellpadding=\5\>;
for($i=0;$i<$number;$i++){
$file=$photodir.$photos[$i];
if($i%2 == 0)
echo <tr><td><img src=\$file\ /></td>;
else
echo <td><img src=\$file\ /></td></tr>;
}
echo </table>;
}
else echo <p style=text-align: center>
Nu sunt fotografii!</p>;
?>
?>
<form method=post
action=<?php echo $_SERVER[PHP_SELF]; ?>
enctype=multipart/form-data>
<input type=hidden name=max_file_size value=10000 />
<table border=0 cellspacing=0 cellpadding=5>
<tr>
<td>Fisier: </td>
<td><input type=file name=file /></td>
</tr>
<tr>
<td colspan=2><input type=submit
name=submit value=Trimite /></td>
</tr>
</table>
</form>
<?php
}
?>
</body>
</html>
Pot fi încãrcate atât fiºiere text, cât ºi fiºiere binare. Dupã cum puteþi constata în
scriptul conþinut în listing-ul 3.3, pentru a se putea realiza transmiterea fiºierului la
server, trebuie ca atributele enctype ºi method ale elementului form sã aibã valorile
multipart/form-data ºi, respectiv, post.
Tot în scriptul inclus în listing-ul 3.3 se poate constata cã formularul XHTML conþine
urmãtoarele trei elemente:
un element input de tip hidden, al cãrui atribut name are ca valoare directiva
max_file_size (care indicã browserului dimensiunea maximã permisã, exprimatã
în bytes, a fiºierelor care vor fi trimise serverului; în exemplul anterior, valoarea
directivei este 10.000; reþineþi cã aceasta nu poate depãºi valoarea directivei
46 PROGRAMAREA ÎN PHP
situaþia în care este precizat un anumit director (ca valoare a directivei upload_tmp_dir în
php.ini). Directorul implicit în care sunt stocate fiºierele încãrcate pe server poate fi
schimbat prin setarea corespunzãtoare a variabilei TMPDIR în mediul în care se executã
PHP.
Pentru a pãstra fiºierelor încãrcate pe discul fix al serverului, acestea trebuie copiate
din directorul temporar în care au fost încãrcate în directorul preferat. Astfel, pentru a
copia un fiºier din directorul temporar într-un subdirector al directorului-rãdãcinã al
sitului Web se poate utiliza o secvenþã similarã cu urmãtoarea:
<?php
$dir_c = getcwd();
if (is_uploaded_file($_FILES[filename][tmp_name]) &&
is_writable($dir_c./myfiles/)) {
copy($_FILES[filename][tmp_name],
$dir_c./myfiles/.$_FILES[filename][name]);
echo Operatia de copiere s-a efectuat!;
}
else
echo Operatia de copiere nu s-a putut efectua!;
?>
În exemplul anterior, fiºierul încãrcat este copiat în subdirectorul myfiles al direc-
torului curent. Funcþia is_uploaded_file() întoarce valoarea TRUE dacã fiºierul al
cãrui nume este transmis ca argument a fost încãrcat pe server, iar funcþia is_writable()
întoarce TRUE dacã se poate scrie în directorul al cãrui nume îi este transmis ca argument.
Pentru a muta un fiºier încãrcat pe server din directorul temporar într-un alt director
precizat de programator poate fi utilizat urmãtorul fragment de cod:
<?php
$uploaddir = getcwd()./myfiles/;
$uploadfile = $uploaddir. $_FILES[filename][name];
if (move_uploaded_file($_FILES[filename][tmp_name],
$uploadfile) && is_writable($uploadfile))
echo Fisierul a fost mutat!;
else
echo Fisierul nu a fost mutat;
?>
Dupã cum se observã, pentru a realiza operaþia de mutare a fost utilizatã funcþia
move_uploaded_file(), care primeºte douã argumente: primul este numele temporar
al fiºierului încãrcat, iar cel de-al doilea, numele fiºierului-destinaþie (în care va fi
mutat). Dacã acesta din urmã existã, va fi suprascris. Funcþia verificã dacã fiºierul al
cãrui nume transmis ca prim argument este valid (i.e., dacã este numele unui fiºier
încãrcat în directorul temporar prin HTTP post) ºi apoi mutã fiºierul respectiv la
destinaþie. Dacã acest nume nu este valid, funcþia va întoarce valoarea FALSE . Este
necesar ca PHP sã aibã drept de scriere în directorul-destinaþie (myfiles, în exemplul
anterior). În situaþia în care fiºierul este valid, dar nu poate fi mutat din diverse motive,
funcþia va întoarce, de asemenea, valoarea FALSE (fiind generatã ºi o eroare PHP de tip
warning).
48 PROGRAMAREA ÎN PHP
Este evident cã PHP trebuie sã aibã dreptul de a scrie în directorul myfiles, pentru ca
operaþiile de copiere ºi, respectiv, mutare sã poatã fi efectuate. Atunci când scrieþi
aplicaþii, þineþi seama cã PHP se executã în contul serverului Web (adesea, acest cont este
nobody), având drepturi limitate asupra fiºierelor ºi directoarelor. Rezultã cã va trebui sã
modificaþi drepturile de acces ale directorului-destinaþie myfiles pentru ca PHP sã poatã
scrie în el.
Este posibil sã se încarce mai multe fiºiere prin intermediul aceluiaºi formular
XHTML dacã acesta este similar cu cel prezentat în continuare (în exemplul oferit, pot
fi încãrcate trei fiºiere):
<form method=post action=<?php echo $PHP_SELF; ?>
enctype=multipart/form-data>
<input type=hidden name=max_file_size
value=1000000 />
Fisier 1: <input type=file name=file[] /><br />
Fisier 2: <input type=file name=file[] /><br />
Fisier 3: <input type=file name=file[] /><br />
<input type=submit name=submit value=Trimite />
</form>
Informaþiile despre cele trei fiºiere încãrcate în exemplul prezentat vor fi gãsite ca
valori ale elementelor tabloului multidimensional $_FILES[file]. De exemplu,
mãrimile fiºierelor sunt date de $_FILES[file][size][0] , $_FILES[file]
[size][1] ºi $_FILES[file][size][2] .
Rotaþia bannerelor
Vã prezentãm în continuare o aplicaþie (listing-ul 3.4) care realizeazã rotaþia ºi afiºarea
unor bannere publicitare. Vom presupune cã numele fiºierelor în care sunt stocate
bannerele încep cu secvenþa banner (care conþine ºase caractere) ºi cã fiºierele se aflã în
subdirectorul banner al directorului curent.
3.4. Fiºierul rotation.php
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Rotatia bannerelor</title>
</head>
<body>
<?php
session_start();
$bannerdir= getcwd()./banner/;
if (is_dir($bannerdir)) {
if($dh=opendir($bannerdir)) {
PHP ªI SISTEMUL DE FIªIERE 49
for($i=0;$i<$dir_length;$i++){
if(strrpos($files[$i], .) == false){
echo <li>.$files[$i]./</li>;
display($file_dir./.$files[$i]);
}
else{
echo <li><a href=.$file_dir./.
$files[$i].>.$files[$i].</a></li>;
}
}
echo </ul>;
}
closedir($hd);
}
}
?>
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Afisare recursiva a directoarelor</title>
</head>
<body>
<?php
display(.);
?>
</body>
</html>
Structura arborescentã
a unui director
CAPITOLUL 4
Prin sesiune se înþelege succesiunea de accesãri ale unor pagini aflate în componenþa
aceluiaºi sit Web de cãtre un utilizator, în aceeaºi fereastrã a navigatorului Web. O
sesiune se încheie în momentul în care utilizatorul închide navigatorul.
Protocolul HTTP, care stabileºte reguli pentru sintaxa ºi modul de transmitere a
mesajelor între clientul ºi serverul Web, precum ºi acþiunile acestora ca rãspunsuri la
diverse comenzi, este lipsit de stãri, adicã nu are posibilitatea de a memora un utilizator
ºi acþiunile acestuia (i.e., serverul Web nu memoreazã cererile clientului), deoarece
comenzile sunt executate independent, fãrã nici o informaþie despre comenzile executate
anterior. Acesta este motivul pentru care este dificil sã se realizeze situri ce rãspund
inteligent la acþiunile utilizatorilor. Tot din acest motiv, valorile celor mai multe variabile
PHP utilizate într-un script se pierd la încheierea execuþiei acestuia. Existã situaþii în
care valorile unor variabile ar trebui pãstrate în cadrul unei sesiuni, de exemplu în
siturile care utilizeazã autentificarea utilizatorilor.
Problema pãstrãrii valorilor unor variabile în cadrul unei sesiuni a primit diverse
soluþii. Douã dintre acestea presupun utilizarea variabilelor cookie ºi, respectiv,
a variabilelor-sesiune. Soluþiile amintite au fost implementate în toate limbajele
de programare pentru Web, inclusiv în PHP.
În acest capitol vom face câteva referiri la variabilele cookie ºi vom prezenta pe larg
folosirea variabilelor-sesiune în limbajul PHP.
Valorile variabilelor cookie sunt pãstrate pe calculatorul utilizatorului ºi folosite în
diverse scopuri, dintre care menþionãm:
PHP ªI UTILIZAREA SESIUNILOR 53
Dupã prelucrarea datelor ºi afiºarea lor, sesiunea curentã poate fi curãþatã (ceea ce
înseamnã cã vor fi ºterse atât datele din fiºierul creat odatã cu sesiunea, cât ºi cele din
tabloul $_SESSION ) ºi, la încheierea acesteia, distrusã (ceea ce înseamnã cã vor fi ºterse
atât fiºierul creat odatã cu sesiunea, cât ºi tabloul $_SESSION). În cele ce urmeazã sunt
explicate modalitãþile de efectuare a celor douã operaþiuni.
Curãþarea sesiunii se poate realiza prin utilizarea funcþiei session_unset(), al
cãrei prototip este urmãtorul:
void session_unset(void)
De asemenea, ºtergerea datelor dintr-o sesiune se poate realiza ºi prin intermediul
funcþiei array(), dupã cum se aratã în continuare:
$_SESSION=array();
Distrugerea unei sesiuni se realizeazã prin intermediul funcþiei session_destroy(),
al cãrei prototip este:
bool session_destroy(void)
Funcþia session_destroy() întoarce valoarea logicã TRUE, dacã sesiunea a fost
distrusã, ºi valoarea logicã FALSE, în caz contrar.
Identificatorul de sesiune este întors de funcþia session_id(), al cãrei prototip
este prezentat în continuare:
string session_id([string id])
Reþineþi cã variabila supergobalã $_SESSION poate stoca nu numai variabile scalare,
ci ºi tablouri, ca în listing-ul 4.2:
4.2. Fiºierul array.php
<?php
$_SESSION[fructe][]=mere;
$_SESSION[fructe][]=banane;
?>
Pentru a evita problemele care pot sã aparã în cazul utilizãrii unor browsere (probleme
ce se manifestã prin posibilitatea pierderii conþinutului unui formular XHTML dupã ce
utilizatorul revine, utilizând butonul Back al browserului, pentru a face unele modificãri
în câmpurile acestuia), este indicat sã plasaþi imediat dupã apelul funcþiei
58 PROGRAMAREA ÎN PHP
<td>Nume utilizator:</td>
<td><input type=text name=user /></td>
</tr>
<tr>
<td>Parola:</td>
<td><input type=password name=passw /></td>
</tr>
<tr>
<td><input type=reset value=Anuleaza /></td>
<td><input type=submit name=submit value=Login />
</td></tr>
</table>
</form></center>
<?php
}
?>
</body>
</html>
Utilizatorul trebuie
sã introducã datele
de identificare.
60 PROGRAMAREA ÎN PHP
Datele de identificare
nu sunt corecte. Nu
este autorizat
accesul în sistem.
Datele de identificare
sunt corecte. Este
autorizat accesul
în sistem.
Dupã cum se observã în listing-ul 4.7, scriptul care realizeazã operaþia de logout
(inclus în fiºierul logout.php) redirecteazã browserul cãtre pagina de login (login.php),
trimiþând acestuia un antet Location, prin intermediul funcþiei PHP header().
Magazin virtual
Comerþul electronic înseamnã vânzarea de bunuri ºi servicii prin intermediul
tehnologiilor Web. Un material complet pe aceastã temã îl puteþi gãsi în capitolul
Afaceri pe Web al lucrãrii Sabin Buraga, Proiectarea siturilor Web. Design ºi
funcþionalitate, ediþia a II-a, Polirom, Iaºi, 2005. În aceastã secþiune ne vom limita
numai la prezentarea unui exemplu de implementare a coºului de cumpãrãturi utilizat de
o aplicaþie de tip magazin virtual.
62 PROGRAMAREA ÎN PHP
Aplicaþia prezentatã, numitã MailShop, oferã spre vânzarea online douã categorii de
produse software pentru poºta electronicã: clienþi de e-mail ºi servere de e-mail.
Utilizând aplicaþia prezentatã, cumpãrãtorii pot sã realizeze urmãtoarele acþiuni:
vizualizarea produselor oferite spre vânzare în magazin (produse software pentru
poºta electronicã);
crearea unui coº de cumpãrãturi ºi efectuarea unor operaþii ca: adãugarea unor
produse în coº, afiºarea conþinutului acestuia, ºtergerea unor produse din coº,
ºtergerea întregului conþinut al coºului;
introducerea datelor de identificare ale cumpãrãtorului;
trimiterea comenzii departamentului de vânzãri al magazinului.
color: #0072aa ;
}
td#price {
font-family: tahoma; font-size: 14px; color: #ff0000;
}
td#info {
font-family: tahoma; font-size: 14px; color: #ff0000;
}
td#info_pers {
font-family: tahoma; font-size: 16px;color: #495f80;
font-style: bold;
}
td#total {
font-family: tahoma; font-size: 16px;color: #ff0000;
font-style: bold; border-top: 1px solid #ff0000;
border-bottom: 1px solid #ff0000;
}
class=link-car>Finalizeaza comanda</a></td>
</tr>
</table>
<?php
if(isset($_GET[op]) && $_GET[op] == getcart)
getcart();
if(isset($_GET[op]) && $_GET[op] == delete) {
$id = $_GET[id];
del_product($id);
getcart();
}
if(isset($_GET[op]) && $_GET[op] == delete_all) {
del_all($id);
}
if(isset($_GET[op]) && $_GET[op] == final) {
buy();
}
if(isset($_GET[op]) && $_GET[op] == ad_info) {
add_info();
}
if(isset($_GET[op]) && $_GET[op] == storage) {
storage();
}
if(isset($_GET[op]) && $_GET[op] == getproducts) {
?>
<table align=center cellspacing=5>
<tr>
<td class=title colspan=2 align=center>
Alege produsul</td>
</tr>
<tr>
<td align=center>
<a href=index.php?op=getproducts&type=client
class=menu>E-mail Clients</a> | </td>
<td alig=center>
<a href=index.php?op=getproducts&type=server
class=menu>E-mail servers</a></td>
</tr>
</table>
<?php
if($_GET[op] == getproducts &&
$_GET[type] == client)
display(client);
}
?>
</body>
</html>
alt=\$product\
align=\center\ width=75></td>
<td id=product width=800>$product</td>
</tr>
<tr>
<td id=company>by $company</td>
</tr>
<tr><td id=price>Pret: $price</td>
</tr>;
if($av)
echo <tr><td><a class=link href= .
$_SERVER[PHP_SELF].?
op=getproducts&type=$type&id= . $id .
>Adauga produsul in cos</a></td></tr>;
else
echo <tr><td class=unavailable>
Currently unavailable</td></tr>;
echo </table>;
}
}
else
echo Nu sunt inregistrate produse;
}
// Functie pentru adaugarea unui produs in cos
function add($id){
if(isset($_SESSION[products]))
foreach($_SESSION[products] as $product_id)
if($product_id == $id)
$exista = true;
if($exista) {
$info = Produsul exista in cos!;
}
else{
$_SESSION[products][$id] = $id;
$info = Produsul a fost adaugat in cos!;
}
return $info ;
}
// Functie pentru vizualizarea continutului cosului
function getcart(){
if(!count($_SESSION[products]))
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Nu sunt produse in cos!</td>
</tr>
</table>;
68 PROGRAMAREA ÎN PHP
else{
$total = 0;
foreach($_SESSION[products] as $id) {
$result=mysql_query(SELECT * FROM email
WHERE id=$id);
$row=mysql_fetch_array($result);
echo <table cellspacing=5 cellpadding=5
style=border-top: 1px solid #0072aa;>;
$id=$row[id];
$product=$row[product];
$company=$row[company];
$price=$row[price];
$urlimage=$row[urlimage];
$total += $price;
echo <tr>
<td rowspan=4>
<image src=\images/$urlimage\
alt=\$product\ align=\center\
width=75></td>
<td id=product width=800>$product</td>
</tr>
<tr>
<td id=company>by $company</td>
</tr>
<tr>
<td id=price>Pret: $price</td>
</tr>
<tr>
<td colspan=2 align=center>
<a href=index.php?op=delete&id=$id
class=link>Sterge</a></td>
</tr>;
echo </table>;
}
echo <table align=center>
<tr>
<td colspan=2 id=total align=center>
Pret total: . $total . </td>
</tr>
</table>;
}
}
// Functie pentru trimiterea comenzii
function buy(){
$nr = count($_SESSION[products]);
if(!$nr) {
PHP ªI UTILIZAREA SESIUNILOR 69
<tr>
<td id=company>by $company</td>
</tr>
<tr>
<td id=price>Pret: $price</td>
</tr>;
echo </table>;
$list .= $product . ( . $company .). Pret .
$price . <br />;
}
echo <table align=center>
<tr>
<td id=totalalign=center>Pret total:
. $total . </td>
</tr>
</table>;
$list .= <br />Pret total: . $total;
$cart .= $list;
$ok_expeditor=mail($cumparator,Confirmare comanda,
$cart,$headers);
$destinatar=sales@localhost;
$cart = Domnul/doamna .$_SESSION[date][sname];
$cart .= .$_SESSION[date][fname].<br />;
$cart .= avand adresa: .
$_SESSION[date][address] .
,<br /> a comandat produsele:<br />;
$cart .= $list;
$ok_distributie=mail($destinatar,comanda,
$cart, $headers);
if($ok_expeditor && $ok_distributie)
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Comanda va fi expediata
in cel mult sapte zile!</td>
</tr>
</table>;
session_unset();
session_destroy();
}
}
/* Functie pentru adaugarea datelor personale
ale cumparatorului */
function add_info(){
if(!isset($_SESSION[date])){
?>
PHP ªI UTILIZAREA SESIUNILOR 71
<form action=index.php>
<table>
<tr>
<td id=info_pers>Nume</td>
<td><input type=text name=sname /></td>
</tr>
<tr>
<td id=info_pers>Prenume</td>
<td><input type=text name=fname /></td>
</tr>
<tr>
<td id=info_pers>Adresa</td>
<td><input type=text size=25
name=address /></td>
</tr>
<tr>
<td id=info_pers>Email</td>
<td><input type=text name=email></td>
</tr>
<tr>
<td colspan=2 align=center>
<input type=submit name=submit
value=Trimite /></td>
</tr>
<input type=hidden name=op value=storage>
</table>
</form />
<?php
}
else
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Datele personale sunt
deja stocate!</td>
</tr>
</table>;
}
// Functie pentru stocarea datelor de identificare
function storage(){
if(!isset($_GET[sname]) || empty($_GET[sname])
|| is_numeric($_GET[sname]))
$error = Numele nu este corect</br >;
if(!isset($_GET[fname]) || empty($_GET[fname])
|| is_numeric($_GET[fname]))
$error .= Prenumele nu este corect</br >;
if(!isset($_GET[address]) || empty($_GET[address])
72 PROGRAMAREA ÎN PHP
|| is_numeric($_GET[address]))
$error .= Adresa nu este corecta</br >;
if(!validateEmail($_GET[email]))
$error .= Adresa de e-mail nu este corecta;
if(!$error) {
$_SESSION[date][sname] = $_GET[sname];
$_SESSION[date][fname] = $_GET[fname];
$_SESSION[date][address] = $_GET[address];
$_SESSION[date][email] = $_GET[email];
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Datele personale au fost
stocate!</td>
</tr>
</table>;
}
else
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Erori:<br /> $error</td>
</tr>
</table>;
}
// Functie pentru validarea adresei de e-mail
function validateEmail($email){
$pattern =
/^\w[\w\d]+(\.[\w\d]+)*@\w[\w\d]+(\.[\w\d]+)*\.
[a-z]{2,4}$/i;
return preg_match($pattern,$email);
}
// Functie pentru eliminarea unui produs din cos
function del_product($id) {
unset($_SESSION[products][$id]);
}
// Functie pentru eliminarea tuturor produselor din cos
function del_all() {
session_unset();
session_destroy();
echo <table cellspacing=5 cellpadding=5>
<tr>
<td id=info>Au fost eliminate toate produsele
din cos!</td>
</tr>
</table>;
}
?>
PHP ªI UTILIZAREA SESIUNILOR 73
Cumpãrãtorul
poate adãuga în
coº oricare dintre
produsele oferite,
disponibile pentru
vânzare.
Cumpãrãtorul
poate vizualiza
oricând conþinutul
coºului.
Finalizarea
cumpãrãturilor
CAPITOLUL 5
2. agenþii de transfer de mesaje (Mail Transport Agent sau Mail Transfer Agent, pe
scurt, MTA): transportã mesajele de la sursã la destinaþie (sunt, de obicei, daemoni
care funcþioneazã în fundal; exemple: sendmail, qmail, Microsoft Exchange, Kerio
MailServer etc.);
3. agentul local de livrare (Local Delivery Agent sau, pe scurt, LDA): preia mesajele
sosite pe maºina-destinaþie ºi le distribuie în cãsuþele poºtale ale utilizatorilor). Acest
subsistem, împreunã cu cel anterior, alcãtuiesc un server de e-mail.
Salutari!
.
Fiecare antet se compune dintr-o linie de text ASCII care conþine numele antetului,
caracterul : (douã puncte) ºi o valoare. În continuare sunt prezentate câteva anteturi
RFC 822 (primele patru sunt legate de transportul mesajelor de e-mail):
To antetul are ca valoare adresa/adresele de e-mail ale destinatarilor primari;
From acest antet are ca valoare adresa de e-mail a pesoanei care a creat mesajul;
Cc valoarea acestui antet este reprezentatã de adresele de e-mail ale destinatarilor
secundari, care primesc o copie (carbon copy) a mesajului;
Bcc acest antet are ca valoare adresa/adresele pentru blind carbon copy;
Replay-To valoarea acestui antet este adresa de e-mail la care pot fi trimise
rãspunsurile;
Date valoarea antetului este reprezentatã de data ºi ora la care a fost trimis mesajul;
Subject acest antet are ca valoare o descriere scurtã a mesajului, afiºabilã pe o
singurã linie.
RFC 822 precizeazã cã utilizatorii pot sã-ºi defineascã anteturi personale, cu condiþia
ca acestea sã înceapã cu ºirul de caractere X-, utilizat pentru a evita conflictele dintre
aceste anteturi ºi cele definite oficial în standard.
Standardul MIME
Standardul RFC 822 permite trimiterea unor mesaje pur textuale, bazate pe codificarea
US-ASCII (pe 7 biþi) a caracterelor. În situaþiile în care trebuie trimise fiºiere binare sau
sunt necesare alte seturi de caractere, acest standard nu mai este suficient. Soluþia este
standardul MIME (RFC 1341 ºi RFC 1521). Acest nume este acronimul pentru Multi-
purpose Internet Mail Extensions (extensii de poºtã elecronicã pentru scopuri multiple).
MIME este utilizat ca un superset pentru RFC 822, extinzând posibilitãþile iniþiale
oferite de acest standard prin introducerea unor anteturi MIME (MIME headers) care
permit un control sporit asupra a ceea ce poate conþine un mesaj de e-mail ºi asupra
modului în care acest conþinut este interpretat.
Standardul MIME permite introducerea în corpul mesajului a unui conþinut
formatat XHTML, utilizarea unor seturi de caractere internaþionale (diferite de
US-ASCII), dar ºi trimiterea unor fiºiere ataºate mesajului. Astãzi, cei mai
mulþi clienþi de e-mail suportã acest standard ºi pot trimite ºi primi fiºiere conþinând
tipuri arbitrare de date.
Deoarece respectã RFC 822, mesajele MIME pot fi trimise utilizând programele ºi
protocoalele de poºtã electronicã existente. De exemplu, pentru a trimite un fiºier binar
78 PROGRAMAREA ÎN PHP
Funcþia mail()
În limbajul PHP, trimiterea mesajelor de e-mail se face prin intermediul funcþiei mail().
Aceasta are urmãtorul prototip:
bool mail(string to,string subject,string message
[,string additional_headers])
Semnificaþiile celor patru parametri sunt:
to conþine adresa de e-mail a destinatarului. Pot fi specificate mai multe adrese
simultan, separate prin virgulã;
subject conþine subiectul mesajului ce va fi trimis;
message conþine mesajul care va fi trimis la destinatar;
additional_headers reprezintã anteturi adiþionale ca From, To, Reply-To, Cc,
Bcc. Ele trebuie separate prin secvenþa/caracterul linie nouã: \r\n (pe sisteme
Windows) ºi \n (pe sisteme UNIX/Linux). Antetul Cc este sensibil la distincþia dintre
majuscule ºi minuscule ºi trebuie scris Cc pe sisteme Win32, iar antetul Bcc nu este
suportat pe sistemele Win32.
Adresa de e-mail poate specificatã în douã moduri:
user@domain, ca în exemplul urmãtor:
imihai@example.com
name <user@domain>, ca în exemplul urmãtor:
Mihai Ionescu <imihai@example.com)
Nu trebuie sã introduceþi vreun caracter linie nouã în parametrii to sau subject . În
caz contrar, mesajul nu va fi trimis. Iatã un exemplu de utilizare a funcþiei mail():
mail(tdaniel@example.com,informatii,
In curand iti voi trimite informatii despre proiect,
From: atraian@example.com\r\n
.Reply-To: atraian@example.com);
PHP ªI POªTA ELECTRONICÃ 81
<?php
}
else{
$info=Nume: .$_POST[nume_exp].<br />;
$info.=Prenume: .$_POST[prenume_exp].<br />;
$info.=Email: .$_POST[mail_exp].<br />;
$message=Mesaj: .$_POST[mesaj].\n;
$email_to=traian@localhost;
$subject=Sugestii pentru pagina Web;
$header=From: contact@localhost\r\n;
$header.=Reply-To: .$_POST[mail_exp];
@mail($email_to,$subject,$message,$header)
or die(Eroare: mesajul nu a fost trimis);
echo <p style=font-size: 14px; color: red;>
Ati trimis datele urmãtoare: <p>.
<p style=font-size: 12px; color: blue;>.
$info.<br />.$message.</p>;
echo <p style=font-size: 14 px>
Multumim pentru mesaj!</p>;
}
?>
</body>
</html>
În figura 5.1 puteþi observa mesajul de e-mail vizualizat în clientul Mozilla Thunderbird:
Salutari!</p>
<table align=center
style=color: blue; cellpadding=2>
<caption>Stadii de redactare</caption>
<tr style=background: #d3d8e0;>
<th>Titlu articol</th>
<th>stadiu</th></tr>
<tr>
<td>Functii PHP</td>
<td>Finalizat</td>
</tr>
<tr>
<td>POO in PHP</td>
<td>In curs de redactare</td>
</tr>
<tr>
<td>PHP si Java</td>
<td>Inceput</td>
</tr>
</table>
</body>
</html>;
$headers = MIME-Version: 1.0\r\n;
$headers .= Content-type: text/html;
charset=iso-8859-2\r\n;
$headers .= To: Traian Anghel <traian@localhost>\r\n;
$headers .= From: Dorina <dorina@localhost>\r\n;
$headers .= Cc: arhiva@localhost\r\n;
$ok = @mail($email_to, $subject, $message, $headers);
if($ok)
echo Mesajul a fost trimis;
?>
</body>
</html>
Iatã cum se vede (figura 5.2) în clientul de poºtã electronicã Mozilla Thunderbird
mesajul trimis din scriptul inclus în listing-ul 5.2:
PHP ªI POªTA ELECTRONICÃ 85
--BoundaryValue
86 PROGRAMAREA ÎN PHP
email Message
--BoundaryValue
email Message - attachment 1
--BoundaryValue
email Message - attachment 2
--BoundaryValue--
.
În listing-ul 5.3 este prezentat un script care permite trimiterea unui e-mail având un
fiºier ataºat (care conþine o imagine în format JPEG):
5.3. Mesaj de e-mail cu fiºier ataºat
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Mesaj de email cu conþinut XHTML</title>
</head>
<body>
<?php
// Calea catre fisierul atasat
$file_att = ina.jpg;
// Tipul fisierului atasat
$file_att_type = image/jpeg;
/* Numele de fisier care va fi utilizat pentru
fisierul atasat */
$file_att_name = ina.jpg;
$email_from = contact@localhost\n;
$email_subject = Fotografie promisa;
$message = <p style=font-family: verdana;
font-size: 24px; color: red;
text-align: center;>
Iti trimit fotografia promisa!</p>;
$email_to = traian@localhost;
$headers = From: .$email_from.\n;
$headers .= Reply-To: contact@localhost\n;
/* Fisierul care va fi atasat este deschis iar
continutul lui este stocat in variabila $data */
$file = fopen($file_att,rb);
$data = fread($file,filesize($file_att));
fclose($file);
/* Se creeaza delimitatorul dintre partile
PHP ªI POªTA ELECTRONICÃ 87
corpului mesajului */
$boundary_value = md5(uniqid(time()));
// Anteturile mesajului
$headers .= MIME-Version: 1.0\n;
$headers .= Content-Type: multipart/mixed;
boundary=\{$boundary_value}\\n;
$headers .= Content_Transfer-Encoding: 7bit;
$email_message = --{$boundary_value}\n .
Content-Type:text/html;
charset=\iso-8859-1\\n .
Content-Transfer-Encoding: binary\n\n .
$message . \n\n;
// Se codifica MIME base64 datele care trebuie atasate
$data = chunk_split(base64_encode($data));
$email_message .= --{$boundary_value}\n .
Content-Type: {$file_att_type}; .
name=\{$file_att_name}\\n .
Content-Transfer-Encoding: base64\n\n .
$data . \n\n .
--{$boundary_value}--\n\n.;
$ok = @mail($email_to, $email_subject,
$email_message, $headers);
if($ok)
echo Mesajul a fost trimis!;
?>
</body>
</html>
Atunci când nu cunoaºteþi tipul fiºierului care va fi ataºat (de exemplu, în situaþia în
care acesta este ales de utilizator ºi transmis scriptului care va trimite e-mailul), utilizaþi
pentru acesta:
$file_att_type = application/octet-stream;
Conþinutul fiºierului care trebuie ataºat este citit într-o variabilã PHP ($data) ºi
codificat MIME base64 utilizând funcþia base64_encode(), al cãrei prototip este
prezentat în continuare:
string base64_encode(string data)
Datele codificate sunt transmise funcþiei ca argumentul data. Rezultatul întors (datele
codificate) este cu circa 33% mai mare decât argumentul transmis acesteia (datele
originale).
Funcþia chunk_split() a fost utilizatã pentru a converti rezultatul întors de funcþia
base64_encode() în formatul impus de RFC 2045. Aceastã funcþie are urmãtorul
prototip:
string chunk_split(string body, [int chunklen]
[, string end]])
88 PROGRAMAREA ÎN PHP
Funcþia împarte ºirul body în pãrþi mai mici care conþin fiecare câte chunklen
caractere, insereazã la sfârºitul fiecãreia secvenþa end ºi întoarce ºirul astfel obþinut,
lãsând nemodificate datele originale (incluse în body). Valorile implicite (utilizate ºi în
scriptul prezentat în aceastã secþiune) ale argumentelor opþionale chunklen ºi end sunt
76 ºi, respectiv, \r\n.
Cele douã funcþii prezentate anterior au fost utilizate împreunã în scriptul inclus în
listing-ul 5.3, astfel:
$data = chunk_split(base64_encode($data))
--e64642234c34eddfe14bb19bb27ea319
Content-Type:text/html; charset=iso-8859-1
Content-Transfer-Encoding: binary
--e64642234c34eddfe14bb19bb27ea319
Content-Type: image/jpeg; name=ina.jpg
Content-Transfer-Encoding: base64
/9j/4AAQSkZJRgABAQEAyADIAAD/
2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsK
CwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/
2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQU==
--e64642234c34eddfe14bb19bb27ea319--
.
Iatã cum se vede (figura 5.3) în clientul de poºtã electronicã Mozilla Thunderbird
1.0.2 mesajul trimis din scriptul inclus în listing-ul 5.3:
PHP ªI POªTA ELECTRONICÃ 89
Trimiteþi fiºiere
ataºate mesajelor
de e-mail.
Realizaþi un newsletter
Vom prezenta o aplicaþie numitã Newsletter Editura Arete1. Aplicaþia permite cititorilor
editurii imaginare Arete:
sã se aboneze la newsletterul editurii;
sã-ºi valideze conturile create anterior;
sã primeascã regulat prin e-mail informaþii despre noile apariþii editoriale;
sã renunþe oricând doresc la abonament.
Creaþi directorul ed ºi un subdirector al acestuia, newsletter. Adresele de e-mail ale
utilizatorilor vor fi pãstrate într-o bazã de date. Vom folosi sistemul de gestiune a bazelor
de date MySQL. Utilizând un client MySQL, creaþi baza de date newsletter ºi tabelul
email, astfel:
mysql>CREATE DATABASE newsletter;
mysql>USE newsletter;
mysql>CREATE TABLE email (
->id VARCHAR(32) NOT NULL default ,
->address VARCHAR(20) NOT NULL default ,
->status ENUM(active,inactive)
->NOT NULL default invalid,
->date DATE NOT NULL default 0000-00-00,
->PRIMARY KEY (address)
->);
Dupã cum se poate constata, tabelul email are patru câmpuri: id (identificator), de tip
VARCHAR , address (adresa de e-mail), de tip VARCHAR, status (statut), de tip ENUM, având
douã valori posibile (active ºi inactive), valoarea implicitã fiind inactive (ceea ce
înseamnã cã, dupã realizarea înscrierii, utilizatorul trebuie sã activeze contul creat; se
evitã astfel înscrierea unui utilizator de cãtre altul, fãrã acordul primului) ºi date (data
înscrierii). Câmpul address defineºte un index PRIMARY KEY, astfel încât în tabel nu pot
fi introduse douã adrese de e-mail identice. Identificatorul id al unui utilizator va fi obþinut
prin intermediul funcþiei md5(), care întoarce un ºir alcãtuit din 32 de caractere.
Presupunem cã Editura Arete publicã lucrãri din domeniul ºtiinþelor socioumane,
având trei subdomenii: logicã, psihologie ºi filozofie. Datele despre lucrãrile publicate
de editurã vor fi stocate în tabelul lucrari, inclus în baza de date newsletter.
Utilizând un client MySQL, creaþi tabelul lucrari astfel:
mysql>CREATE TABLE lucrari (
->id INT(4) NOT NULL auto_increment,
->domain ENUM(logica,psihologie,filosofie)
1. Areté excelenþã într-un domeniu, virtute (Aristotel, Etica Nicomahicã; Platon, Menon).
PHP ªI POªTA ELECTRONICÃ 91
mysql_connect($host,$user,$password);
mysql_select_db($database);
?>
Dacã adresa de e-mail introdusã de utilizator este validã (adicã include caracterele
@ ºi . ), ea va fi introdusã în tabelul email. Totodatã, este trimis un mesaj de e-mail
utilizatorului, prin care acesta este invitat sã-ºi activeze contul. Mesajul include o
referinþã cãtre fiºierul de validare. Referinþa conþine identificatorul utilizatorului ºi tipul
acþiunii care va fi efectuatã (subscribe), necesare pentru activarea contului, fiind introdusã
astfel:
Pentru validarea înscrierii faceti clic
<a href=\http://127.0.0.1/email/ed/newsletter/
activ.php?id=$id&action=subscribe\>aici</a>
Codul utilizat pentru activarea conturilor abonaþilor (funcþia subscribe()) este
inclus în fiºierul activ.php (vezi listing-ul 5.6). Totodatã, fiºierul respectiv include ºi
codul utilizat pentru dezabonarea acestora (funcþia unsubscribe() ).
$id = $_GET[id];
$action = $_GET[action];
if($action == subscribe)
subscribe($id);
if($action == unsubscribe)
unsubscribe($id);
}
else
$message = Eroare. Nu ati precizat informatiile .
necesare!;
echo $message;
?>
Scriptului din listing-ul 5.6 îi sunt transmiºi doi parametri: identificatorul utili-
zatorului (introdus în script prin $_GET[id]) al cãrui cont urmeazã a fi activat,
respectiv ºters ºi acþiunea care trebuie efectuatã (introdus în script prin $_GET[action]).
Acþiunea poate fi subscribe (activarea contului) ºi, respectiv, unsubscribe (ºtergerea
contului).
Activarea contului va fi fãcutã de utilizator prin intermediul mesajului de e-mail
primit în momentul înscrierii, într-un termen rezonabil (14 zile, în exemplul nostru).
Dezabonarea poate fi fãcutã de utilizator oricând, deoarece orice mesaj de e-mail (chiar
ºi cel de la înscriere) primit de acesta va conþine o referinþã cãtre fiºierul activ.php:
Pentru dezabonare faceti clic
<a href=\http://127.0.0.1/email/ed/newsletter/activ.php?
id=$id&action=unsubscribe\>aici</a>
Pentru a elimina conturile care nu au fost activate în termenul stabilit (14 zile) puteþi
utiliza urmãtoarea secvenþã de cod:
$query = DELETE FROM email WHERE .
DATE_ADD(date,INTERVAL 14 DAY) < CURDATE();
mysql_query($query);
Secvenþa anterioarã trebuie executatã în mod regulat. Din acest motiv, o puteþi
introduce la începutul scriptului care trimite newsletterul.
Salvaþi în fiºierul newsletter.php codul-sursã inclus în listing-ul 5.7. În urma execuþiei
scriptului, newsletterul este trimis abonaþilor care ºi-au activat conturile.
5.7. Fiºierul newsletter.php
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Editura Arete - Newsletter</title>
</head>
<body>
<?php
include_once(connect.inc);
PHP ªI POªTA ELECTRONICÃ 95
<body>;
$nletter .= <p class=intro>
Stimate cititor, îþi prezentãm ultimele
cãrþi apãrute la Editura Arete</p>;
$nletter .= <center><img width=700 height=80
src=http://127.0.0.1/email/ed/images/editura.jpg>
</center><br />;
$nletter .= <table align=center class=tab
cellpadding=5>;
// Sunt incluse lucrarile din domeniul Logicii
$query = SELECT * FROM lucrari WHERE domain=logica
AND DATE_ADD(date,INTERVAL 7 DAY) >= NOW();
$result = mysql_query($query);
while($row = mysql_fetch_array($result)) {
$id=$row[id];
$author = $row[author];
$title = $row[title];
$description = $row[description]);
$cover = $row[cover];
$nletter .= <tr><td colspan=\4\ class=section>
In sectiunea Logicã au aparut</td><tr>;
$nletter .= <tr>
<td width=10% class=image>
<img src=.$url_images.$cover.></td>
<td width=15% class=author>.
$author.</td>
<td width=15% class=title>
<a class=link-title href=.$url_title.
?section=logica&id=.$id.>.$title.</a>
</td><td class=description>.$description.
</td></tr>;
}
// Sunt incluse lucrarile din domeniul Psihologiei
$query = SELECT * FROM lucrari WHERE domain=psihologie
AND DATE_ADD(date,INTERVAL 7 DAY) >= NOW();
$result = mysql_query($query);
while($row = mysql_fetch_array($result)) {
$id=$row[id];
$author = $row[author];
$title = $row[title];
$description = $row[description]);
$cover = $row[cover];
$nletter .= <tr><td colspan=\4\ class=section>
In sectiunea Psihologie au aparut</td><tr>;
$nletter .= <tr>
<td width=10% class=image>
PHP ªI POªTA ELECTRONICÃ 97
<img src=.$url_images.$cover.></td>
<td width=15% class=author>.
$author.</td>
<td width=15% class=title>
<a class=link-title href=.$url_title.
?section=logica&id=.$id.>.$title.</a>
</td><td class=description>.$description.
</td></tr>;
}
// Sunt incluse lucrari din domeniul Filosofiei
$query = SELECT * FROM lucrari WHERE domain=filosofie
AND DATE_ADD(date,INTERVAL 7 DAY) >= NOW();
$result = mysql_query($query);
while($row = mysql_fetch_array($result)) {
$id=$row[id];
$author = $row[author];
$title = $row[title];
$description = $row[description]);
$cover = $row[cover];
$nletter .= <tr><td colspan=\4\ class=section>
In sectiunea Filosofie au aparut</td><tr>;
$nletter .= <tr>
<td width=10% class=image>
<img src=.$url_images.$cover.></td>
<td width=15% class=author>.
$author.</td>
<td width=15% class=title>
<a class=link-title href=.$url_title.
?section=logica&id=.$id.>.$title.</a>
</td><td class=description>.$description.
</td></tr>;
}
// Se adauga un mesaj de final
$nletter .= <tr height=20><td colspan=4></td></tr>
</table>;
$info = <p class=footer>Redactia <a class=link
href=\http://127.0.0.1/email/ed\>Editurii Arete</a>
va doreste o zi buna</p>;
$nletter .= $info;
?>
98 PROGRAMAREA ÎN PHP
S-a presupus cã scriptul inclus fiºierul books.php realizeazã afiºarea cãrþilor publicate
de editurã (acest fiºier nu este prezentat). La apelarea scriptului sunt trimiºi doi para-
metri: section, care indicã secþiunea în care se aflã lucrarea, ºi id, identificatorul
lucrãrii, ca în exemplul urmãtor:
<a class=link-title href=.
$url_title.?section=logica&id=.$id.>.$title.</a>
Se trimit informaþii despre cãrþile publicate în ultima sãptãmânã (ºapte zile) în
raport cu data curentã. Astfel, newsletterul poate fi trimis sãptãmânal.
Scriptul care trimite mesajele de e-mail cititorilor abonaþi la newsletter va trebui
executat în mod periodic (o datã pe sãptãmânã, de exemplu, în cazul unei edituri sau
zilnic, dacã este vorba de un cotidian). Din acest motiv, este recomandat ca aceastã
execuþie sã se realizeze automat utilizând daemonul cron pe servere UNIX/Linux (vezi,
în aceastã lucrare, capitolul dedicat planificãrii execuþiei scripturilor), respectiv Task
Scheduled pe servere Windows. În situaþia în care interesele organizaþiei ce trimite
newsletterul o impun, se poate realiza execuþia manualã a scriptului, la momentele
dorite.
PHP ªI POªTA ELECTRONICÃ 99
CAPITOLUL 6
PHP ºi XML
Variaþia în muncã este întotdeauna o
adevãratã destindere.
J.-J. Rousseau
alte dispozitive care pot fi conectate la Internet (în special, dispozitive mobile, aºa cum
sunt PDA-urile Personal Digital Assistant ºi telefoanele mobile).
XML este independent de platformã, extensibil ºi complet compatibil Unicode.
De asemenea, XML poate fi utilizat într-o mare varietate de contexte (e.g.,
transferul de date între aplicaþii), unele dintre acestea neavând de-a face cu
interacþiunea dintre utilizator ºi conþinut (e.g., serviciile Web utilizeazã XML pentru a
trimite cereri ºi rãspunsuri).
În concluzie, XML satisface urmãtoarele douã condiþii:
separã informaþia de prezentare (pentru afiºarea informaþiei incluse în documentele
XML sunt necesare alte tehnologii);
permite transferul datelor între aplicaþii (e.g., servicii Web).
Ca ºi SGML (din care este derivat), XML este, de fapt, un metalimbaj, adicã un
limbaj care permite crearea altor limbaje. În continuare sunt enumerate câteva dintre
limbajele derivate din XML:
WML (Wireless Markup Language): limbaj utilizat în comunicaþiile realizate prin
WAP (Wireless Application Protocol);
SVG (Scalable Vector Graphics): limbaj de structurare a informaþiilor grafice în
format vectorial;
RDF (Resource Description Framework): limbaj pentru descrierea unui format universal
pentru date în Web, furnizând interoperabilitate între aplicaþiile care schimbã informaþii;
RSS (Really Simple Syndication): limbaj utilizat pentru Web syndication (punerea la
dispoziþie pe Web a informaþiilor actualizabile);
XHTML (Extensible HTML): limbaj obþinut prin redefinirea HTML în termenii
limbajului XML;
SMIL (Synchronized Multimedia Integration Language): limbaj utilizat pentru reali-
zarea prezentãrilor multimedia sincronizate pe Web.
Utilizând limbajul XML, programatorul are posibilitatea:
sã defineascã propriile elemente (limbajul nu are un set predefinit de elemente);
sã facã elementele independente de platforma hardware/software;
sã proceseze documentele XML create.
Pentru a utiliza elemente introduse de el însuºi, utilizatorul trebuie sã le
stabileascã semnificaþia (adicã sã le descrie). Sunt folosite mai multe limbaje
pentru descrierea structurii unui document XML, aºa cum sunt: DTD (Document
Type Definition), XML Schema (elaborat de W3), RELAX NG (elaborat de OASIS
Group) ºi XML Data-Reduced (W3). Documentele scrise în aceste limbaje servesc la
validarea documentelor XML.
Iatã un exemplu de document XML bine formatat, care conþine date despre studenþi
(nume name, an de studiu year ºi vârsta age ):
<?xml version=1.0 encoding=UTF-8?>
<catalog>
<student>
<name>Mircea Damian</name>
<year>I</year>
<age>19</age>
</student>
<student>
<name>Radu Florescu</name>
<year>II</year>
<age>20</age>
</student>
<student>
<name>Catalin Popescu</name>
<year>I</year>
<age>19</age>
</student>
<student>
<name>Sorin Danciu</name>
<year>III</year>
<age>21</age>
</student>
</catalog>
Prima linie a documentului anterior se numeºte declaraþie XML. Prezenþa ei nu este
obligatorie, dar este utilã în sensul cã orice dispozitiv care citeºte documentul va ºti cã
acesta este un document XML, aflând totodatã ºi versiunea XML utilizatã.
Un document XML poate fi considerat ca fiind un arbore ierarhic de obiecte (vezi
figura 6.1). Elementul-rãdãcinã (root) sau nodul-rãdãcinã (catalog, în exemplul
anterior) conþine toate celelalte elemente, fiind rãdãcina arborelui. Într-un document
este permis un singur element-rãdãcinã. Acesta are copii (patru elemente student, în
exemplul anterior).
Fiecare element student are fraþi (celelalte elemente student ) ºi un pãrinte
(elementul root, catalog). La rândul lui, fiecare element student are copii ( name,
year ºi age , în exemplul anterior). Copiii elementului student sunt fraþi.
Din punctul de vedere al programatorului care scrie aplicaþii pentru procesarea
documentelor XML, principala sarcinã constã în crearea arborelui XML ºi parcurgerea
acestuia.
Creaþi directorul xml ºi, în el, salvaþi fiºierul catalog.xml, care va conþine documentul
XML creat anterior. În figura 6.2 puteþi observa cã browserul Internet Explorer afiºeazã
conþinutul acestui fiºier ca o serie de noduri indentate (figurile 6.2 a ºi 6.2 b). Acelaºi
conþinut va fi afiºat în mod similar de cãtre browserul Mozilla Firefox. Semnul minus din
faþa unui nod indicã faptul cã acesta conþine alte noduri. Dacã executaþi click pe semnul
minus, browserul va compresa toate nodurile-copil ale acestuia (figura 6.2 b).
104 PROGRAMAREA ÎN PHP
a b
Figura 6.2. Document XML afiºat în Internet Explorer
Atributele XML pot avea, de asemenea, spaþii de nume, utile în procesarea docu-
mentelor XML (e.g., xsi:type transmite date privitoare la tipul datelor conþinute de
elementul al cãrui atribut este).
XML Schema defineºte douã spaþii de nume standard prin intermediul cãrora se pot
crea documente-schemã:
spaþiul de nume identificat de http://www.w3.org/2001/XMLSchema ºi prefixat prin
xs. Acesta conþine definiþiile tipurilor simple descrise în XML Schema cu care se
construieºte un document-schemã (având extensia xsd);
spaþiul de nume identificat de http://www.w3.org/2001/XMLSchema-instance ºi
prefixat prin xsi (XML Schema Instance). Acesta defineºte atribute care pot fi
utilizate în documentele XML pentru a furniza informaþii suplimentare procesoarelor
XML.
Este important de reþinut cã un spaþiu de nume valid nu trebuie sã indice în mod
obligatoriu cãtre un URI existent, ci numai sã presupunã cã alte persoane nu vor utiliza
acelaºi identificator.
Limbaje de descriere
Din punct de vedere istoric, primul limbaj pentru descrierea documentelor XML a fost
DTD. Cu ajutorul unui document DTD pot fi specificate:
elementele care pot apãrea într-un document XML;
ordinea ºi relaþiile dintre elemente;
atributele pentru fiecare element ºi mãºti pentru verificarea valorii acestora.
În continuare, ne vom referi la descrierea prin DTD a documentelor XML. DTD-ul
unui document XML conþine descrieri (sau declaraþii) ale elementelor ºi atributelor care
pot fi incluse în documentele XML de tipul cãruia îi aparþine documentul respectiv.
Trebuie declarate toate elementele, neavând importanþã ordinea în este realizat acest
lucru.
O declaraþie DTD a unui element este compusã dintr-un marcaj ºi o definiþie inclusã
între paranteze. Aceasta din urmã poate conþine reguli pentru:
text simplu;
un singur element-copil;
o secvenþã de elemente-copil, separate prin virgulã.
Astfel, pentru declararea unui element XML care conþine numai text se utilizeazã
urmãtoarea sintaxã:
<!ELEMENT name description>
De exemplu, elementul name (utilizat în catalog.xml) poate fi definit astfel:
<!ELEMENT name (#PCDATA)>
Descrierea #PCDATA a unui element înseamnã cã acesta conþine numai text, care va
fi afiºat de procesorul XML.
Elementele XML vide sunt definite utilizând descrierea EMPTY, aºa cum se aratã în
exemplul urmãtor:
PHP ªI XML 107
<!ELEMENT br EMPTY>
Un element XML care conþine subelemente va fi descris (împreunã cu acestea) în
limbajul DTD ca în exemplul urmãtor:
<!ELEMENT student (name,year,age)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT year (#PCDATA)>
<!ELEMENT age (#PCDATA)>
În exemplul anterior, elementul student are trei subelemente (numite ºi subtipuri):
name, year ºi culoare (ele trebuie sã aparã în aceastã ordine în documentul XML), iar
fiecare subelement poate conþine text simplu. Dacã utilizaþi sintaxa precizatã anterior,
fiecare dintre cele trei subelemente poate fi folosit o singurã datã în interiorul elementului
student. Pentru a schimba numãrul apariþiilor posibile ale unui subelement, în definiþia
acestuia dupã numele sãu trebuie plasat unul dintre caracterele urmãtoare:
+ înseamnã cã subelementul poate sã aparã cel puþin o datã în documentul XML;
* înseamnã cã subelementul poate sã nu aparã deloc sau poate apãrea de mai multe
ori în document;
? înseamnã cã subelementul poate sã nu aparã deloc sau sã aparã numai o singurã
datã în document;
() defineºte o grupare de elemente, ca în exemplul urmãtor (în care elementul
student poate conþine subelementele name, year ºi age sau numai text):
<!ELEMENT student ((name,year,age)|#PCDATA)
Iatã un exemplu în care subelementele item ºi note pot sã aparã de un numãr
oarecare de ori, dar cel puþin o datã fiecare:
<!ELEMENT list (item+,note+)>
Pentru a specifica faptul cã se poate alege între douã elemente, în DTD se utilizeazã
caracterul | , aºa cum se aratã în continuare:
<!ELEMENT identity (pseudonym|nickname)>
Dacã se doreºte precizarea unui anumit numãr de apariþii ale unui subelement într-un
element, trebuie sã se procedeze ca în exemplul urmãtor, în care subelementul item
apare exact de trei ori în interiorul elementului list:
<!ELEMENT list (item,item,item)>
Dacã elementele includ atribute, acestea pot fi declarate în DTD utilizând !ATTLIST .
Declaraþia conþine numele elementului cãruia îi aparþin atributele, urmat de lista acestora
împreunã cu valorile lor posibile, ca în exemplul urmãtor:
<!ELEMENT actor (#PCDATA)>
<!ATTLIST actor
actorid ID #REQUIRED
gender (male|female) #REQUIRED
type CDATA #IMPLIED>
În exemplul anterior, atributul actorid asociazã elementului XML actor un ID,
care în DTD trebuie sã aibã o valoare unicã, utilizatã pentru identificarea elementului
108 PROGRAMAREA ÎN PHP
<!DOCTYPE root_element [
definition
]>
Dacã descrierea DTD utilizatã de un document XML se aflã într-un fiºier extern
(filename.dtd), acesta trebuie precizat prin urmãtoarea declaraþie DOCTYPE, plasatã dupã
prima linie a documentului:
<!DOCTYPE root_element SYSTEM filename.dtd>
În exemplul anterior, fiºierul care conþine documentul DTD trebuie inclus în directorul
care conþine documentul XML. Dacã fiºierul care include documentul DTD este pe un
alt server, el poate fi specificat prin intermediul URL-ului sãu, ca în exemplul urmãtor:
<!DOCTYPE root_element SYSTEM
http://www.exemple.com/dtd/filename.dtd>
De asemenea, prima linie a documentului XML trebuie sã fie:
<?xml version=1.0 standalone=no?>
În continuare este prezentat DTD-ul documentului catalog.xml:
<!ELEMENT catalog (student+)>
<!ELEMENT student (name,year,age)>
<!ELEMENT name (#PCDATA )>
<!ELEMENT year (#PCDATA )>
<!ELEMENT age (#PCDATA )>
Limbajul DTD prezintã totuºi deficienþe notabile, dintre care menþionãm:
nu este bazat pe XML ºi, din acest motiv, aplicaþiile care proceseazã documente
XML trebuie sã cunoascã încã un limbaj (DTD);
declaraþiile DTD sunt globale, astfel încât nu pot fi definite douã elemente diferite cu
acelaºi nume, chiar dacã acestea sunt utilizate în contexte diferite (limbajul DTD nu
include facilitãþi complete pentru spaþiile de nume, fiind propus înaintea apariþiei
acestora);
nu poate controla strict tipul de date utilizate de elementele XML ºi de atributele
acestora.
Spre deosebire de DTD, XML Schema prezintã mai multe facilitãþi, dintre care
menþionãm:
este un limbaj bazat pe XML, documentele-schemã (sau, pe scurt, schemele) scrise
în acest limbaj fiind procesate într-un mod similar cu documentele XML descrise de
acestea;
asigurã suport complet pentru spaþiile de nume, permiþând astfel utilizarea unor
elemente cu nume identice în contexte diferite;
defineºte un set complet de tipuri simple date;
include suport pentru definirea de tipuri complexe de date.
În XML Schema (vezi http://www.w3.org/TR/2004/REC-xmlschema-2-20041028),
tipurile de date predefinite (built-in) se clasificã în primitive ºi derivate. Tipurile primitive
nu sunt definite pe baza altor tipuri, în vreme ce tipurile derivate, aºa cum le spune ºi
numele, sunt derivate din alte tipuri de date. Tipurile primitive sunt: string, decimal,
boolean, base64Binary, hexBinary, float, double, anyURI, QName, notation, duration,
110 PROGRAMAREA ÎN PHP
DB2 (http://www-306.ibm.com/software/data/db2/extenders/xmlext);
Informix (http://www-306.ibm.com/software/data/informix);
Oracle 8i, 9i (http://www.oracle.com/technology/tech/xml/xmldb/index.html);
MS SQL Server (http://www.microsoft.com/sql/default.mspx).
În alte situaþii, pot fi utilizate baze de date XML native, care pãstreazã structura
documentelor XML ºi permit realizarea unor interogãri folosind limbajele de interogare
XML. Cele mai multe baze de date XML native întorc (dupã interogãri) date XML. În
acest caz, dacã aplicaþia utilizeazã date în alt format, este necesarã o procesare prealabilã
a datelor XML. Aceasta reprezintã un dezavantaj pentru aplicaþiile locale care utilizeazã
baze de date XML native în locul bazelor de date relaþionale, dar nu ºi pentru pentru
aplicaþiile distribuite care utilizeazã formatul XML pentru transportul datelor. Iatã câteva
exemple de baze de date XML native (mai multe la http://www.rpbourret.com/xml/
XMLDatabaseProds.htm#native):
Berkeley DB XML (http://www.sleepycat.com/products/xml.shtml);
DBDOM (http://dbdom.sourceforge.net);
Xindice (http://xml.apache.org/xindice).
Puteþi gãsi o listã exhaustivã a bazelor de date utilizate în conjuncþie cu documentele
XML la http://www.rpbourret.com/xml/XMLDatabaseProds.htm.
font-style: italic;
}
year, age {
display: block;
font-weight: bold;
}
Procesoare XML
Procesoarele XML pot fi clasificate în douã categorii: procesoare care realizeazã
validarea documentelor XML ºi procesoare care nu fac aceastã operaþie (verificã numai
dacã documentele XML sunt bine formatate).
Iatã câteva exemple de procesoare XML:
Xerces (creat de Apache XML Project, având variante scrise în Java, C++ ºi Perl;
http://xml.apache.org);
expat (scris în C de cãtre James Clark, http://www.jclark.com/xml/expat.html);
XP (scris în Java de James Clark, http://www.jclark.com/xml/xp/index.html);
XML for Java (XML4J, IBM Alphaworks);
Sun Microsystems Project (http://java.sun.com/xml/index.jsp);
Oracle XML Parser (http://www.oracle.com/technology/tech/xml/index.html).
O implementare gratuitã în C pentru întregul model DOM este reprezentatã de libxml
(http://www.xmlsoft.org). Aceasta este o bibliotecã de procesare a documentelor XML
scrisã de Daniel Veillard de la Consorþiul Web. Ea permite asocierea ºi procesarea
spaþiilor de nume XML, oferã reguli de validare a documentelor etc. Biblioteca libxml
este inclusã ºi în mediul de dezvoltare GNOME (http://www.gnome.org). Versiunea
actualã a acestei biblioteci este libxml2, fiind realizatã sub licenþa MIT (http://
www.opensource.org/licenses/mit-license.html). Ea include implementãri complete pentru
XPath, XPointer ºi XInclude (http://www.w3.org/TR/xinclude) ºi este utilizatã pe mai
multe platforme, incluzând UNIX/Linux, Windows, MacOS X ºi OS/2.
Limbajul XSLT
Ne vom referi în continuare la cea mai importantã tehnologie: limbajul XSLT, utilizat
împreunã cu limbajul XPath, acesta din urmã fiind folosit pentru a preciza calea unui
element XML sau a unui grup de elemente. Puteþi gãsi ultimele specificaþii ale limbajului
XSLT care definesc sintaxa ºi semantica sa în documentul W3C XSL Transfor-
mations (XSLT) version 2.0 (schiþã de lucru W3C din 5 Noiembrie 2004) la adresa
http://www.w3.org/TR/xslt20.
În principiu, utilizând XSLT, se produce dintr-un arbore XML sursã un arbore XML
rezultat, utilizând un fiºier-ºablon XSLT (cu extensia xsl) sau foaie de stil XSLT. Astfel,
un fiºier-ºablon XSLT realizeazã urmãtoarele acþiuni asupra unui document XML:
transformã fiecare element XML în alt element XML (e.g., XHTML);
poate adãuga elemente noi;
poate elimina o parte dintre elemente;
poate sorta elementele;
ia decizii în ceea ce priveºte elementele pe care trebuie sã le afiºeze, prin testarea
unor condiþii;
formateazã datele afiºate.
Fiºierul ºablon utilizat este precizat prin introducerea urmãtoarei linii în documentul
XML:
<?xml:stylesheet type=text/xsl href=filename.xsl?>
Salvaþi în fiºierul catalog.xsl conþinutul listing-ului 6.2. Acesta conþine o foaie de stil
XSLT.
6.2. Fiºierul catalog.xsl
<?xml version=1.0 encoding=ISO-8859-1?>
<xsl:stylesheet version=1.0
xmlns:xsl=http://www.w3.org/1999/XSL/Transform
xmlns=http://www.w3.org/1999/xhtml>
<xsl:output method=html
doctype-public=-//W3C//DTD XHTML 1.0 Transitional//EN
doctype-system=http://www.w3.org/TR/xhtml1/DTD/
xhtml1-transitional.dtd/>
<xsl:template match=/>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Catalog</title>
PHP ªI XML 117
</head>
<body>
<h2>Studenti</h2>
<table border=1 cellpadding=5>
<tr bgcolor=silver>
<th align=center>Nume</th>
<th align=center>An</th>
<th align=center>Varsta</th>
</tr>
<xsl:for-each
select=catalog/student[age<21]>
<xsl:sort select=year/>
<xsl:if test=age!=25>
<tr>
<td><xsl:value-of select=name/></td>
<td><xsl:value-of select=year/></td>
<td><xsl:value-of select=age/></td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Documentul XSLT este un document XML. Acest lucru se precizeazã astfel:
<?xml version=1.0 encoding =iso-8859-1?>
Ca oricare document XML, ºi documentul XSLT are un element-rãdãcinã. Acest
element poate fi declarat în douã moduri, dupã cum se aratã în continuare:
<xsl:stylesheet version=1.0
xmlns:xsl=http://www.w3.org/1999/XSL/Transform>
sau
<xsl:transform version=1.0
xmlns:xsl=http://www.w3.org/1999/XSL/Transform>
Elementul-rãdãcinã precizeazã versiunea XML utilizatã (1.0) ºi spaþiul de nume
XSLT. Acesta din urmã precizat prin xmlns:xsl aratã cã documentul XSL respectã
specificaþiile W3C incluse (în noiembrie 1999) în documentul de la adresa http://
www.w3.org/1999/XSL/Transform. Dacã este precizat spaþiul de nume, trebuie sã se
utilizeze în mod obligatoriu ºi version=1.0. Toate marcajele incluse în fiºierul-ºablon
XSLT vor fi prefixate cu xsl pentru a le asocia cu spaþiul de nume precizat la începutul
documentului.
Elementul output este utilizat pentru a defini tipul documentului rezultat în urma
transformãrii documentului XML. Iatã câteva dintre valorile posibile: text, xml , html
etc. ºi un exemplu de utilizare:
118 PROGRAMAREA ÎN PHP
<xsl:output method=html/>
Limbajul XSLT este un limbaj bazat pe reguli sau un limbaj funcþional, spre
deosebire de alte limbaje de programare (e.g., PHP, C++), care sunt limbaje
procedurale sau orientate pe obiect. Astfel, XSLT necesitã un set de
reguli-ºablon (template rules) furnizate de programator prin care acesta comunicã
procesorului ce trebuie sã facã atunci când întâlneºte diverse elemente într-un document
XML. O regulã-ºablon se declarã cu elementul template, specificat astfel:
<xsl:template>
Atributul match al acestui element identificã porþiunea din documentul XML care se
potriveºte cu valoarea sa, specificatã conform regulilor limbajului XPath. Potrivirea cu
întregul document se realizeazã cu match=/ .
Procesorul XSLT (e.g., navigatorul Web) începe sã citeascã documentul XML cãutând
elemente care se potrivesc cu elementele template din foaia de stil XSL asociatã. Când
este gãsit un astfel de element, conþinutul elementului template corespunzãtor comunicã
procesorului XSLT ce ieºire trebuie sã genereze ºi, apoi, cãutarea continuã. Când un
ºablon conþine un element apply-template, procesorul XSLT va cãuta elementele
XML conþinute în interiorul elementului curent ºi le aplicã regulile asociate:
<xsl:apply-templates/>
Fiºierul-ºablon XSLT poate conþine elementul for-each, specificat astfel:
<xsl:for-each>
Acest element este utilizat pentru bucle for ºi are select ca atribut. Elementul
for-each asigurã aplicarea acþiunilor specificate în interiorul sãu (de un numãr de ori
egal cu numãrul de potriviri care au loc cu expresia XPath din atributul select).
Atributul select mai poate conþine ºi informaþie de filtrare, astfel încât sã se potriveascã
numai anumite elemente. Astfel, dacã se doreºte sã se afiºeze numai studenþi a cãror
vârstã este mai micã de 21 de ani, trebuie sã se utilizeze:
<xsl:for-each select=catalog/student[age<21]>
În exemplul anterior a fost utilizatã secvenþa escape < (less than) pentru a
reprezenta operatorul < (mai mic).
Fiºierul-ºablon XSLT poate conþine elementul value-of, specificat astfel:
<xsl:value-of>
Cel mai important atribut al acestui element este select. Utilizarea elementului are
ca rezultat afiºarea conþinutului elementului precizat ca valoare a acestui atribut. Astfel,
dacã documentul XML conþine secvenþa:
<name>Radu Florescu</name>
pentru a afiºa Radu Florescu se utilizeazã secvenþa urmãtoare:
<xsl:value-of select=name>
Fiºierul-ºablon XSLT poate conþine elementul sort, specificat astfel:
<xsl:sort>
PHP ªI XML 119
Acest element utilizeazã atributul select ºi este utilizat pentru sortarea datelor
afiºate, în funcþie de valoarea acestui atribut. Rândul urmãtor permite sortarea datelor de
ieºire dupã valoarea elementului year:
<xsl:sort select=year/>
De asemenea, fiºierul-ºablon XSLT poate conþine elementul if, specificat astfel:
<xsl:if>
Acþiunile specificate în interiorul acestui element sunt efectuate numai dacã este
îndeplinitã condiþia precizatã prin atributul test, aºa cum se aratã în exemplul urmãtor:
<xsl:if test=age!=25>
În acest exemplu vor fi afiºate datele studenþilor a cãror vârstã este diferitã de 25.
Foaia de stil XSLT se închide astfel:
</xsl:stylesheet>
Lista completã a elementelor XSLT o puteþi gãsi în documentul W3C XSL Transfor-
mations (XSLT) version 2.0, menþionat anterior.
// constructor
function xmlParser() {
$this->xml_parser = ;//instanta analizorului XML
$this->xml_file = ;//fisierul XML care va fi procesat
$this->xhtml_code = ;//codul XHTML generat
//multimea marcajelor de inceput
$this->start_tags= array();
//multimea marcajelor de sfarsit
$this->end_tags=array();}
// destructor
function destroy() {
if ($this->xml_parser)
xml_parser_free($this->xml_parser);
}
// alte metode
// seteaza marcajele de inceput
function set_start_tags($tags) {
$this->start_tags = $tags;
}
// seteaza marcajele de sfarsit
function set_end_tags($tags) {
$this->end_tags = $tags;
}
// seteaza numele fisierului XML
function set_xml_file($file) {
$this->xml_file = $file;
}
// furnizeaza codul HTML generat
function get_xhtml_code() {
return $this->xhtml_code;
}
/* tratarea evenimentului de aparitie a unui marcaj
de început */
function start_element($parser,$name,$attrs) {
if ($format = $this->start_tags[$name])
$this->xhtml_code .= $format;
126 PROGRAMAREA ÎN PHP
}
/* tratarea evenimentului de aparitie a unui marcaj
de sfarsit */
function end_element($parser,$name) {
if ($format = $this->end_tags[$name])
$this->xhtml_code .= $format;
}
/* tratarea evenimentului de aparitie a unui element XML
de tip CDATA */
function character_data($parser,$data) {
$this->xhtml_code .= $data;
}
/* tratarea evenimentului de aparitie a unei instructiuni
de procesare */
function processing_instruction($parser,$target,$data) {
switch (strtolower($target)) {
case php: eval($data);
break;
}
}
// functia de analiza (parsare) a documentului XML
function parse() {
// instantiaza procesorul XML
$this->xml_parser = xml_parser_create();
// inregistreaza functiile de analiza
xml_set_object($this->xml_parser, &$this);
/* seteaza optiunile de analiza (tag-urile nu
sunt rescrise cu caractere mari) */
xml_parser_set_option(
$this->xml_parser,XML_OPTION_CASE_FOLDING, false);
// seteaza functiile de procesare a elementelor XML
xml_set_element_handler(
$this->xml_parser,start_element,end_element);
xml_set_character_data_handler(
$this->xml_parser,character_data);
xml_set_processing_instruction_handler(
$this->xml_parser,processing_instruction);
// deschide fisierul XML
if (!($fp = fopen($this->xml_file,r)))
die(Fisierul XML nu poate fi deschis);
// proceseaza fisierul
while ($data = fread($fp, 4096)) {
if(!xml_parse($this->xml_parser,$data, feof($fp))) {
// eroare de procesare
die(Eroare XML: .
xml_error_string(xml_get_error_code($this->xml_parser))
PHP ªI XML 127
. in linia .
xml_get_current_line_number($this->xml_parser));
}
}
}
}
?>
În listing-ul 6.6 vã prezentãm o altã clasã pentru a cãrei implementare au fost utilizate
acele elemente de programare orientatã pe obiect introduse în PHP începând cu versiunea
5.0. Clasa Catalog va fi folositã pentru procesarea aceluiaºi document XML (catalog.xml).
6.6. O clasã pentru procesarea unui document XML (PHP 5.0+)
<?php
class Catalog {
protected $parser;
//stiva care va retine elementul XML curent
protected $stack = array();
// functia constructor
public function __construct() {
PHP ªI XML 129
$this->parser = xml_parser_create();
xml_set_object($this->parser, $this);
xml_set_element_handler(
$this->parser,startElement,endElement);
xml_set_character_data_handler(
$this->parser,characterData);
}
// functia destructor
public function __destruct() {
xml_parser_free($this->parser);
}
}
}
}
$dom = new DomDocument();
// Este incarcat documentul XML din fisier
$dom->load(catalog.xml);
// Se obtine elementul-radacina
$root = $dom->documentElement;
/* Se proceseaza, recursiv, intregul document pornind de la
elementul-radacina */
process_children($root);
?>
DOMNode->nodeType (de tip int) conþine tipul nodului XML (o constantã de tipul
XML_xxx_NODE. De exemplu, XML_TEXT_NODE (nodul este un obiect DOMText) ºi
XML_ELEMENT_NODE (nodul este un obiect DOMElement);
DOMNode->nodeValue (de tip string) conþine valoarea nodului (în funcþie de tipul
sãu).
Funcþia recursivã process_children() primeºte ca parametru un obiect DOMElement.
Dacã acesta este de tip DOMText , scriptul afiºeazã valoarea lui. În caz contrar, este
apelatã recursiv funcþia process_children() . Dupã execuþia scriptului anterior (pe
care-l puteþi salva în fiºierul showcontent.php), browserul va afiºa:
Mircea Damian I 19 Radu Florescu II 20 Catalin Popescu I 19
Sorin Danciu III 21
$a = $dom->createelement(a);
$a->setAttribute(href, http://www.php.net/);
$a->appendChild($dom->createTextNode((sit PHP)));
$p->appendChild($a);
/* Adauga punctul de final si un element <br>*/
$text = $dom->createTextNode(.);
$p->appendChild($text);
$br = $dom->createElement(br);
$p->appendChild($br);
// Adauga Succes!
$text = $dom->createTextNode(Succes!);
$p->appendChild($text);
echo $dom->saveHTML();
?>
Listing-ul 6.11 conþine codul pentru validarea documentului XML inclus în catalog.xml.
Salvaþi conþinutul acestui listing în fiºierul validschema.php.
$root = $doc->createElement($table);
$doc->appendChild($root);
/* Se extrag inregistrarile din tabel si se adauga cate un
nod in arborele XML pentru fiecare camp */
while($row = mysql_fetch_assoc($result)) {
$occ = $doc->createElement(record);
$root->appendChild($occ);
foreach($row as $fieldname => $fieldvalue) {
$child = $doc->createElement($fieldname);
$occ->appendChild($child);
$fieldvalue = mb_convert_encoding(
$fieldvalue,UTF-8, ISO-8859-1);
$value = $doc->createTextNode($fieldvalue);
$child->appendChild($value);
}
}
// Documentul XML este salvat pe disc
$doc->save($table..xml);
?>
Export de date
MySQL în XML
Dupã cum se poate constata, scriptul inclus listing-ul anterior realizeazã exportul
datelor incluse în tabelul info al bazei de date car (create anterior) într-un document
XML care va fi salvat pe disc în fiºierul info.xml.
Salvaþi conþinutul listing-ului 6.12 în fiºierul mysql2xml.php ºi executaþi-l (http://
127.0.0./xml/dom/mysql2xml.php).
PHP ªI XML 139
die($status->getMessage());
}
// Se afiseaza documentul XML generat
header(Content-type: text/xml);
echo $Serializer->getSerializedData();
?>
Dupã cum se poate observa în scriptul prezentat, pentru a genera un document XML
folosind pachetul PEAR::XML_Serializer, trebuie parcurse etapele descrise în continuare:
se creeazã o structurã de date PHP de tip tablou asociativ (e.g., $product) sau
obiect, care va fi transformatã (serializatã) într-un document XML;
se declarã o serie de opþiuni de serializare sub forma unui tablou asociativ (e.g.,
$options );
se realizeazã serializarea structurii de date. Pentru aceasta trebuie generatã o instanþã
a clasei XML_Serializer ºi, apoi, folositã metoda serialize() a obiectului
respectiv;
documentul XML obþinut poate fi afiºat (dacã se doreºte).
În continuare sunt prezentate opþiunile de serializare utilizate în scriptul anterior
(puteþi obþine lista completã a opþiunilor în numãr de 27 analizând definiþia clasei
XML_Serializer instalatã în sistem):
addDecl în cazul în care are valoarea TRUE, adaugã instrucþiunea de procesare
<?xml version=1.0?>; valoarea implicitã este FALSE;
encoding stabileºte codificarea utilizatã în document, adãugând atributul encoding
(e.g., <?xml version=1.0 encoding=ISO-8869-1?> ); valoarea implicitã
este ;
indent precizeazã un ºir utilizat pentru indentarea marcajelor; astfel, documentul
XML generat va fi mai uºor accesibil utilizatorului uman; valoarea implicitã a
opþiunii este ;
rootName precizeazã numele elementului-rãdãcinã al documentului XML generat;
valoarea implicitã este array .
În continuare, sunt prezentate metodele clasei XML_Serializer utilizate în scriptul
anterior (puteþi obþine lista completã a metodelor în numãr de 11 analizând definiþia
clasei sau documentaþia pachetului, pe care o gãsiþi la adresa http://pear.php.net/
manual/en/package.xml.xml-serializer.php):
constructorul XML_Serializer::XML_Serializer() primeºte ca argument opþio-
nal tabloul $options, care conþine opþiuni de serializare. În cazul în care tabloul nu
este transmis ca argument sau, dacã este transmis, precizeazã valori numai pentru o
PHP ªI XML 141
parte a opþiunilor posibile, vor fi utilizate valori implicite pentru opþiunile care nu
sunt precizate în mod explicit;
metoda XML_Serializer::serialize() primeºte ca argument structura de date
care urmeazã a fi serializatã. Valoarea întoarsã poate fi TRUE, în caz de succes, sau
un obiect PEAR_Error, în cazul în care s-a produs o eroare;
metoda XML_Serializer::getSerializedData() întoarce (sub formã de ºir)
documentul XML serializat sau un obiect PEAR_Error, în cazul apariþiei unei erori.
font-size: 14px;>
Varsta: .$student->age.</span><br /></p>;
}
?>
</body>
</html>
extension=php_extname.dll
Extensia PHP XMLReader furnizeazã acces read only ºi forward only la fluxul de
date XML. Se utilizeazã un cursor (sau cititor) care este mutat de la un nod la altul al
documentului XML. Nodul curent este nodul pe care este poziþionat cursorul. Avansul
cursorului se realizeazã utilizând oricare dintre metodele de citire.
Dupã cum se poate constata, introducerea datelor în tabelul catalog are loc odatã cu
execuþia metodei endHandler(), dacã nodul-element procesat este student. Interogarea
SQL se construieºte pe baza ºirurilor $str ºi $dat, ce reþin numele elementelor XML ºi,
respectiv, datele XML. Salvaþi conþinutul listing-ului 6.17 în fiºierul xml2mysql.php ºi
executaþi-l (http://127.0.0.1/xml/dom/xml2mysql.php). În concordanþã cu conþinutul
fiºierului catalog.xml, sunt generate urmãtoarele ºiruri (care vor fi transmise ca
interogãri SQL, serverului MySQL prin intermediul funcþiei mysql_query() ):
PHP ªI XML 147
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Editaþi fiºierul catalog.xml ºi adãugaþi (dupã prima linie):
<?xml-stylesheet href=catalog.xsl type=text/xsl?>
Salvaþi în fiºierul xslt.php codul inclus în listing-ul 6.19.
6.19. Fiºierul xslt.php
<?php
// Este incarcat documentul XML
$xml = new DomDocument;
$xml->load(../sax/catalog.xml);
// Este incarcat documentul XSL
$xsl = new DomDocument;
$xsl->load(catalog.xsl);
// Se creeaza un procesor
$processor = new XSLTProcessor;
// Se includ regulile xsl definite în catalog.sxl
$processor->importStyleSheet($xsl);
// Se realizeaza transformarea
echo $processor->transformToXML($xml);
?>
Metoda XSLTProcessor() este constructorul clasei cu acelaºi nume,
XSLTProcessor->importStylesheet() importã conþinutul obiectului XSL transmis
ca argument, pentru a fi utilizat în transformarea documentului XML, iar XSLTProcessor->
transformToXML() realizeazã transformarea propriu-zisã a documentului respectiv.
Rezultatul pe care-l veþi obþine dupã execuþia scriptului inclus în xslt.php va fi similar cu
cel prezentat în continuare:
afiºate dupã execuþia scriptului din listing-ul 6.19
Transformarea XSLT
a documentelor XML
folosind PHP
CAPITOLUL 7
Standarde utilizate
Conceptul de serviciu Web a apãrut în 1999, odatã cu încercãrile unor firme
(e.g., Hewlett-Packard) de a realiza aplicaþii distribuite în Internet. Un pas
înainte a fost fãcut de Microsoft prin intermediul platformei .NET, care are
drept element de bazã noþiunea de serviciu Web. Apariþia acestei platforme a constituit un
semnal pentru marile firme, care au început încorporarea rapidã a noii tehnologii în
produsele lor.
Serviciile Web sunt aplicaþii modulare sau funcþii independente ºi autodescriptive
care pot fi apelate în Internet sau în intranetul unei firme. Funcþiile pot realiza diverse
operaþii, de la simpla validare a unor date pânã la efectuarea de calcule complexe sau
procesarea afacerilor. Ele realizeazã schimbul de date în sisteme distribuite via TCP, prin
intermediul protocolului HTTP, folosind formatul XML pentru a împacheta cererea ºi
rãspunsul.
În principiu, un serviciu Web funcþioneazã astfel: utilizatorul efectueazã o cerere,
iar serviciul Web furnizeazã un rãspuns, ambele în format XML. Acest proces este
denumit generic Web service consuming.
Descrierea serviciilor Web (locaþie, operaþiuni, parametri ºi tipuri de date) se reali-
zeazã prin intermediul limbajului WSDL (Web Service Description Language), dezvoltat
de IBM ºi Microsoft. WSDL este similar cu un limbaj de schemã XML. De altfel,
WDSL utilizeazã toate tipurile de date din specificaþiile XML Schema. Folosind acest
limbaj, se pot crea fiºiere XML care conþin seturi de definiþii ce descriu serviciile Web.
Un fiºier cu extensia wsdl, care include un document WSDL, furnizeazã toate informaþiile
necesare pentru a accesa ºi utiliza un serviciu Web. Documentul WSDL este folosit în
cursul dezvoltãrii unui serviciu Web pentru a crea interfaþa acestuia. Utilizând diverse
instrumente (e.g., Axis, http://ws.apache.org/axis/http://ws.apache.org/axis), se pot
crea clase Java care sã reprezinte serviciul Web dintr-un fiºier WSDL (vezi WSDL2Java
pentru informaþii suplimentare).
Înregistrarea ºi gãsirea serviciilor Web se realizeazã prin intermediul standardului
UDDI (Universal Description, Discovery, and Integration). Acest standard oferã utiliza-
torilor o modalitate sistematicã ºi unitarã de a gãsi furnizorii serviciilor Web prin
intermediul unui registru centralizat al acestora. Specificaþiile UDDI (ajunse la versiunea 3)
pot fi gãsite la adresa http://uddi.org/pubs/uddi-v3.0.1-20031014.htm.
Mecanismul de apelare (invocare) a serviciilor Web ºi de schimb al mesajelor între
client ºi server este descris de un protocol de comunicare. Acesta este independent de
platformã, limbaj de programare, reþea sau protocol de transport. Ca exemple, menþionãm
XML-RPC (XML-Remote Procedure Call) ºi SOAP (Simple Object Access Protocol).
Deºi nu este un standard oficial W3C, protocolul XML-RPC este larg utilizat. În
raport cu SOAP, marele avantaj al acestui protocol este simplitatea sa. XML-RPC este
implementat în PHP, precum ºi în alte limbaje de programare (Java, Perl, Tcl etc.).
Protocolul XML-RPC, proiectat pentru a fi cât mai simplu, realizeazã apeluri de
proceduri la distanþã, permiþând transmiterea unor date predefinite. Protocolul
SOAP urmãreºte sã umple golurile lãsate de XML-RPC, oferind un mecanism
complex de reprezentare a datelor schimbate. Spre deosebire de XML-RPC, care permite
PHP ªI SERVICIILE WEB 151
Datele sunt schimbate între client ºi serviciu prin intermediul unui protocol de
transport; în mod obiºnuit, acesta este HTTP. Se obþine, astfel, o stivã de protocoale ºi
limbaje utilizatã de serviciile Web (vezi figura 7.2).
Standardul UDDI
Standardul UDDI a fost publicat în septembrie 2000, la el contribuind firmele Ariba,
IBM ºi Microsoft. În momentul actual, suportul pentru aceastã tehnologie s-a extins,
implicând o comunitate foarte mare de firme. Proiectul UDDI menþine un registru public
de afaceri (cu servicii incluse) accesibil online, denumit UBR (UDDI Business Registry),
care a intrat în funcþiune pe data de 2 mai 2001. Acesta conþine, de fapt, câþiva regiºtri
replicaþi gãzduiþi de firme numite operatori UDDI sau operatori de regiºtri.
Standardul UDDI, ajuns la versiunea 3.0 (http://www.oasis-open.org/committees/
uddi-spec/tcspecs.shtml), furnizeazã douã specificaþii ce definesc structura registrului de
servicii ºi operaþiile care se pot efectua:
o definiþie a informaþiilor care trebuie furnizate despre fiecare serviciu ºi cum trebuie
codificate acestea (set de structuri de date);
o specificaþie API, care oferã un set de API-uri pentru publicare (înregistrarea
serviciilor) ºi interogare (pentru a gãsi serviciile).
În plus, operatorii de regiºtri furnizeazã fiecare câte o interfaþã-utilizator bazatã pe
pagini Web prin intermediul cãreia pot fi realizate operaþiuni de înregistrare, gestionare
ºi cãutare de servicii Web (e.g., http://uddi.microsoft.com, http://uddi.sap.com,
https://uddi.ibm.com).
UDDI codificã trei tipuri de informaþii despre serviciile Web:
informaþii de tip pagini albe, care includ nume ºi detalii de contact;
informaþii de tip pagini galbene, care oferã o împãrþire a serviciilor bazatã pe tipul
serviciului ºi pe categoria de afaceri;
informaþii de tip pagini verzi, incluzând date tehnice despre serviciu.
Informaþiile oferite în registru permit gãsirea serviciilor Web în funcþie de anumite
carateristici ale acestora. Astfel, în mod obiºnuit, cãutarea serviciilor Web se face în
funcþie de categoria din care fac parte, dupã numele serviciului, dupã furnizor ºi dupã
tipul serviciului. În limbajul UDDI, tipurile de servicii sunt numite tModel. Fiecare
tModel este caracterizat printr-un nume, o descriere ºi un identificator unic (tModelKey).
Un registru UDDI conþine date despre un ansamblu de servicii Web, fiind actualizat
automat la oricare schimbare sau adãugare de noi servicii. Registrele UDDI pot fi
publice sau private. Datele UDDI sunt gãzduite de noduri UBR (ale unor companii),
care au obligaþia de a le menþine în conformitate cu specificaþiile precizate de consorþiul
UDDI.org (http://uddi.org/specification.html). Firme ca Microsoft ºi IBM întreþin
regiºtri publici, conform specificaþiilor UDDI. La adresa http://www.uddi.org/register.html
puteþi gãsi legãturi cãtre regiºtrii menþionaþi. Mai multe noduri se pot organiza într-un
nor UDDI, toate nodurile având aceleaºi înregistrãri. Unui operator de nod UBR i se
cere sã replice datele celorlalte noduri prin utilizarea unui canal securizat, furnizând o
PHP ªI SERVICIILE WEB 153
Protocolul XML-RPC
Conceptul RPC (Remote Procedure Call) este des întâlnit în dezvoltarea de software. El
se referã la utilizarea de cãtre o aplicaþie a unor proceduri (scrise, eventual, în alte
limbaje de programare) incluse în programe aflate la distanþã (i.e., pe un calculator,
diferit de cel pe care se executã aplicaþia). Acest mecanism permite unor aplicaþii
executate pe platforme diferite (e.g., Windows ºi Linux) sã comunice între ele. Programul
care apeleazã o procedurã aflatã la distanþã se numeºte client RPC. Serverul RPC cere
execuþia procedurilor ºi trimite clientului rezultatele generate.
XML-RPC este o specificaþie ºi un set de implementãri care permit software-ului
ce ruleazã pe diverse platforme ºi în medii eterogene sã efectueze apeluri de
proceduri prin Internet (e.g., o funcþie Perl poate face un apel cãtre o metodã PHP).
Acest standard utilizeazã protocolul HTTP pentru transportul mesajelor ºi formatul
XML pentru codificarea datelor schimbate.
Un mesaj XML-RPC este o cerere HTTP-POST, respectiv un rãspuns HTTP. O
conversaþie dintre douã sisteme (numite client XML-RPC ºi server XML-RPC) începe
cu cererea clientului ºi se terminã cu rãspunsul serverului.
Un mesaj XML-RPC este alcãtuit din antet ºi corp. Antetul unui mesaj conþine
anteturi HTTP. Corpul unui mesaj este scris în format XML. Corpul cererii conþine
numele unei metode (proceduri) care va fi executatã pe server ºi parametrii necesari
acesteia. Corpul rãspunsului include parametrii (sau datele) întorºi în urma execuþiei
metodei.
În continuare este prezentat un exemplu care conþine o cerere XML-RPC (în care
puteþi remarca antetul ºi corpul acesteia):
POST /RPC HTTP/1.0
User-Agent: PHP/5.0.4
Host: 127.0.0.1
Content-Type: text/xml
Content-Length: 275
<?xml version=1.0?>
<methodCall>
<methodName>my_method</methodName>
<params>
<param>
154 PROGRAMAREA ÎN PHP
<value><string>parametru</string></value>
</param>
</params>
</methodCall>
Dupã cum se poate constata, în prima linie a cererii este inclus un URI ( /RPC, în
exemplul anterior). Acesta poate fi un singur slash (/ ) dacã serverul trateazã numai
cereri XML-RPC. În alte cazuri (ca în exemplul anterior), URI-ul ruteazã cererea cãtre
directorul în care sunt stocate scripturile ce executã serviciile Web. Dupã cum se poate
observa, trebuie specificate urmãtoarele anteturi HTTP: User-Agent (valoarea lui
precizeazã clientul Web care a lansat cererea), Host (valoarea sa este adresa IP a
calculatorului care a trimis cererea), Content-Type (valoarea acestui câmp este text/
xml , care aratã cã datele transmise sunt în format XML) ºi Content-Length.
Corpul cererii este o structurã XML, incluzând un element <methodCall>. Acesta
trebuie sã includã un element-copil, <methodName>. Nodul-text care urmeazã este un
ºir ce precizeazã numele metodei apelate ºi trebuie sã conþinã numai caractere
alfanumerice, liniuþa de subliniere ºi caracterele punct, douã puncte ºi slash. Interpretarea
ºirului este lãsatã pe seama serverului. Acest ºir poate fi, de exemplu, numele unui fiºier
care conþine un script ce va fi executat în urma trimiterii unei cereri XML-RPC sau calea
cãtre un astfel de fiºier.
În cazul în care se transmit parametri cãtre procedurã, <methodCall> trebuie sã
includã un element-copil numit <params>. Acesta va include oricâte elemente <param>,
fiecare cu câte un element-copil <value> . Elementele <value> precizeazã datele
transmise de la client la server. Ca ºi toate celelalte lucruri în XML, datele sunt
reprezentate textual, utilizând marcaje-container, ºi pot fi scalare (sau primitive) ºi
complexe (array ºi struct).
Tipurile de date scalare XML-RPC sunt:
întreg cu semn pe patru octeþi (tipul este reprezentat ca <i4> sau <int>); exemple:
<i4>56</i4> , <int>-123</int>;
boolean (acest tip este reprezentat ca <boolean>), având douã valori posibile: 0
(false) ºi 1 (true); exemplu: <boolean>1</boolean>;
ºir de caractere (reprezentat ca <string>); exemplu: <string>Albert
Einstein</string>;
numãr cu semn în virgulã flotantã cu dublã precizie (tipul este reprezentat ca
<double>); exemplu: <double>5.6789</double>;
timp ºi/sau datã (acest tip este reprezentat ca <dateTime.iso8601>); exemplu:
<dateTime.iso8601>20050818T16:38:54</dateTime.iso8601> ;
secvenþã binarã codificatã cu base64 (reprezentatã ca <base64> ); exemplu:
<base64>PK4LBLJQQhSRUpVjdoXPk8D0sQ8EGHsdeTFC</base64>.
Datele de tipul timp/datã sunt reprezentate conform ISO 8601, care este un standard
internaþional pentru specificarea datei ºi a timpului. Conform acestui standard, data este
reprezentatã ca YYYY-MM-DD (e.g., 2005-08-18). Liniuþele pot fi omise, astfel încât se
poate utiliza ºi reprezentarea YYYYMMDD (e.g., 20050818). Dacã nu intereseazã ziua, se
poate utiliza reprezentarea YYYY-MM (e.g., 2005-08 ). Se poate reprezenta chiar ºi
numai anul, ca YYYY (e.g., 2005). Notaþia 00:00 (ºi nu 24:00) este preferatã pentru a
reprezenta miezul nopþii.
PHP ªI SERVICIILE WEB 155
Conform ISO 8601, timpul este reprezentat ca hh:mm:ss (e.g., 21:45:23). Poate fi
omis caracterul douã puncte (colon), utilizându-se reprezentarea hhmmss (e.g., 214523).
Pot fi omise secundele (e.g., 21-45 sau 2145) sau chiar ºi minutele (e.g., 21). Dacã
data ºi timpul sunt reprezentate simultan, trebuie precizatã mai întâi data ºi apoi timpul,
separate prin majuscula T (e.g., 20050818T21:45:23). Puteþi gãsi informaþii supli-
mentare despre standardul ISO 8601 la adresa http://www.cl.cam.ac.uk/~mgk25/
iso-time.html.
Un element <struct> poate conþine un numãr oarecare de elemente <member>,
fiecare dintre acestea incluzând un element <name> ºi unul <value>. Iatã un exemplu
de datã de tip struct:
<struct>
<member>
<name>Mircea</name>
<value><i4>21</i4></value>
</member>
<member>
<name>Dorina</name>
<value><i4>36</i4></value>
</member>
</struct>
Structurile pot fi recursive, astfel încât oricare <value> poate conþine o datã struct
sau de un alt tip, inclusiv array.
Un tablou (<array>) conþine un singur element <data>, care poate include oricâte
elemente <value>. Iatã un exemplu de parametru <array>:
<array>
<data>
<value><string>Albert</string></value>
<value><string>Einstein</string></value>
<value><i4>1921</i4></value>
</data>
</array>
Elementele <array> nu trebuie sã aibã nume. Tablourile pot fi recursive, oricare
<value> poate conþine o datã array sau de oricare alt tip, inclusiv struct.
În continuare, este prezentat rãspunsul serverului:
HTTP/1.1 200 OK
Date: Tue, 16 Aug 2005 14:43:14 GMT
Server: Apache/2.0.52 (Win 32) PHP/5.0.4
X-Powered-By: PHP/5.0.4
Content-Length: 154
Connection: close
Content-Type: text/xml
<?xml version=1.0?>
<methodResponse>
<params>
156 PROGRAMAREA ÎN PHP
<param>
<value><int>1234</int></value>
</param>
</params>
</methodResponse>
În cazul în care nu existã o eroare la nivel inferior, serverul întoarce codul de stare
200 OK. Dupã cum se poate constata, antetul Content-Type al rãspunsului are valoarea
text/xml (adicã rãspunsul este în format XML). Antetul Content-Length trebuie sã
fie prezent ºi corect.
Corpul rãspunsului constã dintr-o singurã structurã XML. Aceasta este un element
<methodResponse> , care poate conþine un singur element <params> (ce conþine un
singur element <param>, care, la rândul lui, conþine un singur element <value>) sau
un singur element <fault> (prin care se returneazã clientului un mesaj de eroare în
cazul în care apelul serviciului Web respectiv a eºuat; acesta conþine un element
<value>, de tip struct, cu douã elemente, unul numit <faultCode> , de tip int, ºi altul
numit <faultString> , de tip string). Elementele <params> ºi <fault> nu pot fi
prezente simultan.
OntoSys (http://ontosys.com/) a introdus un tip de date suplimentar; acesta este nil,
reprezentat ca <nil/>. Folosindu-l, poate fi precizatã orice valoare diferitã de null. Iatã
un exemplu de utilizare:
<struct>
<member>
<name>varsta_minima</name>
<value><int>18</int></value>
</member>
<member>
<name>varsta_maxima</name>
<value><nil/></value>
</member>
</struct>
În exemplul anterior este creat un element <struct>, cu douã elemente, care
reprezintã un domeniu. Valoarea lowerBound este un element <int>, iar valoarea
upperBound este un element <nil/>, ceea ce înseamnã cã nu existã o limitã superioarã.
Limitãrile protocolului XML-RPC sunt reprezentate de:
dificultatea de a transmite un obiect ca argument procedurii care va fi apelatã la distanþã;
utilizarea exclusivã a unor tipuri de date predefinite;
datele de tip struct ºi array sunt anonime, ceea ce face greoaie identificarea acestora,
mai ales atunci când este transmis un numãr mare de date având tipurile menþionate.
Protocolul SOAP
În acord cu W3C, SOAP (http://www.w3.org/TR/soap) este un protocol simplu, realizat
pentru schimbul de informaþii structurate (mesaje) într-un mediu distribuit. SOAP este o
metodã de comunicare în Internet, folosind XML ca limbaj pentru alcãtuirea mesajelor
PHP ªI SERVICIILE WEB 157
xmlns:env=http://www.w3.org/2003/05/soap-envelope
env:encodingStyle=http://www.w3.org/2003/05/soap-encoding
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance>
<env:Body>
<m:GetStudentGradeResponse xmlns:m=http://example.org/
grade>
<return xsi:type=xsd:string>10</return>
</m:GetStudentGradeResponse>
</env:Body>
</env:Envelope>
<SOAP-ENV:Body>
<m:GetStudentGradeResponse xmlns:m=http://example.org/grade>
<return xsi:type=xsd:string>10</return>
</m:GetStudentGradeResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
tipuri compuse:
o struct: o structurã este o datã compusã în care numele membrilor este singura
distincþie dintre aceºtia, un membru neputând avea acelaºi nume ca ºi un altul;
o array: tablourile SOAP sunt definite ca având tipul SOAP-ENC:array, fiind
reprezentate numai valorile elementelor, fãrã numele acestora.
Iatã un exemplu de definire ºi utilizare a tipului de date struct în SOAP:
<element name=carte>
<complexType>
<element name=autor type=xsd:string/>
<element name=titlu type=xsd:string/.
<element name=editura type=xsd:string/>
</complexType>
</element>
<e:carte>
<autor>Sabin Buraga</autor>
<titlu>Proiectarea siturilor Web<titlu>
<editura>Polirom</editura>
</e:carte>
164 PROGRAMAREA ÎN PHP
<nume_favorite SOAP-ENC:arrayType=xsd:string[2]>
<nume>Dorina</nume>
<nume>Maria</nume>
</nume_favorite>
Pentru informaþii suplimentare privind tipurile de date utilizate în SOAP, citiþi
secþiunea urmãtoare a lucrãrii.
Limbajul WSDL
WSDL (Web Service Description Language) este un limbaj bazat pe XML pentru
descrierea serviciilor Web, reprezentând o componentã importantã a triadei WSDL,
UDDI, SOAP/XML-RPC. Versiunea curentã a limbajului este 1.1 (http://www.w3.org/
TR/wsdl).
Descrierea WSDL a unui serviciu Web este o colecþie de metadate despre serviciul
respectiv care comunicã:
ce face acest serviciu (operaþiunile pe care le efectueazã);
unde este localizat (detalii ale adresei la care poate fi invocat);
cum poate fi accesat (detalii ale formatelor de date ºi tipurilor de protocoale utilizate
pentru accesarea operaþiunilor).
În esenþã, descrierea WSDL spune utilizatorului tot ceea ce are nevoie pentru a
putea utiliza serviciul respectiv.
WSDL furnizeazã atât o descriere abstractã, cât ºi o descriere concretã a
serviciilor Web, precum ºi modalitatea în care sunt legate elementele care
constituie subiectele acestor descrieri. Descrierea abstractã include definiþii ale
tipurilor de date (utilizând sintaxa furnizatã de XML Schema), ale mesajelor ºi tipurilor de
port (interfeþele serviciului Web). Descrierea concretã include locaþia în care este oferit
serviciul Web, precum ºi protocolul specific utilizat de client pentru a comunica cu acesta.
Un document WSDL este compus din urmãtoarele ºapte categorii de secþiuni:
tipuri (types), inclusã în documentul WSDL prin intermediul elementului types .
Secþiunea este un container pentru definiþiile tipurilor de date (utilizate de serviciul
Web), folosind o serie de sisteme de tipuri (e.g., XSD);
mesaj (message), inclusã prin elementul message. Un document WSDL poate include
0 sau mai multe mesaje. Un mesaj este o definiþie abstractã a datelor care vor fi
schimbate cu serviciul Web ºi este alcãtuit din pãrþi (parts) logice, fiecare dintre ele
fiind asociatã cu o definiþie inclusã într-un sistem de tipuri de date. Elementul
message conþine obligatoriu atributul name (deoarece este un identificator, numele
unui mesaj trebuie sã fie unic într-un document WSDL). Elementul part (care
reprezintã o parte a unui mesaj) are douã atribute: name (trebuie sã fie unic printre
elementele-copil ale elementului message ) ºi type (ia valori definite în XML Schema
sau în secþiunea types a documentului WSDL);
PHP ªI SERVICIILE WEB 165
tip de port (port type), inclusã prin elementul portType. Este un set abstract de
operaþiuni (vezi punctul urmãtor) suportate de unul sau mai multe puncte finale
(endpoints). Fiecare operaþiune se referã la un mesaj de intrare ºi la mesaje de ieºire.
În esenþã, în aceastã secþiune este descrisã interfaþa serviciului (ceea ce face acesta).
Un document WSDL poate avea mai multe secþiuni port type, ceea ce este echivalent
cu a spune cã serviciul corespunzãtor prezintã o funcþionalitate exprimatã de mai
multe interfeþe. Un element portType are un atribut name, care defineºte numele cu
care va fi referitã în document interfaþa descrisã de elementul respectiv. Uneori (dar
nu în mod obligatoriu), numele elementelor portType sunt acordate dupã ºablonul
numeServiciuWebPortType;
operaþiune (operation), inclusã în documentul WSDL prin intermediul elementului
operation; este inclusã într-o secþiune port type. O operaþiune este o descriere
abstractã a unei acþiuni suportate de serviciul Web. În esenþã, o acþiune defineºte o
metodã pe serviciu Web (numele metodei, parametrii de intrare ºi de ieºire ºi, dacã
este necesar, erorile care pot fi generate). În general, un element operation poate
defini patru tipuri de operaþiuni:
o operaþiune cerere-rãspuns (elementul operation conþine douã elemente-copil,
unul de intrare input ºi altul de ieºire output , în aceastã ordine),
echivalentã cu un apel de metodã;
o operaþiune cu sens unic (elementul operation conþine numai elementul de
intrare), echivalentul unei operaþii care transmite date ºi nu necesitã rãspuns;
o operaþiune de notificare (elementul operation conþine numai elementul de
ieºire), echivalentul notificãrii clientului de cãtre server asupra producerii unui
eveniment;
o operaþiune de solicitare a unui rãspuns (operation conþine atât elementul de
ieºire, cât ºi pe cel de intrare, în aceastã ordine), echivalentul unei cereri adresate
de server clientului, la care se aºteaptã rãspuns;
legãturã (binding), inclusã prin elementul binding. O legãturã ataºeazã la o interfaþã
(portType) informaþii referitoare la protocolul de transport utilizat, stilul apelului
(rpc sau document) ºi specificã, pentru fiecare operaþie conþinutã de portType, cum
se face codarea, spaþiul de nume ºi modul de codificare (encodingStyle ). Un
element binding are precizat în mod obligatoriu atributul name. Acesta trebuie sã
fie unic în documentul WSDL. Tipul de port legat se specificã prin intermediul atri-
butului type. Accesarea serviciului se face prin intermediul unui protocol de comu-
nicare precizat prin intermediul prefixului elementului binding (e.g., soap:binding).
Atributul style al acestui element precizeazã stilul apelului, iar atributul transport
precizeazã protocolul de transport;
port (port), inclusã în secþiunea service prin intermediul elementului port. Un port
este un punct final (endpoint), definit prin asocierea dintre o adresã de reþea ºi o
legãturã (binding). El este denumit punct final deoarece defineºte informaþiile necesare
pentru accesarea ºi invocarea serviciului Web. Un element port trebuie sã aibã
precizat numele (unic în documentul WSDL) prin intermediul atributului name;
serviciu (service), inclusã prin intermediul elementului service. Un serviciu com-
binã un set de puncte finale (porturi) înrudite. Este important de subliniat cã un
serviciu Web poate utiliza legãturi multiple.
166 PROGRAMAREA ÎN PHP
Prefixul tns (this namespace) este utilizat prin convenþie pentru a face referire
la documentul curent.
Þinând seama de structura unui document WSDL, acesta poate fi considerat ca un set
de definiþii incluse în elementul-rãdãcinã ( definitions). În continuare vom detalia
setul de definiþii, împãrþindu-l în douã categorii: definiþii incluse în descrierea abstractã
ºi, respectiv, în descrierea concretã a serviciului Web.
Descrierea abstractã a unui serviciu Web într-un document WSDL se realizeazã
astfel:
<definitions>
<types>
PHP ªI SERVICIILE WEB 167
În acest exemplu a fost definit un tip abstract de date numit persoana, care conþine
douã elemente numite nume ºi varsta, având tipurile xsd:string ºi xsd:int.
Iatã cum se precizeazã secþiunea message:
<message name=mesaj_cerere>
<part name=identificator type=xsd:string/>
</message>
message name=mesaj_raspuns>
<part name=return type=tns:persoana/>
</message>
În acest exemplu sunt definite douã mesaje (având numele mesaj_cerere ºi
mesaj_raspuns). Fiecare mesaj conþine câte o parte (part), având numele
identificator ºi return , tipurile acestora fiind xsd:string ºi, respectiv,
tns:partType (tip abstract definit în secþiunea types).
Iatã cum se precizeazã într-un document WSDL, în cele mai multe cazuri, secþiunea
portType:
<portType name=interfata_serviciu>
<operation name=trimite_mesaj>
<input message=tns:mesaj_cerere/>
<output message=tns:mesaj_raspuns/>
</operation>
</portType>
În acest exemplu, tipul de port interfata_serviciu include o singurã operaþiune,
de tip cerere-rãspuns, denumitã trimite_mesaj. Elementul de intrare specificã mesajul
168 PROGRAMAREA ÎN PHP
SWV5), care pot fi utilizate în vederea încheierii unui contract. Preþurile sunt actualizate
sãptãmânal, în ziua de vineri. Detaliile serviciului wsdl sunt publicate la adresa
http://www.sugartech.co.za/soap/wsdlserver.php#, iar documentul WSDL este generat
dinamic utilizând adresa http://www.sugartech.co.za/soap/wsdlserver.php?wsdl.
Vom folosi aplicaþia XMLSpy 2005 pentru a trimite cereri cãtre serverul SOAP. Puteþi
descãrca separat aceastã aplicaþie sau întreaga suitã de aplicaþii din care face parte, denumitã
Altova Enterprise XML Suite 2005 (http://www.altova.com/products_ide.html). Este
necesarã obþinerea unei chei (key) pentru a putea utiliza aceste produse.
Lansaþi aplicaþia XMLSpy 2005 ºi din meniul File alegeþi opþiunea Open. Aici
introduceþi URL-ul http://www.sugartech.co.za/soap/wsdlserver.php?wsdl. Va fi
afiºat documentul WSDL al serviciului wsdl. În conþinutul sãu (prezentat în con-
tinuare) pot fi identificate secþiunile ºi subsecþiunile unui document WSDL, enume-
rate anterior:
<?xml version=1.0 encoding=ISO-8859-1 ?>
<definitions
xmlns:SOAP-ENV=http://schemas.xmlsoap.org/soap/envelope/
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:SOAP-ENC=http://schemas.xmlsoap.org/soap/encoding/
xmlns:si=http://soapinterop.org/xsd
xmlns:tns=urn:http://www.sugartech.co.za
xmlns:soap=http://schemas.xmlsoap.org/wsdl/soap/
xmlns:wsdl=http://schemas.xmlsoap.org/wsdl/
xmlns=http://schemas.xmlsoap.org/wsdl/
targetNamespace=urn:http://www.sugartech.co.za>
<types>
<xsd:schema targetNamespace=urn:http://www.sugartech.co.za>
<xsd:import namespace=http://schemas.xmlsoap.org/soap/
encoding/ />
<xsd:import namespace=http://schemas.xmlsoap.org/wsdl/ />
<xsd:complexType name=Price>
<xsd:all>
<xsd:element name=Date type=xsd:string />
<xsd:element name=Symbol type=xsd:string />
<xsd:element name=Open type=xsd:float />
<xsd:element name=Low type=xsd:float />
<xsd:element name=High type=xsd:float />
<xsd:element name=Close type=xsd:float />
<xsd:element name=Last type=xsd:float />
<xsd:element name=Chnge type=xsd:float />
</xsd:all>
</xsd:complexType>
</xsd:schema>
</types>
<message name=PreviousCloseRequest>
<part name=symbol type=xsd:string />
PHP ªI SERVICIILE WEB 171
</message>
<message name=PreviousCloseResponse>
<part name=return type=tns:Price />
</message>
<portType name=wsdlPortType>
<operation name=PreviousClose>
<documentation>Returns the previous closing price of a
sugar futures contract</documentation>
<input message=tns:PreviousCloseRequest />
<output message=tns:PreviousCloseResponse />
</operation>
</portType>
<binding name=wsdlBinding type=tns:wsdlPortType>
<soap:binding style=rpc
transport=http://schemas.xmlsoap.org/soap/http />
<operation name=PreviousClose>
<soap:operation soapAction=urn:http://www.sugartech.co.za
#PreviousClose style=rpc />
<input>
<soap:body use=encoded
namespace=urn:http://www.sugartech.co.za
encodingStyle=http://schemas.xmlsoap.org/soap/
encoding/ />
</input>
<output>
<soap:body use=encoded
namespace=urn:http://www.sugartech.co.za
encodingStyle=http://schemas.xmlsoap.org/soap/
encoding/ />
</output>
</operation>
</binding>
<service name=wsdl>
<port name=wsdlPort binding=tns:wsdlBinding>
<soap:address location=http://sugartech.co.za/soap/
wsdlserver.php />
</port>
</service>
</definitions>
xmlns:tns=urn:http://www.sugartech.co.za>
<SOAP-ENV:Body>
<ns1:PreviousCloseResponse
xmlns:ns1=urn:http://www.sugartech.co.za>
<return xsi:type=tns:Price>
<Date xsi:type=xsd:string>2005-08-19</Date>
<Symbol xsi:type=xsd:string>SBV5</Symbol>
<Open xsi:type=xsd:float>9.65</Open>
<Low xsi:type=xsd:float>9.65</Low>
<High xsi:type=xsd:float>9.8</High>
<Close xsi:type=xsd:float>9.68</Close>
<Chnge xsi:type=xsd:float>.01</Chnge>
</return>
</ns1:PreviousCloseResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
WSFL (Web Service Flow Language) este un limbaj bazat pe XML, pentru
descrierea modului în care mai multe servicii Web lucreazã împreunã pentru a
realiza procesarea afacerilor. Prezentarea acestui limbaj depãºeºte scopul lucrãrii
noastre (vezi http://www-306.ibm.com/software/solutions/webservices/pdf/WSFL.pdf).
XML-RPC în PHP
Implementãri ale XML-RPC în PHP
XML-RPC a fost lansat de UserLand Software (http://www.userland.com) în aprilie
1998, în scopul de a permite aplicaþiilor sã schimbe mesaje între ele, indiferent de
limbajele în care au fost scrise, de platformele pe care sunt executate ºi de locaþia
sistemelor pe care ruleazã. Compania UserLand Software a implementat protocolul
XML-RPC în produsul sãu UserLand Frontier. Începând de atunci, au fost dezvoltate
diverse implementãri ale protocolului ºi mai multe companii au adoptat aceastã tehnologie
pentru produsele lor.
La adresa http://www.xmlrpc.com/directory/1568/implementations gãsiþi o listã de
implementãri ale protocolului XML-RPC scrise în PHP, Perl, Python, Java, Frontier, C/
C++, Delphi etc.
În continuare, vom enumera ºi caracteriza pe scurt câteva implementãri în limbajul
PHP ale protocolului XML-RPC:
XML-RPC EPI este o implementare a XML-RPC scrisã în C. Aceasta a fost dezvoltatã
de Epinions (http://www.epinions.com) pentru uz intern. În martie 2000, XML-RPC
PHP ªI SERVICIILE WEB 175
require_once(xmlrpc.inc);
// Se creeaza un client XML-RPC
$client = new xmlrpc_client(/rpc/phpxmlrpc/server1.php,
localhost, 80);
// Se afiseaza informatii pentru depanare
$client->setDebug(1);
/* Se creeaza doua obiecte xmlrpcval
(se codifica variabilele PHP) */
$inputNumber1 = new xmlrpcval(12.78, double);
$inputNumber2 = new xmlrpcval(23.01, double);
// se creeaza un tablou de obiecte xmlrpcval
$parameters = array($inputNumber1, $inputNumber2);
// Se creeaza mesajul XML-RPC
$message = new xmlrpcmsg(sum, $parameters);
// Se trimite mesajul si se primeste raspunsul
$response = $client->send($message);
// Se verifica daca a aparut vreo eroare
if($response->faultCode() == 0) {
// Daca nu sunt erori:
// Se decodifica raspunsul intr-un tip PHP
$rsp = xmlrpc_decode($response->value());
// Se afiseaza raspunsul (35.79)
echo $rsp;
}
else {
/* Daca apare o eroare, se afiseaza codul acesteia
si o explicatie corespunzatoare */
echo Eroare: <br />Cod: . $response->faultCode() .
<br />Explicatie . $response->faultString() .
<br />;
}
?>
</body>
</html>
$client->setDebug($flag);
unde parametrul $flag ia valoarea 1 sau 0, dupã cum se cere sau nu se cere clientului
XML-RPC sã afiºeze informaþii pentru depanare în browser. Dacã metoda nu este
apelatã de client, aceste informaþii nu se afiºeazã.
Constructorul clasei xmlrpcval primeºte douã argumente: variabila PHP care
trebuie codificatã ($numeVar) ºi tipul XML-RPC rezultat (type):
inputNumber1 = new xmlrpcval($numeVar, type);
<?xml version=1.0?>
<methodCall>
<methodName>sum</methodName>
<params>
<param>
<value><double>12.78</double></value>
<value><double>34.05</double></value>
</param>
</params>
</methodCall>
Rãspunsul generat de serverul XML-RPC va fi similar cu urmãtorul (rezultatul
furnizat de procedura sum este 46.83):
PHP ªI SERVICIILE WEB 179
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2005 19:17:49 GMT
Server: Apache/2.0.52 (Win32) PHP/5.0.4
X-Powered-By: PHP/5.0.4
Content-Length: 131
Connection: close
Content-Type: text/xml
În exemplul oferit, scripturile care includ clientul ºi, respectiv, serverul XML-RPC
sunt executate în acelaºi sistem (localhost). Dacã aveþi acces la încã un server,
transferaþi acolo scriptul server.php ºi celelalte fiºiere necesare. Dacã serverul la care
aveþi acces este domain.com, în fiºierul client.php înlocuiþi:
$client = new xmlrpc_client(/rpc/phpxmlrpc/server.php,
localhost, 80);
cu secvenþa urmãtoare:
$client = new xmlrpc_client(/server_path/server.php,
domain.com, 80);
Dacã pachetul este instalat deja în sistem, puteþi aduce versiunea cea mai recentã
astfel:
pear upgrade XML_RPC
În pachetul PEAR::XML_RPC sunt definite mai multe obiecte, fiecare servind unui
scop precis. În continuare vom prezenta câteva dintre acestea:
XML_RPC_Client creeazã un client XML-RPC. La iniþializare, trebuie sã se
transmitã constructorului trei argumente: calea cãtre scriptul aflat pe server, numele
de gazdã al serverului ºi numãrul portului utilizat de severul HTTP.
180 PROGRAMAREA ÎN PHP
$response = $client->send($msg);
// Se citeste raspunsul
if (!$response->faultCode()) {
// Nu a fost generata eroare
$value = $response->value();
echo $value->scalarval() . \n;
}
else {
// A fost generata o eroare
// Se afiseaza codul si continutul erorii
echo Codul erorii: . $response->faultCode() .
Mesaj: . $response->faultString();
}
?>
</body>
</html>
suma lor, construieºte un ºir $response ºi, pe baza acestuia, un obiect-rãspuns (prin
iniþializarea obiectului XML_RPC_Response). Obiectul-rãspuns este întors apelantului
(clientul XML_RPC), fiind preluat de acesta ca $response. Clientul extrage ºi afiºeazã
valoarea întorsã de server, care reprezintã suma celor doi parametri trimiºi.
Constructorul XML_RPC_Value() codificã variabilele PHP în date XML-RPC,
conform tabelului 7.1.
Deoarece XML-RPC nu are tipul NULL, tipul NULL PHP este convertit la tipul
<string> XML-RPC. Tipul Array PHP este convertit la tipul <struct> XML-RPC.
Cererea generatã de clientul XML-RPC este similarã cu urmãtoarea (clientul a trimis
numerele 6 ºi 9):
POST /rpc/pear HTTP/1.0
User-Agent: PHP/5.0.4
Host: 127.0.0.1
Content-Type: text/xml
Content-Length: 275
<?xml version=1.0?>
<methodCall>
<methodName>sum</methodName>
<params>
<param>
<value><int>6</int></value>
<value><int>9</int></value>
</param>
</params>
</methodCall>
Connection: close
Content-Type: text/html; charset=ISO-8859-2
}
else {
// Daca a fost generata o eroare
echo Codul erorii: . $response->faultCode() .
Mesaj: . $response->faultString();
}
}
?>
</body>
</html>
if (!$result = mysql_query($query)) {
// Interogare nereusita
return new XML_RPC_Response(0, $XML_RPC_erruser+3,
Interogarea nu a fost executata);
}
// Se verifica rezultatul interogarii
if (mysql_num_rows($result) == 1) {
// numele si parola au fost gasite in baza de date
return new XML_RPC_Response(new XML_RPC_Value(1, boolean));
}
else {
// numele si parola nu au fost gasite in baza de date
return new XML_RPC_Response(new XML_RPC_Value(0, boolean));
}
}
?>
Numele utilizatorului (name) ºi parola (password) sunt incluse într-o datã struct.
Procedura apelatã pe server este login. Funcþia care serveºte aceastã procedurã este
phpLogin(). În interiorul funcþiei, din obiectul trimis de client este extrasã data de tip
struct, apoi sunt extraºi membrii structurii ºi valorile acestora. Valorile obþinute sunt
utilizate pentru a construi o interogare SQL, care cautã aceste valori în tabelul users al
bazei de date rpc. În cazul în care valorile cãutate sunt gãsite, serverul întoarce (într-un
obiect-rãspuns) valoarea logicã 1 (clientul XML-RPC va interpreta aceastã valoare în
sensul cã accesul este permis). În caz contrar, obiectul întors include valoarea logicã 0
(clientul va interpreta aceastã valoare în sensul cã accesul este interzis).
Folosind PEAR::XML_RPC, programatorul poate genera erori în script folosind
obiectul XML_RPC_Response. Codul numeric al acestor erori trebuie sã înceapã de la
valoarea $XML_RPC_erruser +1 (unde $XML_RPC_erruser este o variabilã globalã
care seteazã codul erorii generate în clasele PEAR::XML_RPC).
Cererea generatã de clientul XML-RPC este similarã cu urmãtoarea (clientul trimite
ºirurile daniel ºi password):
POST /rpc/pear HTTP/1.0
User-Agent: PHP/5.0.4
Host: 127.0.0.1
Content-Type: text/xml
Content-Length: 275
<?xml version=1.0?>
<methodCall>
<methodName>sum</methodName>
<params>
<param>
<value><string>daniel</string></value>
<value><string>password</string></value>
</param>
PHP ªI SERVICIILE WEB 187
</params>
</methodCall>
Rãspunsul generat de serverul XML-RPC este similar cu urmãtorul:
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2005 18:37:30 GMT
Server: Apache/2.0.52 (Win32) PHP/5.0.4
X-Powered-By: PHP/5.0.4
Content-Length: 641
Connection: close
Content-Type: text/html; charset=ISO-8859-2
SOAP în PHP
Implementãri ale SOAP în PHP
Protocolul SOAP a fost implementat prin intermediul limbajului PHP în diverse instru-
mente, precum:
NuSOAP (http://sourceforge.net/projects/nusoap), dezvoltat iniþial ca SOAPx4 (de
cãtre Dietrich Ayala, http://dietrich.ganx4.com/nusoap);
pachetul PEAR::SOAP (http://pear.php.net/package/SOAP);
188 PROGRAMAREA ÎN PHP
extensia SOAP pentru PHP, care poate fi utilizatã pentru a scrie servere ºi clienþi
SOAP. Utilizarea acestei extensii necesitã prezenþa în sistem a bibliotecii libxml2
(http://www.xmlsoft.org);
SWSAPI, care este un Web Service API (dezvoltat de compania Active State) numit
Simple Web Service API, adicã SWSAPI. În fapt, este un API unificat pentru
programarea serviciilor Web în Perl, PHP ºi Python.
</input>
<output>
<soap:body use=encoded
namespace=http://127.0.0.1/soap
encodingStyle=http://schemas.xmlsoap.org/
soap/encoding//>
</output>
</operation>
</binding>
<service name=echo>
<port name=echoPort
binding=tns:echoStringBinding>
<soap:address location=http://127.0.0.1/soap/server.php/>
</port>
</service>
</definitions>
veþi obþine ºi cererea ºi rãspunsul SOAP. Astfel, cererea SOAP formulatã de client este
similarã cu:
POST /soap/server.php?php HTTP/1.0
Host: localhost
User-Agent: NuSOAP/0.7.2 (1.94)
Content-Type: text/xml; charset=ISO-8859-1
SOAPAction:
Content-Length: 522
<ns1:echoString xmlns:ns1=http://tempuri.org>
<inputString xsi:type=xsd:string>PHP</inputString>
</ns1:echoString>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Rãspunsul SOAP furnizat de server va fi similar cu similar cu:
HTTP/1.1 200 OK
Date: Wed, 24 Aug 2005 18:02:43 GMT
Server: Apache/2.0.52 (Win32) PHP/5.0.4
X-Powered-By: PHP/5.0.4
X-SOAP-Server: NuSOAP/0.7.2 (1.94)
Content-Length: 532
Connection: close
Content-Type: text/xml; charset=ISO-8859-1
veþi copia fiºierul nusoap.php. Directorul server trebuie protejat prin intermediul unui
fiºier .htaccess plasat chiar în acest director. Conþinutul acestui fiºier poate fi similar cu:
AuthName zona secreta
AuthType Basic
AuthUserFile etc/httpd/utilizatori
require valid-user
Dacã doriþi sã obþineþi date suplimentare legate de autentificarea HTTP, consultaþi
capitolul 27, Utilizarea variabilelor-sesiune, secþiunea Autentificarea HTTP, din
lucrarea Traian Anghel, Dezvoltarea aplicaþiilor Web folosind XHTML, PHP ºi MySQL,
Editura Polirom, Iaºi, 2005.
</td>
</tr>
</table>
</form>
<?php
if(isset($_GET[submit])) {
require_once(nusoap.php);
$type = $_GET[type];
$parameters = array(symbol => $type);
$client = new soapclient(http://www.sugartech.co.za/
soap/wsdlserver.php);
$result = $client->call(PreviousClose, $parameters);
if(!$err = $client->getError()) {
echo <center><h2>Sugar Price</h2></center>;
echo <table align=center border=1 cellpadding=5>;
foreach($result as $cheie => $element)
echo <tr><td width=100 bgcolor=#d6d6d6> .
$cheie .
</td><td width=100 bgcolor=#f9f9f9> .
$element .
</td></tr>;
echo </table>;
}
else {
Eroare: . $err;
}
//echo $client->request;
//echo $client->response;
}
?>
transport=http://schemas.xmlsoap.org/soap/http/>
<operation name=doGoogleSearch>
<soap:operation soapAction=urn:GoogleSearchAction/>
<input>
<soap:body use=encoded
namespace=urn:GoogleSearch
encodingStyle=http://schemas.xmlsoap.org/
soap/encoding//>
</input>
<output>
<soap:body use=encoded
namespace=urn:GoogleSearch
encodingStyle=http://schemas.xmlsoap.org/
soap/encoding//>
</output>
</operation>
</binding>
Dupã cum se poate constata, parametrii de cãutare (doi având tipul xsd:boolean ,
doi tipul xsd:int ºi ºase tipul xsd:string), care trebuie precizaþi în cererea SOAP
formulatã de client (vezi mesajul doGoogleSearch), sunt urmãtorii:
key (desemneazã cheia furnizatã de Google, fiind necesarã pentru accesarea serviciului);
q (ºirul cãutat);
start (indexul de start pentru numãrarea rezultatelor cãutãrii, de exemplu 1);
maxResults (numãrul de rezultate întors ca rãspuns la o cerere; valoarea maximã
posibilã este 10);
filter (valorile posibile sunt FALSE ºi TRUE. Valoarea TRUE activeazã un mecanism
de filtrare care eliminã rezultatele foarte asemãnãtoare sau care provin din aceeaºi
paginã Web. Google recomandã utilizarea valorii FALSE);
200 PROGRAMAREA ÎN PHP
<xsd:element name=relatedInformationPresent
type=xsd:boolean/>
<xsd:element name=hostName type=xsd:string/>
<xsd:element name=directoryCategory
type=typens:DirectoryCategory/>
<xsd:element name=directoryTitle type=xsd:string/>
</xsd:all>
</xsd:complexType>
<xsd:complexType name=ResultElementArray>
<xsd:complexContent>
<xsd:restriction base=soapenc:Array>
<xsd:attribute ref=soapenc:arrayType
wsdl:arrayType=typens:ResultElement[]/>
</xsd:restriction>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
</types>
Dupã cum puteþi constata, tipul complex GoogleSearchResult include unsprezece
date (douã având tipul xsd:boolean, trei tipul xsd:int, una tipul xsd:double,
trei tipul xsd:string, una tipul complex typens:ResultElementArray ºi alta
tipul complex typens:DirectoryCategoryArray) . Am prezentat, de asemenea, ºi
definiþia tipului complex typens:ResultElementArray.
Puteþi afla semnificaþia datelor ale cãror declaraþii de tipuri le-am prezentat citind
documentul APIs_Reference.html. În continuare, vom prezenta numai câteva (cele pe
care le vom folosi efectiv):
resultElements (tablou care conþine rezultatele cãutãrii);
estimatedTotalResultsCount (numãrul estimat al rezultatelor obþinute în cãu-
tarea curentã);
resultElements[URL] (URL-ul absolut asociat unui rezultat al cãutãrii);
resultElements[title] (titlul unui rezultat al cãutãrii, întors în format HTML);
resultElements[snippet] (aratã ºirul cãutat în contextul în care apare într-un
rezultat al cãutãrii).
În listing-ul 7.10 este prezentat un client SOAP pentru serviciul GoogleSearchService
(operaþiunea doGoogleSearch), scris în PHP, care utilizeazã biblioteca NuSOAP. Salvaþi
scriptul în fiºierul clientGoogleSearch.php.
7.10. Fiºierul clientGoogleSearch.php
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Servicii Web cu NuSOAP</title>
</head>
202 PROGRAMAREA ÎN PHP
<body>
<h3>Client SOAP/NuSOAP pentru accesarea
<i>GoogleSearchService</i></h3>
<form method=post
action=<?php echo $_SERVER[PHP_SELF]; ?>>
<table bgcolor=#d6d6d6 cellpadding=5>
<tr>
<td>Sir de cautare</td>
<td><input type=text name=myString size=50
value=<?php if(isset($_POST[myString]))
echo $_POST[myString]; ?>></td>
</tr>
<tr>
<td>Selecteaza Limba</td>
<td align=center>
<select name=lang>
<option value=all
<?php if(isset($_POST[lang]) &&
$_POST[lang] == all)
echo selected; ?>>Toate limbile</option>
<option value=lang_ro
<?php if(isset($_POST[lang]) &&
$_POST[lang] == lang_ro)
echo selected; ?>>Limba romana</option>
<option value=lang_en
<?php if(isset($_POST[lang]) &&
$_POST[lang] == lang_en)
echo selected; ?>>Limba engleza</option>
</select>
</td>
</tr>
<tr>
<td></td>
<td align=center>
<input type=reset name=Reset>
<input type=submit name=submit
value=Search>
</td>
</tr>
</table>
</form>
<?php
if(isset($_POST[submit]) && isset($_POST[myString]) &&
trim($_POST[myString])) {
require_once(nusoap.php);
$myString = $_POST[myString];
PHP ªI SERVICIILE WEB 203
if(isset($_POST[lang])){
$lang = $_POST[lang];
if($lang == all)
$lang = ;
}
// Seteaza parametrii
$parameters = array(
key =>********************************, // 32 caractere
q => $myString,
start => 1,
maxResults => 10,
filter => false,
restrict => ,
safeSearch => true,
lr => $lang,
ie => ,
oe =>
);
$soapclient = new soapclient (
http://api.google.com/GoogleSearch.wsdl, wsdl);
$results = $soapclient->call(doGoogleSearch, $parameters);
if (is_array($results[resultElements])) {
echo <p><b>Cautarea pentru <i> .
$myString . </i> a gasit .
$results[estimatedTotalResultsCount] .
intrari:</b></p>;
foreach ($results[resultElements] as $result) {
echo <p><a href= .
$result[URL] .
target=_blank> .
($result[title] ? $result[title] :
fara title) .
</a><br /> .
($result[snippet] ? $result[snippet] :
fara snippet) .
<br>.$result[URL].</p>;
}
} else { // Daca nu sunt intoarse rezultate
echo <b>Cautarea cu Google nu a intors nici
un rezultat.</b>;
}
}
?>
</body>
</html>
204 PROGRAMAREA ÎN PHP
Implicit, serviciul întoarce numai acele documente care includ toþi termenii prezenþi
în ºirul de cãutare (preluat în parametrul q). Cu alte cuvinte, nu este necesar sã includeþi
(în script) operatorul AND între termeni. Puteþi gãsi alte precizãri privind ºirul de
cãutare în documentul APIs_Reference.html, secþiunea 2.2, Query Terms.
Conform restricþiilor impuse de Google, se pot efectua cel mult 1.000 de cãutãri
zilnic utilizând o cheie datã, iar la o cãutare se pot afiºa cel mult 10 rezultate.
Totuºi, dacã numãrul de rezultate obþinute în urma unei cereri este mai mare
decât 10, le puteþi afiºa pe toate (sau numai o parte din ele) utilizând o buclã for în care
incrementaþi parametrul start (i.e., într-o paginã vor fi afiºate rezultatele obþinute în
urma mai multor cereri SOAP adresate serviciului de cãutare).
<operation name=PowerSearchRequest>
<soap:operation soapAction=http://soap.amazon.com />
<input>
<soap:body use=encoded
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://soap.amazon.com />
</input>
<output>
<soap:body use=encoded
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://soap.amazon.com />
</output>
</operation>
<operation name=ManufacturerSearchRequest>
<soap:operation soapAction=http://soap.amazon.com />
<input>
<soap:body use=encoded
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://soap.amazon.com />
</input>
<output>
<soap:body use=encoded
encodingStyle=http://schemas.xmlsoap.org/soap/encoding/
namespace=http://soap.amazon.com />
</output>
</operation>
</binding>
<message name=PowerSearchRequest>
<part name=PowerSearchRequest type=typens:PowerRequest />
</message>
<message name=PowerSearchResponse>
<part name=return type=typens:ProductInfo />
</message>
<message name=ManufacturerSearchRequest>
<part name=ManufacturerSearchRequest
type=typens:ManufacturerRequest />
</message>
<message name=ManufacturerSearchResponse>
<part name=return type=typens:ProductInfo />
</message>
Mesajele prezentate utilizeazã tipuri de date typens:PowerRequest ,
typens:ManufacturerRequest ºi ProductInfo incluse în spaþiul de nume al
documentului, xmlns:typens=http://soap.amazon.com ºi definite în secþiunea
types a fiºirului WSDL.
Tipul typens:PowerRequest (al datelor trimise în cererea SOAP pentru operaþiunea
PowerSearchRequest ) este definit astfel:
<xsd:complexType name=PowerRequest>
<xsd:all>
<xsd:element name=power type=xsd:string/>
<xsd:element name=page type=xsd:string/>
<xsd:element name=mode type=xsd:string/>
<xsd:element name=tag type=xsd:string/>
<xsd:element name=type type=xsd:string/>
<xsd:element name=devtag type=xsd:string/>
<xsd:element name=sort type=xsd:string minOccurs=0/>
<xsd:element name=locale type=xsd:string minOccurs=0/>
</xsd:all>
</xsd:complexType>
<head>
<title>Servicii Web cu PEAR::SOAP</title>
<style>
.results {
font-family: tahoma; font-size: 14px;
}
td#authors {
background: #d6d6d6; font-family: tahoma;
font-size: 14px;
}
td#product {
background: #d6d6d6; font-family: tahoma;
font-size: 18px; font-style: bold;
}
td#price {
background: #ffffff; font-family: tahoma;
font-size: 14px; color: #ff0000;
}
</style>
</head>
<body>
<h3>Client PEAR::SOAP pentru accesarea
<i>AmazonSearchService</i></h3>
<?php
require_once(SOAP/Client.php);
// URL-ul fiserului WSDL al serviciului
$wsdl_url = http://soap.amazon.com/schemas3/
AmazonWebServices.wsdl;
// Se instantiaza un obiect SOAP_WSDL
$WSDL = new SOAP_WSDL($wsdl_url);
// Metoda getProxy() intoarce un obiect-client
$client = $WSDL->getProxy();
// Parametrii care vor fi inclusi in cererea SOAP
$params = array(
manufacturer => OReilly,
mode => books,
sort => +title,
page => 1,
type => lite,
tag => trachtenberg-20,
devtag => *******************, //ID (10 caractere)
);
/* Este apelata metoda ManufacturerSearchRequest a serviciului,
careia i se trimit parametri intr-un tablou asociativ. */
$results = $client->ManufacturerSearchRequest($params);
// Sunt afisate rezultatele intoarse de metoda
210 PROGRAMAREA ÎN PHP
sau numai o parte dintre ele , creaþi o buclã for, incrementaþi page ºi afiºaþi câte
produse doriþi;
type stabileºte ce rezultate vor fi întoarse. Valoarea lite a parametrului (utilizatã
în exemplul oferit) se referã la faptul cã sunt întoarse informaþii referitoare numai la
numele produsului, autor, preþ, imagini (trei tipuri) ºi producãtor. Valoarea heavy se
referã la faptul cã sunt întoarse toate informaþiile disponibile despre produs;
tag valoarea sa este un nume asociat Amazon;
devtag este ID-ul obþinut de dezvoltator (vezi începutul acestei secþiuni).
Utilizaþi AmazonSearchService
pentru a cãuta informaþii referitoare
la produsele oferite de Amazon.com.
Utilizaþi AmazonSearchService
pentru a cãuta informaþii
folosind criterii de cãutare
multiple
PHP ºi REST
Introducere în REST
Termenul REST (Representational State Transfer) a fost introdus de Roy Fielding pentru
a descrie arhitectura Web, fiind un model pentru calculul distribuit. Atunci când este
aplicat serviciilor Web, implicã trei tehnologii: HTTP, URI ºi XML. În continuare vom
detalia noþiunea de serviciu Web REST.
Ca ºi alte tipuri de servicii Web, arhitectura REST este bazatã pe conceptul de
resursã. Unui serviciu Web REST îi sunt impuse urmãtoarele constrângeri:
1. Interfeþele sale sunt limitate la protocolul HTTP. Astfel, în REST sunt definite
urmãtoarele interfeþe:
HTTP GET, utilizatã pentru a obþinere o reprezentare a unei resurse. Un consu-
mator utilizeazã metoda pentru a regãsi o reprezentare folosind un URI. Serviciile
furnizate prin aceastã interfaþã nu atrag nici o obligaþie din partea consumatorilor;
HTTP DELETE, utilizatã pentru pentru a elimina reprezentãrile unei resurse;
HTTP POST, utilizatã pentru actualizarea sau crearea reprezentãrilor unei resurse;
HTTP PUT, utilizatã pentru crearea reprezentãrilor unei resurse.
PHP ªI SERVICIILE WEB 213
2. Mesajele utilizeazã formatul XML, putând fi validate prin specificaþii XML Schema
sau RELAX NG.
Serviciile Web REST necesitã o infrastructurã software minimalã, adicã una care sã
implementeze protocolul HTTP ºi tehnologii de procesare XML, suportate de cele mai
multe platforme ºi limbaje de programare.
Amazon ºi REST
Dupã cum aþi remarcat, filozofia serviciilor Web REST este aceea de a nu instala
instrumente suplimentare (pe client ºi server) pentru a trimite ºi primi date, ci de a le
utiliza pe cele deja existente. Utilizând tehnologii XML ca SAX, DOM, XSLT, XPath,
XQuery etc., datele XML obþinute pot fi prelucrate în forma doritã.
Mai multe companii (printre care Amazon ºi Yahoo!) au pus la dispoziþia progra-
matorilor servicii Web bazate pe REST. Clientul trimite o cerere HTTP GET serviciului.
Acesta proceseazã cererea ºi întoarce rezultatele obþinute fãrã a le modifica. REST
utilizeazã un ºir de interogare pentru a transmite o serie de parametri serviciului Web. În
continuare, vom prezenta un exemplu în care este realizat un client pentru accesarea
serviciului Amazon.
În directorul DocumentRoot, creaþi subdirectorul rest în care veþi salva fiºierele ce
includ codul-sursã prezentat în aceastã secþiune. Pentru început, va trebui sã obþinem
documentul XML (corespunzãtor cererii formulate) întors de serviciul de cãutare Amazon.
În acest scop, utilizaþi scriptul din listing-ul 7.12, pe care-l veþi salva în fiºierul
getXML.php:
<?php
$baseurl = http://xml.amazon.com/onca/xml3;
$query_string = ;
/* Se stabilesc valori pentru parametrii care vor fi
trimisi serviciului */
$params = array(ManufacturerSearch => OReilly,
mode => books,
sort => +salesrank,
page => 1,
type => lite,
f => xml,
t => trachtenberg-20 ,
dev-t => ******************** ,
);
// Se construieste sirul de interogare
foreach ($params as $key => $value) {
$query_string .= $key= . urlencode($value) . &;
}
// Se construieste URL-ul care va include parametrii cererii
$url = $baseurl?$query_string;
214 PROGRAMAREA ÎN PHP
Dupã cum se poate observa, trebuie precizaþi o serie de parametri care vor fi trimiºi
serviciului. Aceºtia sunt valori ale elementelor tabloului $params. Ulterior, elementele
tabloului sunt extrase ºi introduse în ºirul de interogare $query_string . Acesta este
concatenat cu $baseurl, care conþine URL-ul serviciului, obþinându-se ºirul $url .
Semnificaþiile parametrilor sunt similare cu cele prezentate în secþiunea referitoare la
construcþia unui client SOAP pentru serviciul Amazon.com. Este adãugat parametrul f ,
având valoarea xml (în exemplul nostru), ceea ce înseamnã cã serviciul va întoarce un
document XML.
Dupã crearea unei instanþe a clasei DOMDocument, se încarcã, utilizând metoda
load(), documentul XML localizat la adresa $url, dupã care se afiºeazã ºirul întors
de metoda saveXML(). Iatã un fragment din acest document:
<?xml version=1.0 encoding=UTF-8 ?>
<ProductInfo
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:noNamespaceSchemaLocation=http://xml.amazon.com/schemas3/
dev-lite.xsd>
<Request>
<Args>
<Arg value= name=UserAgent />
<Arg value=1Y30FNW9Z8E728G7JX5R name=RequestID />
<Arg value=us name=locale />
<Arg value=1 name=page />
<Arg value=******************** name=dev-t />
<Arg value=trachtenberg-20 name=t />
<Arg value=xml name=f />
<Arg value=books name=mode />
<Arg value=OReilly name=ManufacturerSearch />
<Arg value=lite name=type />
<Arg value=salesrank name=sort />
</Args>
</Request>
<TotalResults>1547</TotalResults>
<TotalPages>155</TotalPages>
<Details url=http://www.amazon.com/exec/obidos/ASIN/...>
<Asin>0596009410</Asin>
<ProductName>Mac OS X : The Missing Manual, Tiger Ed
(Missing Manual)</ProductName>
PHP ªI SERVICIILE WEB 215
<Catalog>Book</Catalog>
<Authors>
<Author>David Pogue</Author>
</Authors>
<ReleaseDate>12 July, 2005</ReleaseDate>
<Manufacturer>OReilly</Manufacturer>
<ImageUrlSmall>http://images.amazon.com/images/P/
0596009410.01.THUMBZZZ.jpg</ImageUrlSmall>
<ImageUrlMedium>http://images.amazon.com/images/P/
0596009410.01.MZZZZZZZ.jpg</ImageUrlMedium>
<ImageUrlLarge>http://images.amazon.com/images/P/
0596009410.01.LZZZZZZZ.jpg</ImageUrlLarge>
<Availability>Usually ships in 24 hours</Availability>
<ListPrice>$29.95</ListPrice>
<OurPrice>$19.18</OurPrice>
<UsedPrice>$13.23</UsedPrice>
</Details>
<!-- urmeaza informatii despre inca 9 lucrari -->
</ProductInfo>
Dupã cum se poate observa, elementul-rãdãcinã al documentului XML este
ProductInfo. Acesta are ca elemente-copil Request (include parametrii cererii),
TotalResults , TotalPages ºi nouã elemente Details. Fiecare element Details
are ca elemente-copil Asin, ProductName , Catalog, Authors, ReleaseDate ,
Manufacturer, ImageUrlSmall, ImageUrlMedium, ImageUrlLarge, Availability,
ListPrice ºi OurPrice. La rândul sãu, elementul Authors are ca elemente-copil
unul sau mai multe elemente Author.
De asemenea, se pot obþine informaþii despre elementele incluse în documentul XML
întors de serviciu utilizând ºi schema XML precizatã la începutul documentului: http://
xml.amazon.com/schemas3/dev-lite.xsd, care include definiþiile acestor elemente. În
general, Amazon Web Services oferã urmãtoarele documente în care sunt incluse
definiþiile elementelor utilizate în cererile ºi rãspunsurile SOAP ºi REST:
lite DTD: http://xml.amazon.com/schemas3/dev-lite.dtd
heavy DTD: http://xml.amazon.com/schemas3/dev-heavy.dtd
lite XSD: http://xml.amazon.com/schemas3/dev-lite.xsd
heavy XSD: http://xml.amazon.com/schemas3/dev-heavy.xsd
SOAP WSDL: http://soap.amazon.com/schemas3/AmazonWebServices.wsdl
Documentul XML obþinut poate fi prelucrat în mai multe moduri. De exemplu, datele
incluse pot fi introduse într-o bazã de date, pot fi afiºate în browser dupã ce sunt
procesate sau transformate utilizând XSLT etc. În continuare, vom aplica metodele DOM
ºi SimpleXML (PHP versiunea 5.0 sau una ulterioarã) pentru procesarea documentului
XML. Salvaþi conþinutul listing-ului 7.13 în fiºierul getXHTML.php ºi executaþi-l (http://
127.0.0.1/rest/getXHTML.php):
216 PROGRAMAREA ÎN PHP
}
?>
</body>
</html>
Accesaþi Amazon
Web Services REST.
.$Result->Summary.<br /></p>;
}
?>
</body>
</html>
Aici, se realizeazã
o cãutare dupã
cuvântul-cheie Einstein.
Scriptul din listing-ul 7.15 include un client pentru serviciul Amazon. Salvaþi scriptul
în fiºierul rest.php.
<?php
require_once(HTTP/Request.php);
require_once (XML/Unserializer.php);
$baseurl = (http://xml.amazon.com/onca/xml3);
$query_string = ;
$params = array(ManufacturerSearch => O\Reilly,
mode => books,
sort => +salesrank,
page => 1,
type => lite,
f => xml,
t => trachtenberg-20 ,
dev-t => ********************,
);
foreach ($params as $key => $value) {
$query_string .= $key= . urlencode($value) . &;
}
$amazon_url = $baseurl?$query_string;
// Se creeaza un obiect HTTP_Request, specificand un URL
$Request = &new HTTP_Request($amazon_url);
// Se trimite cererea catre server
$status = $Request->sendRequest();
// Se verifica daca sunt erori
if (PEAR::isError($status)) {
die(Problema la conectare: . $status->toString());
}
// Daca sunt probleme...
if ($Request->getResponseCode() != 200) {
die(Cerere esuata: . $Request->getResponseCode());
}
// Se obtine corpul XML al raspunsului trimis de Amazon
$amazon_xml = $Request->getResponseBody();
// Se creeaza un obiect XML_Unserializer
$Unserializer = new XML_Unserializer();
// Se deserializeaza corpul XML obtinut anterior
$status = $Unserializer->unserialize($amazon_xml);
// Se verifica daca sunt erori
if (PEAR::isError($status)) {
die($status->getMessage());
}
// Se obtin datele PHP din XML
$amazon_data = $Unserializer->getUnserializedData($amazon_xml);
224 PROGRAMAREA ÎN PHP
?>
<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Strict//EN
http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd>
<html xmlns=http://www.w3.org/1999/xhtml
lang=en xml:lang=en>
<head>
<title>Amazon Search</title>
</head>
<body>
<h1>Amazon Search</h1>
<table>
<tr>
<td>
<?php
if(isset($amazon_data))
foreach ($amazon_data[Details] as $details) {
?>
<table width=500>
<tr>
<td align=center><b>
<?php echo nl2br(wordwrap($details[ProductName],
30)); ?></b>
</td>
<td rowspan=2 align=right>
<img src=<?php echo $details[ImageUrlSmall]; ?> />
</td>
</tr>
<tr>
<td>
<?php
$Authors=$details[Authors];
$Auths=$Authors[Author];
if(is_array($Auths)) {
echo Authors:;
foreach($Auths as $author)
echo <br />
. $author;
}
else{
echo Author:;
echo $Auths;
}
?><br />
ISBN: <?php echo $details[Asin]; ?><br />
Price: <?php echo $details[OurPrice]; ?><br />
</td>
PHP ªI SERVICIILE WEB 225
</tr>
</table>
<?php
}
?>
</td>
</tr>
</table>
</body>
</html>
header(Content-Type: text/xml);
echo $amazon_xml;
?>
$result = htmlspecialchars($uddi->query(find_business,
$options));
echo <pre>$result</pre>;
?>
În scriptul anterior au fost utilizate urmãtoarele metode:
constructorul UDDI::UDDI primeºte ca argumente numele registrului UBR care va
fi utilizat pentru cãutare (valorile posibile sunt IBM ºi Microsoft) ºi (ca parametru
opþional) versiunea UDDI utilizatã (valorile posibile sunt 1 , 2 ºi 3 , valoarea implicitã
fiind 1; se recomandã utilizarea versiunii 2 );
metoda UDDI::query() trimite o interogare serverului care gãzduieºte registrul.
Ea primeºte ca argumente mesajul UDDI (adicã numele operaþiunii care trebuie
realizatã de registru) ºi un tablou cu parametri de interogare, întorcând sub formã de
ºir rãspunsul serverului.
Iatã o parte din rãspunsul întors de server (mesajul SOAP) ºi afiºat de browser, dupã
execuþia scriptului getresponse.php (http://127.0.0.1/uddi/getresponse.php):
HTTP/1.1 200 OK
Connection: close
Date: Fri, 23 Sep 2005 18:15:52 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 1.1.4322
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 1995
</businessInfo>
<businessInfo
businessKey=84825cd2-d874-4b5b-8c70-79be8f870775>
<name xml:lang=en>IBM WSTK Demo</name>
<description xml:lang=en />
<serviceInfos />
</businessInfo>
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
</businessInfos>
</businessList>
</soap:Body>
</soap:Envelope>
Microsoft opereazã urmãtoarele douã noduri UBR publice, accesibile prin interfeþe
de programare UDDI:
un nod UBR, utilizat pentru interogare ºi publicare, accesibil prin interfeþele:
o UDDI Web User Interface: http://uddi.microsoft.com;
o UDDI API Inquiry Interface: http://uddi.microsoft.com/inquire;
o UDDI API Publish Interface: http://uddi.microsoft.com/publish;
un nod UBR pentru dezvoltare ºi testare (dedicat dezvoltatorilor), accesibil prin
urmãtoarele interfeþe:
o UDDI Web User Interface: http://test.uddi.microsoft.com;
o UDDI API Inquiry Interface: http://test.uddi.microsoft.com/inquire;
o UDDI API Publish Interface: http://test.uddi.microsoft.com/publish.
În continuare sunt enumerate operaþiunile suportate de nodul utilizat pentru dezvol-
tarea ºi testarea aplicaþiilor dezvoltatorilor, accesibil prin UDDI API Inquiry Interface.
Între paranteze sunt precizate numele operaþiunilor, aºa cu sunt ele definite în documentul
WSDL al serviciului (http://test.uddi.microsoft.com/inquire.asmx?WSDL):
PHP ªI SERVICIILE WEB 229
FindBinding (find_binding);
GetBusinessDetailExt (get_businessDetailExt);
FindRelatedBusinesses (find_relatedBusinesses);
ValidateCategorization (validate_categorization);
GetTModeDetail (get_tModeDetail);
GetBusinessDetail (get_businessDetail);
FindTModel (find_tModel );
FindService (find_service);
GetServiceDetail (get_serviceDetail);
FindBusiness (find_business );
GetBindingDetail (get_bindingDetail).
În scripturile anterioare a fost utilizatã operaþiunea FindBusiness (find_business ,
în documentul WSDL). Exemple pentru cererea ºi rãspunsul SOAP corespunzãtoare pot
fi analizate la adresa http://test.uddi.microsoft.com/inquire.asmx?op=FindBusiness.
230 PROGRAMAREA ÎN PHP
CAPITOLUL 8
PHP ºi AJAX
Când ai puþine dorinþe, ai ºi puþine lipsuri.
Plutarh
AJAX este un acronim pentru Asynchronous JavaScript And XML. Acest termen
(similar cu XMLHttpRequest scripting) se referã o tehnicã de tip remote scripting,
utilizatã pentru crearea aplicaþiilor Web interactive. Tehnica amintitã foloseºte, combinat,
urmãtoarele limbaje ºi tehnologii:
XHTML ºi CSS pentru prezentarea informaþiilor;
Document Object Model (DOM) ºi JavaScript pentru afiºare dinamicã ºi interacþiune;
XML ºi XSLT pentru schimbul de date ºi manipularea acestora;
obiectul XMLHttpRequest pentru accesarea asincronã a datelor.
Dupã cum se poate constata, AJAX eliminã interacþiunea obiºnuitã dintre clientul ºi
serverul Web, bazatã pe reîncãrcarea completã a paginii, introducând un inter-
mediar motorul AJAX între aceºtia. Astfel, este posibilã actualizarea unor
porþiuni (componente) ale paginii, fãrã a fi necesarã reîncãrcarea completã a acesteia.
Interacþiunea dintre motorul AJAX ºi server este asincronã, ceea ce permite utilizatorului sã
interacþioneze cu aplicaþia fãrã a aºtepta actualizarea unor componente ale paginii. Rezultã,
astfel, o vitezã mãritã în execuþia aplicaþiilor Web, similarã cu cea a aplicaþiilor desktop.
Tehnica AJAX este utilizatã într-un numãr mare de aplicaþii, dintre care enumerãm Orkut
(https://www.orkut.com/Login.aspx), Google Groups (http://groups-beta.google.com),
Google Maps (http://google.maps.com), Gmail (http://www.gmail.com), Google Suggest
(http://www.google.com/webhp?complete=1&hl=en), Flikr (http://www.flickr.com/),
ObjectGraph Dictionary (http://www.objectgraph.com/dictionary/) etc. De asemenea,
motorul de cãutare Amazon (www.a9.com) utilizeazã o tehnicã similarã.
Metode
În continuare, sunt prezentate (în ordine alfabeticã), pe scurt, metodele comune ale
instanþelor obiectului XMLHttpRequest suportate de browserele Mozilla 1+, Firefox,
Netscape 7+, Internet Explorer 5+ ºi Opera 8.0:
abort(): opreºte cererea curentã;
getAllResponseHeaders(): întoarce ca un ºir de caractere (string) setul complet
de anteturi HTTP (nume ºi valori) ale unei resurse;
getResponseHeader(name): întoarce ca un ºir de caractere (string) valoarea
antetului name inclus în anteturile HTTP ale rãspunsului serverului. În exemplul
urmãtor este obþinutã data ultimei modificãri (antetul Last-Modified ) a unui fiºier
aflat pe server:
234 PROGRAMAREA ÎN PHP
request.getResponseHeader(Last-Modified)
open(method,url[,asyncFlag[,userName[,password]]]): iniþia-
lizeazã o cerere cãtre serverul Web, având ca parametri metoda ( method) care va fi
utilizatã de cerere, URL-ul (url ) folosit pentru conexiunea cu serverul ºi alþi
parametri ai cererii curente. Primii doi parametri sunt obligatorii. Metodele care pot
fi utilizate sunt cele ale protocolului HTTP. Cele mai des utilizate sunt GET (dacã se
intenþioneazã obþinerea unor date de pe server), POST (dacã se doreºte trimiterea
datelor pe server, mai ales dacã lungimea acestora depãºeºte 512 bytes sau dacã se
doreºte modificarea sau crearea resurselor pe server) ºi HEAD (dacã se doreºte
obþinerea anteturilor unei resurse). Al treilea parametru (asyncFlag ) este opþional
ºi precizeazã dacã tranzacþiile cu serverul trebuie manipulate sincron sau asincron.
Dacã valoarea acestui parametru de tip boolean este TRUE (valoare implicitã),
înseamnã cã procesarea scriptului are loc imediat dupã apelul funcþiei send(), fãrã
a aºtepta rãspunsul serverului (manipulare asincronã). Dacã valoarea parametrului
este FALSE, scriptul aºteaptã sosirea rãspunsului de la server (manipulare sincronã).
Este indicat sã utilizaþi modul asincron, pentru ca utilizatorul sã nu decidã închiderea
browserului în cazul în care rãspunsul serverului soseºte târziu. Ceilalþi parametri
opþionali sunt numele de utilizator (username) ºi parola (password), utilizaþi în
cazul în care conectarea la server necesitã autentificare;
setRequestHeader(header,value): seteazã un antet HTTP al cererii curente
(header este numele antetului care trebuie precizat, iar value este corpul acestuia).
Înainte de a fi apelatã aceastã funcþie, trebuie utilizatã funcþia open(). În exemplul
urmãtor, antetul Content-Type va primi valoarea text/xml :
request.setRequestHeader(Content-Type,text/xml)
Proprietãþi
Dupã trimiterea cererii, pot fi utilizate proprietãþile instanþei obiectului XMLHttpRequest
(toate proprietãþile sunt read only). În continuare, sunt prezentate proprietãþile comune
browserelor amintite anterior:
onreadystatechange : oferã o modalitate de tratare a evenimentului readyState la
fiecare schimbare a stãrii acestuia. Astfel, prin intermediul acestei proprietãþi poate
fi precizatã o funcþie pentru procesarea rãspunsului serverului, ca în exemplul oferit
în continuare (vezi ºi proprietãþile urmãtoare):
function handlerRequest() {
if (req.readyState == 4) {
if (req.status == 200) {
//instructiuni pentru procesarea raspunsului
}
else {
alert(Cererea nu poate fi satisfacuta:\n
PHP ªI AJAX 235
+req.statusText);
}
}
}
request.onreadystatechange = handlerRequest;
request = false;
}
} else
if(window.ActiveXObject) {
try {
request = new ActiveXObject(Msxml2.XMLHTTP);
} catch(e) {
try {
request = new ActiveXObject(Microsoft.XMLHTTP);
} catch(e) {
request = false;
}
}
}
Pentru crearea unei instanþe a obiectului XMLHttpRequest puteþi utiliza funcþia
getObject(), prezentatã în continuare. Salvaþi codul JavaScript în fiºierul getobject.js:
function getObject() {
var request;
try {
request = new ActiveXObject(Msxml2.XMLHTTP);
} catch (e) {
try {
request = new ActiveXObject(Microsoft.XMLHTTP);
} catch (E) {
request = new XMLHttpRequest();
}
}
if (!request && typeof XMLHttpRequest!=undefined) {
try {
request = new XMLHttpRequest();
} catch (e) {
request = false;
}
}
return request;
}
var url=http://127.0.0.1/xmlhttprequest/
validate.php?userid=+user
request=getObject();
request.onreadystatechange = handleRequest;
request.open(GET,url,true);
if(window.XMLHttpRequest)
request.send(null);
if(window.ActiveXObject)
request.send();
}
function handleRequest() {
if (request.readyState==4)
if(request.status == 200)
if(request.responseText==exista)
alert(Acest utilizator (+user +
) exista deja!);
}
function getFocus(){
document.form1.first.focus();
}
// ]] -->
</script>
</head>
<body onLoad=getFocus();>
<center><h3>Inregistrare</h3>
<form action=validare.php method=get name=form1>
<table cellspacing=5>
<tr><td align=left>First Name</td>
<td><input type=text name=first size=15
maxlength=20 /></td>
</tr>
<tr><td align=left>Second Name</td>
<td><input type=text name=second size=15
maxlength=20 /></td>
</tr>
<tr><td align=left>User ID</td>
<td><input type=text name=userid size=15
maxlength=20
onBlur=validateUserID(this.value); /></td>
</tr>
<tr><td align=left>Password</td>
<td><input type=text name=password size=15
maxlength=20 /></td>
</tr>
<tr><td></td>
<td colspan=2><input type=submit name=submit
240 PROGRAMAREA ÎN PHP
value=Register /></td>
</tr>
<tr><td></td>
<td><input type=reset name=reset
value=Reset /></td>
</tr>
</table>
</form>
</center>
</body>
</html>
Dupã cum se observã, câmpul în care utilizatorul introduce identificatorul pe care ºi-l
doreºte este creat astfel:
<input type=text name=userid
onBlur=validateUserID(this.value);>
Evenimentul onBlur se va produce atunci când este pãrãsit câmpul userid. Validarea
este realizatã prin intermediul funcþiei validateUserID(). În scrierea acestei funcþii
puteþi utiliza ºi un URL relativ:
var URL=validare.php?userid=+userid;
Acþiune rapidã
Tehnica AJAX poate fi utilizatã pentru a efectua diverse acþiuni în pagina încãrcatã în
browser, acþiuni care implicã o cerere cãtre server. În aceastã secþiune vor fi prezentate
douã exemple: în primul exemplu se obþin anteturilor unei resurse, iar în al doilea, se
afiºeazã informaþii referitoare la server.
Cu o cerere HEAD, un server întoarce numai anteturile resursei, ceea ce înseamnã cã
pot fi obþinute informaþii despre aceasta (ca, de exemplu, data ultimei modificãri) fãrã
a o descãrca. Pentru a obþine toate anteturile resursei trebuie utilizatã metoda
getAllResponseHeaders() , iar pentru a obþine numai un anumit antet al acesteia trebuie
utilizatã metoda getResponseHeader(name), în care name este numele antetului ce se
doreºte a fi obþinut. În continuare este prezentat un exemplu prin intermediul cãruia sunt
obþinute toate anteturile HTTP ale unei resurse aflate pe server, respectiv un singur antet
(Last-Modified, care precizeazã data ultimei modificãri) al acesteia. Aplicaþia include un
singur fiºier h.html , al cãrui conþinut este prezentat în listing-ul 8.3:
8.3. Obþinerea anteturilor unei resurse: fiºierul h.html
<!DOCTYPE html
PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN
DTD/xhtml1-transitional.dtd>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<title>Anteturi HTTP</title>
<script src=getobject.js type=text/javascript></script>
<script type=text/javascript language=javascript>
<!-- <[[CDATA[
242 PROGRAMAREA ÎN PHP
var request;
function getHeader(f){
var url=http://127.0.0.1/index.html;
request=getObject();
if (f == 1)
request.onreadystatechange = handleRequest1;
if (f == 2)
request.onreadystatechange = handleRequest2;
request.open(HEAD,url,true);
if(window.XMLHttpRequest)
request.send(null);
if(window.ActiveXObject)
request.send();
}
function handleRequest1(){
if (request.readyState == 4) {
if (request.status == 200) {
ShowDiv(message);
document.getElementById(message).innerHTML =
request.getAllResponseHeaders();
}
else {
ShowDiv(message);
document.getElementById(message).innerHTML =
A aparut o problema:\n+request.statusText;
}
}
}
function handleRequest2(){
if (request.readyState == 4) {
if (request.status == 200) {
ShowDiv(message);
document.getElementById(message).innerHTML =
request.getResponseHeader(Last-Modified);
}
else {
ShowDiv(message);
document.getElementById(message).innerHTML =
A aparut o problema:\n+request.statusText;
}
}
}
/* Functia face vizibil elementul al carui identificator il
primeste ca argument. */
function ShowDiv(divid){
if (document.layers)
PHP ªI AJAX 243
document.layers[divid].visibility=show;
else
document.getElementById(divid).
style.visibility=visible;
}
/* Functia ascunde elementul al carui identificator il
primeste ca argument. */
function HideDiv(divid){
if (document.layers)
document.layers[divid].visibility=hide;
else
document.getElementById(divid).
style.visibility=hidden;
}
// ]]> -->
</script>
</head>
<body>
Vezi <a href=/ onMouseOver=getHeader(1);
onMouseOut=HideDiv(message);>
toate anteturile</a><br />
Vezi <a href=/ onMouseOver=getHeader(2);
onMouseOut=HideDiv(message);>
data ultimei modificari</a><br />
<div id=message style=width: 300px; color: blue;
padding: 5px; border: 1px solid gray; visibility:hidden>
</div>
</body>
</html>
function HideDiv(divid){
if (document.layers)
document.layers[divid].visibility=hide;
else
document.getElementById(divid).
style.visibility=hidden;
}
// ]]> -->
</script>
</head>
<body>
<a href=# onMouseOver=getTime();
onMouseOut=HideDiv(message);>
Informatii despre server</a><br />
<div id=message style=width: 450px; margin-top: 20px;
color:blue; visibility:hidden;>
</div>
</body>
</html>
if(window.ActiveXObject)
request.send();
}
function handleRequest() {
if (request.readyState == 4) {
if (request.status == 200) {
numbers.cell.value = request.responseText;
} else {
alert(Datele cerute nu pot fi cautate:\n +
request.statusText);
}
}
}
function getFocus(){
document.numbers.friend.focus();
}
// ]]> -->
</script>
</head>
<body>
<form name=numbers>
<table>
<tr>
<td>Friend</td>
<td><input type=text name=friend
onBlur=getNumber(this.value); id=friend /></td>
</tr>
<tr>
<td>Cell number</td>
<td><input type=text id=cell name=cell size=20
style=background-color: snow;
border: 2px dashed gray;text-align: center;
disabled=disabled /></td>
</tr>
</table>
</form>
</body>
</html>
În continuare este prezentatã o altã aplicaþie (Nobel Prize) utilizatã pentru extragerea
dinamicã a datelor fãrã refresh. Aplicaþia conþine douã fiºiere: physicist.html (listing-ul
8.8) ºi physicist.php (listing-ul 8.9).
În baza de date XMLHttpRequest, creaþi tabelul info:
mysql>USE xmlhttprequest;
mysql>CREATE TABLE info
->(
->id VARCHAR(20) NOT NULL,
->name VARCHAR(40) NOT NULL,
->year YEAR(4) NOT NULL,
->domain ENUM(mecanica cuantica,fizica atomica) NOT NULL,
->PRIMARY KEY (id)
->);
PHP ªI AJAX 249
}
else {
alert(Datele cerute nu pot fi cautate:\n+
request.statusText);
}
}
}
// ]]> -->
</script>
</head>
<body>
<center><form name=form1>
<select name=physicist onChange=loadInfo(this)>
<option>Alege un nume!</option>
<option value=einstein>Albert Einstein</option>
<option value=heisenberg>Werner Heisenberg</option>
<option value=broglie>Louis de Broglie</option>
<option value=bohr>Niels Bohr</option>
<option value=born>Max Born</option>
</select>
<br /><br />
</form>
<div id=message name=message></div>
</center>
</body>
</html>
CAPITOLUL 9
PHP ºi RSS
Ceea ce nu facem repede nu facem deloc!
Constantin Noica
Ce este RSS?
RSS, dezvoltat iniþial de firma Netscape (martie 1999) ca RSS 0.90, este
numele unei familii de formate XML utilizate de editorii Web în scopul
distribuirii ºtirilor, noutãþilor sau sumarurilor. Aceastã activitate este cunoscutã sub
numele de Web syndication (punerea la dispoziþie a informaþiilor actualizabile).
Prin utilizarea RSS, informaþiile de pe Web devin mult mai uºor de gãsit, iar
programatorii Web pot rãspândi aceste informaþii mult mai uºor grupurilor de utilizatori
interesaþi. Deoarece datele RSS sunt de mici dimensiuni, ele se încarcã rapid; din acest
motiv, ele pot fi utilizate inclusiv în dispozitive ca telefoane mobile ºi PDA-uri.
În particular, RSS este utilizat de cãtre:
siturile de ºtiri;
siturile unor companii care vând diverse produse;
siturile de tip weblog.
Acronimul RSS se poate referi la:
Rich Site Summary (RSS 0.9x). RSS 0.90 nu se mai utilizeazã. RSS 0.91 permite o
flexibilitate scãzutã. Versiunile 0.92, 0.93 ºi 0.94 permit o flexibilitate ridicatã. Este
indicat ca în locul versiunilor 0.91, 0.92, 0.93 ºi 0.94 sã se utilizeze RSS 2.0);
RDF Site Summary (RSS 1.0). RSS 1.0, bazat pe RDF (Resource Description
Framework), este extensibil prin module, fiind utilizat pentru aplicaþiile bazate pe
RDF sau care necesitã module RDF specifice;
Really Simple Syndication (RSS 2.0). RSS 2.0 este utilizat în scopuri generale, pentru
punerea la dispoziþie a informaþiilor actualizabile.
PHP ªI RSS 253
RSS în browser
Popularitatea crescândã a formatului RSS a determinat producãtorii de browsere sã
implementeze posibilitatea de a vizualiza acest tip de ºtiri. În Internet Explorer, acest
lucru este posibil numai dupã adãugarea unor plugin-uri (ca, de exemplu, Lektora
http://www.clickthenews.com/ , Pluck http://www.pluck.com/ , myFeeds http://
www.myfeeds.com.ar etc.). Browserele Firefox ºi Opera (începând cu versiunea 8.0) au
implementatã aceastã posibilitate.
În browserul Firefox, facilitatea se numeºte Live Bookmarks. Ea poate fi utilizatã în
douã moduri. Primul mod presupune cunoaºterea adresei feed-ului ºi se realizeazã prin
intermediul ferestrei Tools-Manage Bookmarks. Al doilea mod este mai intuitiv. Astfel,
dacã Firefox gãseºte într-o paginã Web un link cãtre un feed RSS, în colþul din dreapta
jos al ferestrei browserului va apãrea un icon. Dacã utilizatorul executã un clic pe acest
icon, feed-ul corespunzãtor va fi adãugat automat în lista bookmarks. În acest browser nu
este prezentat decât titlul ºtirii/articolului, fãrã descrierea sa.
În Opera 8.0, dacã utilizatorul executã un clic pe iconul RSS/XML din pagina Web,
browserul detecteazã prezenþa feed-ului ºi îl adaugã automat în meniul Feeds. Acest
browser prezintã atât titlul, cât ºi descrierea ºtirii/articolului. În acest capitol vom utiliza
browserul Opera 8.0 pentru a vizualiza feed-ul RSS oferit ca exemplu. De asemenea,
vom utiliza browserul Internet Explorer 6.0.
Sintaxa RSS
Un document RSS este de tip XML. De aceea, el trebuie sã fie în acord cu specificaþiile
XML 1.0 (Extensible Markup Language 1.0 Third Edition).
În continuare, ne vom referi la scrierea documentelor RSS 2.0. Puteþi obþine informaþii
complete despre sintaxa RSS 2.0 consultând RSS at Harvard Law (la adresa http://
blogs.law.harvard.edu/tech/rss).
Prima linie a unui document RSS este o declaraþie XML:
<xml version=1.0>
Elementul-rãdãcinã al unui document RSS este rss; el conþine restul documentului.
Acest element trebuie sã aibã precizat atributul version. De asemenea, elementul rss
poate avea precizat atributul encoding (valoarea implicitã a acestuia fiind utf-8):
<rss version=2.0 encoding= iso-8859-1>
Un element rss poate conþine numai un singur element channel (canal):
<channel>
...........
</channel>
Elementul channel trebuie sã aibã urmãtoarele trei subelemente (obligatorii):
title specificã numele canalului;
link precizeazã URI-ul cãtre resursa asociatã. O schemã des utilizatã este http://;
description reprezintã descrierea canalului.
PHP ªI RSS 255
Dupã ce aþi scris un feed RSS, este recomandat sã îl validaþi. Puteþi gãsi validatoare
la adresele http://rss.scripting.com ºi http://feedvalidator.org.
În continuare, puteþi analiza un exemplu în care este prezentat un document RSS 2.0.
Pentru scrierea acestuia au fost utilizate o parte dintre elementele prezentate anterior:
<?xml version=1.0 encoding=iso-8859-1 ?>
<rss version=2.0>
<channel>
<title>ExampleNet Forum - Ultimele postari</title>
<link>http://www.example.net/</link>
<description>Sit cu tutoriale RSS</description>
<generator>powered by RSS ExampleNet</generator>
<item>
<title>Introducere in XML</title>
<link>http://www.example.net/forum/viewtopic.php?t=149</link>
<description>XML, adica Extensible Markup Language ...
</description>
<pubDate>Tue, 12 Jul 2005 11:40:16 -0500</pubDate>
<author>Radu</author>
</item>
<item>
<title>Introducere in RSS 2.0</title>
<link>http://www.example.net/forum/viewtopic.php?t=150</link>
<desscription>RSS 2.0, adica Really Simple Syndication ...
</description>
<pubDate>Tue, 12 Jul 2005 10:56:23 -0500</pubDate>
<author>Daniel</author>
</item>
</channel>
</rss>
În cele ce urmeazã puteþi analiza acelaºi exemplu, scris ca document RSS 1.0. Dupã
cum aþi aflat deja, RSS 1.0 se bazeazã pe RDF. Gãsiþi informaþii complete despre RDF
la http://www.w3.org/RDF, iar despre RSS 1.0, la http://purl.org/rss/1.0/spec sau la
http://web.resource.org/rss/1.0/spec.
<rdf:RDF
xmlns:rdf=http://www.w3.org/1999/02/22-rdf-syntax-ns#
xmlns=http://purl.org/rss/1.0/
xmlns:dc=http://purl.org/dc/elements/1.1/
>
<channel rdf:about=http://www.example.net/articles.rss>
<title>ExampleNet Forum - Ultimele postari</title>
<link>http://www.example.net</link>
<description>Sit cu tutoriale RSS</description>
<generator>powered by RSS ExampleNet</generator>
<items>
<rdf:Seq>
PHP ªI RSS 257
<rdf:li rdf:resource=http://www.example.net/forum/
viewtopic.php?t=149/>
<rdf:li rdf:resource=http://www.example.net/forum/
viewtopic.php?t=150/>
</rdf:Seq>
</items>
</channel>
<item rdf:about=http://www.example.net/forum/
viewtopic.php?t=149>
<title>Introducere in XML</title>
<link>http://www.example.net/forum/viewtopic.php?t=149</link>
<description>XML, adica Extensible Markup Language ...
</description>
<dc:creator>Radu</dc:creator>
<dc:date>2005-05-07</dc:date>
</item>
<item rdf:about=http://www.example.net/forum/
viewtopic.php?t=150>
<title>Introducere in RSS 2.0</title>
<link>http://www.example.net/forum/viewtopic.php?t=150</link>
<description>RSS 2.0, adica Really Simple Syndication ...
</description>
<dc:creator>Daniel</dc:creator>
<dc:date>2005-05-07</dc:date>
</item>
</rdf:RDF>
font-family: tahoma;
font-size: 12px;
color: #1477a0;
text-decoration: none
}
.link:hover {
font-family: tahoma;
font-size: 12px;
color: #495f80;
text-decoration: underline
}
.info{
font-family: tahoma;
text-size: 9;
font-weight: bold;
letter-spacing: 1px;
}
td#cell {
font-family: tahoma;
text-size: 9px;
font-weight: bold;
}
td#cell1 {
background-color: #0072aa;
color: #ffffff;
font-family: tahoma;
text-size: 9;
font-weight: bold;
text-align: left;
}
td#cell2 {
background-color: silver;
color: #000000;
font-family: tahoma;
text-size: 9;
font-weight: bold;
text-align: left;
}
td#article {
font-family: tahoma;
text-size: 9;
font-weight: normal;
}
260 PROGRAMAREA ÎN PHP
Dacã în browserul Internet Explorer executaþi clic pe iconul RSS, veþi putea vizualiza
documentul RSS generat prin execuþia fiºierului rss.php:
Dupã ce adãugaþi feed-ul RSS creat anterior (având titlul XHTML, PHP ºi MySQL
ultimele articole) în Opera, introduceþi încã o înregistrare în tabelul articles (a cincea).
264 PROGRAMAREA ÎN PHP
Dacã doriþi sã vizualizaþi unul dintre ultimele trei articole, faceþi clic pe legãtura
corespunzãtoare acestuia în fereastra care afiºeazã pagina generatã prin execuþia scriptului
articles3.php sau în pagina care afiºeazã conþinutul canalului RSS. Obþineþi rezultatul
urmãtor (în browserul Opera 8.0):
Dupã cum puteþi constata, fiºierul rss.php genereazã un document RSS fãrã a-l scrie
pe discul serverului. Dacã doriþi, aveþi posibilitatea de a genera fiºiere RSS pe disc, în
condiþiile în care existã drepturile corespunzãtoare. Scriptul inclus în fiºierul rss2.php
(listing-ul 9.5) va genera un fiºier RSS (articles.rss) în directorul curent:
PHP ªI RSS 265
Dacã preferaþi generarea unui fiºier RSS, trebuie ca scriptul care îl genereazã (în
exemplul nostru, el este inclus în fiºierul rss2.php) sã fie executat în mod regulat. În
acest scop, trebuie sã utilizaþi daemonul cron (citiþi capitolul urmãtor). Legãtura cãtre
fiºierul RSS trebuie precizatã astfel:
<a type=application/rss+xml href=feed.rss>RSS feed</a>
Generarea unui document RSS fãrã scrierea sa într-un fiºier este o metodã mai
eficientã de utilizare a formatului, deoarece, în acest caz, actualizarea canalului
RSS corespunzãtor se face numai în funcþie de intervalul ales de utilizator în
browser (de exemplu, în Opera 8.0, acest interval poate fi ales în Feeds Manage Feeds,
prin editarea canalului dorit) sau în feed aggregator-ul local. De asemenea, într-un feed
aggregator local poate fi precizat de cãtre utilizator ºi numãrul de itemi care se doreºte
sã fie reþinuþi de cãtre acesta.
global $currentTag;
global $title, $link, $description;
$titleKey = *RSS*CHANNEL*TITLE;
$linkKey = *RSS*CHANNEL*LINK;
$descKey = *RSS*CHANNEL*DESCRIPTION;
if ($currentTag == $titleKey) {
$title = $data;
}
elseif ($currentTag == $linkKey) {
$link = $data;
}
elseif ($currentTag == $descKey) {
$description = $data;
}
Procesor RSS
în PHP (1) cu SAX
Puteþi adãuga ºi alte elemente de stil CSS pentru a obþine un aspect cât mai plãcut.
Este necesar ca directorul în care se aflã fiºierul rss_parser.php sã conþinã ºi fiºierul
lens.gif. În caz contrar, eliminaþi elementul img din scriptul prezentat în listing-ul
anterior.
Adãugaþi încã o înregistrare (a ºasea) în tabelul articles. Dacã executaþi încã o datã
fiºierul rss_parser.php, veþi obþine un rezultat similar cu:
XHTML, PHP ºi MySQL ultimele articole: procesor RSS cu SAX (2)
Procesor RSS
în PHP (2) cu SAX
Procesor RSS
cu PEAR XML_RSS
html_entity_decode($item[description]).
</td></tr><tr><td style=font-family: tahoma;
font-size: 12px;>
(<a href=.html_entity_decode($item[link])).
>citeste ...</a>)
</td></tr></table>;
}
}
?>
Procesor RSS
cu miniXML
PHP ªI RSS 273
Puteþi utiliza URL-ul unui feed RSS extern ca argument al metodei fromString().
De exemplu, dacã URL-ul este http://www.webservices.org/index.php/ws/rss/feed/
latestlinks, trebuie sã introduceþi în script:
$xmlDoc->fromString(file_get_contents(
http://www.webservices.org/index.php/ws/rss/feed/latestlinks));
CAPITOLUL 10
PHP ºi cron
Nu-i de ajuns sã faci binele, mai trebuie sã-l faci ºi bine!
Diderot
Cele mai multe scripturi PHP sunt executate în momentul în care un utilizator
acceseazã pagina Web corespunzãtoare. Aceste vizite nu pot fi planificate
În calitate de
administrator Web, probabil cã aþi simþit necesitatea execuþiei anumitor scripturi PHP în
mod regulat, independent de existenþa unor vizitatori. Scopurile pentru care este necesarã
execuþia regulatã a unor scripturi pot fi: actualizarea sitului, eliminarea unor fiºiere a
cãror prezenþã nu mai este necesarã, trimiterea unor mesaje de e-mail în mod regulat
(e.g., newsletter), efectuarea operaþiei de backup ºi altele.
O soluþie ar fi sã iniþiaþi chiar dumneavoastrã execuþia scripturilor respective, prin
intermediul browserului (cu condiþia ca aceste scripturi sã fie conþinute în directoarele ºi
subdirectoarele sitului). Este clar cã aceastã soluþie nu este eficientã, fiind, de multe ori,
hilarã
În cele ce urmeazã vã vom prezenta o soluþie mult mai elegantã.
Sistemele UNIX/Linux pun la dispoziþia utilizatorilor un instrument deosebit de util
de planificare în timp a proceselor rulate, reprezentat de daemonul cron (numit crond).
În sistemele Windows, echivalentul acestuia se numeºte Scheduled Tasks.
Fiºiere crontab
Daemonul cron foloseºte fiºiere de control numite tabele cron sau fiºiere crontab.
Utilizatorul root îºi poate organiza sarcinile administrative în douã grupe:
sarcini administrative obiºnuite, care trebuie executate la intervale regulate de timp;
sarcini specializate, care trebuie executate la un anumit moment de timp.
Sarcinile specializate pot fi executate ca intrãri în fiºierul /etc/crontab (la care are
acces numai utilizatorul root). Sarcinile specializate sunt organizate în directoare cron
speciale. Aceste directoare sunt:
/etc/cron.hourly (pentru sarcini executate în fiecare orã);
/etc/cron.daily (pentru sarcini executate în fiecare zi);
PHP ªI CRON 275
Comanda crontab
Nu trebuie sã editaþi fiºierele crontab direct. În acest scop, folosiþi comanda crontab .
Daemonul cron examineazã fiºierele crontab ale utilizatorilor numai la iniþializare.
Atunci când faceþi schimbãri în fiºierul crontab personal folosind comanda crontab, un
mesaj care indicã schimbarea este trimis daemonului cron.
Pentru mãrirea securitãþii, poate fi interzisã folosirea comenzii crontab unor
utilizatori sau tuturor utilizatorilor (cu excepþia utilizatorului root). În acest scop, sunt
folosite fiºierele cron.deny ºi cron.allow:
/etc/cron.allow
/etc/cron.deny
Utilizatorul root poate crea, edita sau ºterge aceste fiºiere. Intrãrile în cele douã
fiºiere sunt numele de utilizator (user name), câte unul pe o linie. În exemplul urmãtor
puteþi analiza un posibil conþinut al fiºierului cron.allow:
root
traian
irina
george
Cele douã fiºiere sunt examinate atunci când este invocatã comanda crontab. Dacã
numele utilizatorului se aflã în fiºierul cron.allow, comanda crontab poate fi executatã
de cãtre acesta. Dacã fiºierul cron.allow existã, numele utilizatorului root trebuie sã
aparã în acest fiºier.
Dacã administratorul de sistem doreºte sã interzicã în mod explicit unui utilizator sã
utilizeze comanda crontab, o poate face introducând numele de utilizator al acestuia în
fiºierul cron.deny.
276 PROGRAMAREA ÎN PHP
Intrãri cron
Fiºierul crontab conþine câte o intrare pentru fiecare sarcinã cron. Intrãrile sunt separate
prin caracterul newline. Fiecare intrare conþine ºase câmpuri obligatorii, separate prin
spaþii sau prin caracterul tab, în ordinea urmãtoare:
minute hour day_of_month month weekday job
PHP ªI CRON 277
PATH desemneazã lista directoarelor (elementele listei sunt separate prin caracterul : )
în care pot fi gãsite programele ºi scripturile. HOME este directorul-rãdãcinã pentru
comandã/script. MAILTO aratã cui vor fi trimise rezultatele execuþiei sarcinilor. Implicit,
acestea sunt trimise proprietarului fiºierului crontab, dar pot fi specificaþi ºi alþi desti-
natari, ca, de exemplu, adresa de e-mail a administratorului de sistem. Iatã un exemplu
în care sunt definite cele patru variabile de mediu:
SHELL=/bin/bash
PATH=/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
Utilizând variabila de mediu HOME, intrarea din exemplul anterior se mai poate scrie
astfel:
0 * * * * php -q $HOME/cron.php >>$HOME/cron.log 2>>$HOME/cron.err
Trebuie reþinut cã, utilizând comanda crontab, nu pot fi trimise sistemului sarcini
în mod individual. Cu alte cuvinte, trebuie trimise toate sarcinile în acelaºi timp (în
cursul unei singure operaþii de editare).
Dacã doriþi sã evitaþi pierderea intrãrilor existente într-un fiºier crontab, pentru a
adãuga o sarcinã nouã trebuie sã procedaþi astfel (se presupune cã utilizatorul curent este
traian):
folosind comanda crontab, listaþi fiºierul crontab în care doriþi sã adãugaþi o nouã
sarcinã, folosind opþiunea -l, ºi redirectaþi acest fiºier în altul, pe care-l puteþi edita
ulterior:
#crontab -l >/tmp/crontab.traian
Dacã pachetul wget este instalat, veþi obþine o serie de informaþii legate de acesta.
Wget trebuie invocat împreunã cu URL-ul scriptului care trebuie executat, ca în exemplul
urmãtor:
0 4 * * 1,3,5 wget http://www.example.com/message.php
Dacã situl dumneavoastrã este gãzduit pe un server Linux, probabil cã aveþi la dispoziþie
o aplicaþie cu interfaþã graficã pentru gestionarea lui, care, în multe cazuri poate fi cPanel.
Prin intermediul acestuia puteþi edita fiºierul crontab personal (figura 10.1):
Faceþi clic pe pictograma Cron jobs. Puteþi opta pentru unul dintre cele douã niveluri:
Standard ºi Advanced (Unix Style).
Puteþi gestiona
fiºierul crontab
personal.
Dacã optaþi pentru nivelul Standard, pentru gestionarea intrãrilor din fiºierul crontab
vã este pus la dispoziþie un formular ca în figura 10.2.
Dacã optaþi pentru nivelul Advanced (Unix Style), pentru gestionarea intrãrilor din
fiºierul crontab vã este pus la dispoziþie un formular ca în figura 10.3.
Nivelul Advanced
ANEXA A
Managerul de pachete poate utiliza mai multe interfeþe cu utilizatorul, numite frontends.
Interfaþa implicitã, instalatã prin execuþia go-pear.bat, este interfaþa în linie de comandã
(CLI Command Line Interface), a cãrei utilizare o vom prezenta în secþiunea urmãtoare.
De asemenea, pot fi instalate ºi douã interfeþe grafice: una bazatã pe browser ºi alta pe
GTK+.
În secþiunea Instalarea automatã a pachetelor PEAR prin FTP a acestei anexe vom
prezenta instalarea ºi utilizarea interfeþei bazate pe browser.
GTK+ (GIMP Tool Kit) a fost dezvoltat iniþial pentru GIMP (GNU Image Manipulation
Program). Astãzi, este utilizat ca parte centralã a proiectului GNOME. GTK+ a fost
portat ºi pe sistemul Windows.
INSTALAREA PACHETELOR PEAR 283
Pentru a putea instala interfaþa PEAR GTK este necesar ca, în prealabil, sã instalaþi
extensia PHP-GTK (http://gtk.php.php), bazatã pe GTK+, care permite scrierea apli-
caþiilor GUI (Graphical User Interface) independente de platformã. Ulterior, instalaþi
pachetul PEAR_Frontend_Gtk, care conþine interfaþa PEAR GTK. Aceastã interfaþã
poate fi pornitã prin comanda pear -G.
În urma execuþiei comenzii, veþi obþine o listã ordonatã alfabetic, care conþine numele
pachetelor PEAR ºi versiunile acestora.
De exemplu, pentru a instala pachetul XML_RSS, utilizat pentru procesarea feed-urilor
RSS, folosiþi comanda:
pear install -o XML_RSS
Puteþi remarca (figura A.1) faptul cã, pentru satisfacerea dependenþelor PEAR,
instalarea pachetului XML_RSS necesitã instalarea prealabilã a pachetului
XML_Tree, care este realizatã automat utilizând comanda anterioarã.
Instalarea pachetelor
PEAR de pe
http://pear.php.net,
folosind pear
unde file.tgz este arhiva care conþine pachetul ce trebuie instalat. De exemplu, pentru
instalarea pachetului XML_SVG, utilizat pentru generarea documentelor SVG (Scalable
Vector Graphics), trebuie sã aveþi în directorul PHP arhiva cu numele
XML_SVG-1[1].0.0.1.tgz (care conþine, la data scrierii acestei lucrãri, cea mai recentã
versiune stabilã a pachetului) ºi sã folosiþi comanda:
pear install XML_SVG-1[1].0.0.1.tgz
Dacã nu existã o versiune stabilã a pachetului pe care doriþi sã-l instalaþi, veþi obþine
un mesaj de eroare. Pentru a realiza instalarea pachetului, aveþi la îndemânã douã
metode:
1. Setaþi directiva preferred_state la valorile alpha sau beta ºi apoi efectuaþi instalarea:
pear config-set preferred_state alpha|beta
pear install package
ca exemplul urmãtor:
pear config-set preferred_state beta
pear install OLE
sau
pear install package-version_number
ca în exemplul urmãtor:
pear install Spreadsheet_Excel_Writer-beta
sau
pear install Spreadsheet_Excel_Writer-0.8
Crearea unui
utilizator pentru
accesul restrictiv
la PEAR Installer
ini_set(include_path, /home/user/includes/
. PATH_SEPARATOR . ini_get(include_path));
Astfel, începutul fiecãrui script PHP care utilizeazã pachetul XML_RSS trebuie sã
conþinã secvenþa de cod urmãtoare:
<?php
ini_set(include_path, /home/user/public_html/includes/
. PATH_SEPARATOR . ini_get(include_path));
include_once(RSS/RSS.php);
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
?>
288 ANEXA A
ANEXA B
Aspecte generale
Securitatea informaticã se defineºte ca fiind protecþia datelor într-un sistem informatic,
implicând comunicarea, stocarea ºi procesarea acestora.
Asigurarea securitãþii unui sistem informatic se referã la urmãtoarele aspecte:
integritatea (asigurã persoanele care utilizeazã sistemul cã datele sunt corecte);
confidenþialitatea (informaþiile sunt folosite numai de cãtre persoanele autorizate);
valabilitatea (datele stocate sunt disponibile oricând este nevoie de ele).
Securitatea unui sistem informatic trebuie sã fie o preocupare constantã pentru
programatori (ºi, evident, nu numai pentru ei), în fiecare etapã a dezvoltãrii
sistemului. Pe de altã parte, posibilitatea de utilizare a unui sistem se poate
reduce odatã cu creºterea securitãþii acestuia. Din acest motiv, trebuie întotdeauna sã se
asigure un echilibru între securitate ºi posibilitatea de utilizare a unui sistem.
Atunci când se vorbeºte despre securitatea aplicaþiilor Web, se utilizeazã termeni ca risks
(riscuri), threats (ameninþãri) ºi vulnerabilities (vulnerabilitãþi). Riscul reprezintã posibilitatea
de a suferi pagube sau pierderi. Ameninþarea este o expresie a intenþiei de a produce pagube,
rãu sau de a pedepsi. Vulnerabilitatea înseamnã un gol de securitate al aplicaþiei, adicã o
slãbiciune a acesteia, care permite utilizatorilor neautorizaþi sã aibã acces asupra sa. Vulne-
rabilitãþile unei aplicaþii pot avea drept cauzã un design prost al acesteia, o implementare
greºitã, dar ºi o administrare incorectã. O ameninþare poate exploata o vulnerabilitate.
PHP este un limbaj de programare ºi un interpretor utilizat într-un server Web ca modul
sau executat separat prin intermediul CGI (Common Gateway Interface). Din acest motiv,
scripturile PHP pot sã acceseze sistemul de fiºiere al serverului, sã deschidã conexiuni cu
alte servere, sã execute comenzi, sã interacþioneze cu bazele de date etc. Posibilitãþile PHP
enumerate ridicã problema securitãþii aplicaþiilor Web scrise în limbajul PHP.
290 ANEXA B
Dacã register_globals are valoarea On, pagina anterioarã poate fi accesatã utilizând
?authorized=1 în ºirul de interogare. Astfel, un utilizator neautorizat poate avea acces
în sistem. Dacã register_globals are valoarea Off , datele obiºnuite (aºa cum este
$authorized ) utilizate în script nu sunt afectate de datele trimise de utilizator.
Desigur cã, dacã scriptul anterior include la începutul lui linia $authorized=false;
(adicã se realizeazã iniþializarea variabilei), atunci utilizatorul neautorizat nu mai poate
pãcãli scriptul, chiar dacã directiva register_globals are valoarea On.
Setarea la valoarea Off a directivei implicã preluarea în script a datelor (trimise de
utilizator) prin intermediul tablourilor globale $_GET, $_POST, $_COOKIE, $_SESSION
ºi $_SERVER. În felul acesta, puteþi sã controlaþi sursa datelor care intrã în sistem.
În concluzie: setaþi la valoarea Off directiva register_globals (dacã aveþi acces la
fiºierul de configurare php.ini) ºi în mod obligatoriu iniþializaþi la începutul scriptului
variabilele utilizate!
Filtrarea datelor
Prin filtrare se înþelege implementarea a unui mecanism prin care se valideazã datele
introduse în aplicaþie. Mecanismul de filtrare a datelor trebuie astfel realizat încât sã:
nu poatã fi ocolit (voluntar sau involuntar);
nu permitã ca date invalide sã treacã drept date valide ºi invers.
Filtrarea datelor presupune:
determinarea surselor datelor prin utilizarea tablourilor globale $_GET, $_POST,
$_COOKIE, $_SESSION ºi $_SERVER;
validarea datelor: aceasta înseamnã cã, înainte de a fi utilizate, trebuie sã se verifice
dacã datele respectã un anumit format (de exemplu, cel al unei adrese de e-mail) sau
dacã nu includ anumite caractere interzise;
utilizarea unei convenþii de nume stricte pentru date;
utilizarea unei abordãri de tip listã albã (white list) pentru datele validate (aplicaþia
va utiliza numai datele conþinute în aceastã listã, iar datele vor fi incluse în listã
numai dupã filtrarea lor).
Iatã un exemplu în care se realizeazã filtrarea datelor:
<?php
$clean = array();
if(isset(($_POST[color])) {
$color == $_POST[color]
if($color == red || $color == blue || $color == green))
$clean[color] = $_POST[color];
}
?>
292 ANEXA B
Funcþia transformã toate caracterele din ºirul string care au entitãþi HTML echi-
valente în aceste entitãþi ºi întoarce ºirul astfel obþinut ( & devine &, devine ",
devine , < devine <, > devine >, (c) devine ©, (r) devine ®
etc.). Argumentul opþional quote_style precizeazã ce se va întâmpla cu ghilimelele
simple ºi duble (sunt posibile trei valori: ENT_COMPAT converteºte ghilimelele duble ,
ENT_QUOTES converteºte ambele tipuri de ghilimele ºi ENT_NOQUOTES nu converteºte
ghilimelele, prima fiind valoarea implicitã). Argumentul opþional charset precizeazã
setul de caractere utilizat în conversie (setul de caractere utilizat implicit este
ISO-8859-1). Iatã un exemplu de utilizare a acestei funcþii:
<?php
$html = array();
$html[user] = htmlentities($clean[user],ENT_QUOTES,UTF-8);
echo <p>Bine ai venit {$html[user]}.</p>;
?>
string strip_tags(string string [, string allowable_tags])
Funcþia întoarce ºirul string având eliminate tag-urile XHTML ºi PHP. Parametrul
opþional allowable_tags precizeazã tag-urile care nu vor fi eliminate din ºir. Este bine
sã se elimine <script>, <iframe>, <img>, marcajele pentru formatarea textului etc.).
string mysql_escape_string ( string unescaped_string)
Funcþia întoarce ºirul transmis ca argument dupã ce asupra acestuia este aplicat
mecanismul de escaping asupra ghilimelelor. ªirul întors poate fi utilizat, astfel, de
funcþia mysql_query() pentru a trimite interogãri MySQL, ca în exemplul urmãtor:
<?php
$mysql = array();
$mysql[user] = mysql_real_escape_string($clean[user]);
$sql = SELECT * FROM users WHERE
username = {$mysql[user]};
$result = mysql_query($sql);
?>
SECURITATEA APLICAÞIILOR WEB 293
Pericolul reprezentat de atacurile XSS devine real atunci când datele stocate în baza
de date a aplicaþiei ºi în fiºierele utilizate de aceasta sau care vin dintr-o sursã externã
sunt afiºate într-o paginã Web de cãtre alþi utilizatori deoarece browserele acestora
executã codul introdus de atacator.
În continuare, este descris scenariul dupã care se desfãºoarã un atac XSS:
atacatorul viziteazã un sit vulnerabil;
atacatorul posteazã pe situl respectiv cod JavaScript (în mod obiºnuit, codul postat
trimite informaþii la un alt sit denumit cross-site , care poate fi chiar situl
atacatorului);
un utilizator oarecare viziteazã situl vulnerabil;
acesta încarcã o paginã ce conþine codul introdus de utilizator, care va fi executat. În
urma execuþiei codului JavaScript, se trimit la situl atacatorului informaþii importante
referitoare la utilizatorul respectiv.
Exemplu
Scenariile dupã care se desfãºoarã atacurile XSS sunt limitate numai de imaginaþia
atacatorului. În continuare, vom prezenta un exemplu în care un atacator intrã în posesia
cookie-urilor unui utilizator ºi apoi le utilizeazã pentru a folosi contul acestuia. În acest
exemplu, atacatorul introduce cod JavaScript într-o bazã de date prin intermediul unei
aplicaþii PHP. Salvaþi în fiºierul xss.php scriptul prezentat ºi executaþi-l (http://127.0.0.1/
securitate/xss.php). Sã presupunem cã o aplicaþie colecteazã datele utilizatorilor (nume
de utilizator ºi parolã) în tabelul MySQL users:
<?php
if (!get_magic_quotes_gpc()) {
$_POST[username] = mysql_real_escape_string
($_POST[username]);
$_POST[email] = mysql_real_escape_string ($_POST[email]);
}
$sql = INSERT INTO users(username, email)
VALUES({$_POST[username]}, {$_POST[email]});
$result = mysql_query($sql);
?>
Funcþia get_magic_quotes_gpc() întoarce configuraþia curentã a directivei
magic_quotes_gpc din fiºierul php.ini (0 pentru Off ºi 1 pentru On). Dacã directiva are
valoarea On, nu mai este necesarã utilizarea funcþiei de escaping. Dupã cum se poate
constata, înainte de stocarea datelor, este utilizatã funcþia de escaping mysql_real_
escape_string(), ceea ce este corect!
Presupunem cã tabelul users este accesibil administratorului prin intermediul urmã-
torului script (care nu face altceva decît sã afiºeze datele incluse în tabelul respectiv):
. . . . . . . . . . . . . . .
<table border=1 cellpadding=2>
<tr>
<th>Username</th>
296 ANEXA B
<th>Email</th>
</tr>
<?php
$sql = SELECT username, email FROM users;
$result = mysql_query($sql);
while ($current_user = mysql_fetch_array($result)) {
echo <tr>
<td>{$current_user[username]}</td>
<td>{$current_user[email]}</td>
</tr>;
}
?>
</table>
. . . . . . . . . . . . . . .
Dacã un utilizator introduce drept nume de utilizator (user name) o secvenþã JavaScript
similarã cu:
<script>alert(Te-am pacalit!)</script>
În exemplul prezentat, sistemul atacat este chiar serverul pe care atacatorul a stocat
codul JavaScript (sistemul vulnerabil).
SECURITATEA APLICAÞIILOR WEB 297
Prevenire
Dupã cum se constatã ºi din exemplul anterior, pentru evitarea producerii atacurilor XSS
se impun urmãtoarele reguli de bazã:
filtrarea datelor care vin din surse externe (asiguraþi-vã cã datele care vin din aceste
surse corespund cu datele aºteptate);
utilizarea valorii Off a directivei register_globals;
folosirea unei convenþii de nume stricte pentru date;
utilizarea unei abordãri de tip listã albã (white list) pentru date;
instalarea unui firewall pe server (care inspecteazã datele sosite ºi le respinge dacã
includ cod XHTML sau JavaScript);
utilizarea mecanismului de escaping la afiºarea datelor.
Exemplu
Pentru a înþelege cum se realizeazã atacurile CSRF, vã prezentãm un exemplu. Fie un
forum fictiv (www.forum.org), care foloseºte un formular pentru posturi:
<table><form action=/add_post.php>
<tr>
<td>Subject</td>
<td><input type=text name=subject />
</td>
</tr>
<tr>
<td>Message</td>
<td><textarea name=message></textarea></td>
</tr>
<tr>
<td><input type=submit value=Adauga mesaj /></td>
</tr>
</form></table>
Deoarece nu este specificatã metoda prin care datele sunt transmise serverului, va fi
utilizatã metoda implicitã GET, iar datele incluse de utilizator în formular vor fi adãugate
URL-ului resursei cerute, add_post.php. Antetul HTTP al cererii va fi similar cu:
GET /add_post.php?subject=ceva&message=ura HTTP/1.1
Host: wwww.forum.org
Cookie: PHPSESSID=8d89d934175be7411a25ff0bbb1fab5b
Am presupus cã identificatorul de sesiune este stocat într-un cookie. Dacã cineva vrea
sã falsifice mesajele pe acest forum, tot ceea ce trebuie sã facã este sã copieze formatul
acestei cereri ºi sã o trimitã la victimã (prin intermediul unei pagini). Fie urmãtorul
element:
<img src=http://www.forum.org/add_post.php?
subject=bad_subject&message=bad_message />
Când browserul cere aceastã imagine, cererea HTTP va avea acelaºi format cu
cererea anterioarã, inclusiv cookie-ul utilizat pentru managementul sesiunii. Cu o simplã
schimbare în acest URL, un atacator poate modifica subiectul ºi conþinutul mesajului,
trimiþând orice doreºte.
Prevenire
Pentru prevenirea atacurilor CSRF trebuie respectate urmãtoarele reguli:
utilizarea în formulare a metodei POST;
folosirea unui semn (token) aleatoriu pentru a confirma cã utilizatorul foloseºte
formularul din care se presupune cã trimite datele;
utilizarea valorii Off a directivei register_globals.
SECURITATEA APLICAÞIILOR WEB 299
SQL Injection
Definiþie. Explicaþii
SQL Injection este o tehnicã prin care un atacator creeazã sau schimbã comenzi SQL
existente, prin utilizarea unei aplicaþii (sit Web), folosind caractere speciale, în scopul de
a obþine acces neautorizat (prin ocolirea mecanismului de autentificare) la date stocate în
baze de date ºi de a realiza citirea, adãugarea ºi modificarea acestora, precum ºi crearea
unor noi conturi de utilizator.
Realizarea unor atacuri de acest tip este de multe ori consecinþa unei insuficiente
filtrãri a datelor. Atacurile SQL Injection pot fi blocate printr-o filtrare corectã.
Rezultã cã acest tip de atac este posibil din cauza vulnerabilitãþii aplicaþiilor ºi nu a
unor probleme ale serverelor de baze de date sau ale serverelor Web.
Efectele atacurilor SQL Injection depind de codul utilizat de atacator, de importanþa
datelor stocate în baza de date, de gradul de filtrare a datelor, dar ºi de posibilitatea ca
serverul de baze de date sã permitã interogãri multiple.
Este posibil ca utilizatorul sã trimitã simultan interogãri multiple, dacã le separã prin
punct ºi virgulã. Ultimele versiuni de MySQL permit trimiterea de interogãri multiple,
dar extensia PHP ext/msqli obligã la folosirea unei anumite funcþii pentru trimiterea lor
(mysqli_multi_query() în loc de mysqli_query()). Este indicat sã permiteþi
folosirea unei singure interogãri la un moment dat.
Exemplu
În continuare vom prezenta un exemplu de atac SQL Injection. Sã presupunem cã
interogarea utilizatã pentru a realiza operaþia de login este similarã cu:
SELECT * FROM users
WHERE username = mihai
AND password = 123456789
În scriptul PHP, variabila $query care va fi pasatã ca argument funcþiei mysql_query()
se va scrie astfel:
$query = SELECT * FROM users WHERE username = .$form_user.
AND password = .$form_password.;
unde $form_user ºi $form_password sunt datele introduse de cãtre utilizator în
formularul de login.
Dacã utilizatorul introduce în formular:
ca nume de utilizator (care se va regãsi în $query ca valoare a variabilei $form_user):
OR 1=1 --
ca parolã (care se va regãsi ca valoare a variabilei $form_passw) orice doreºte:
anything
variabila $query va arãta astfel:
$query = SELECT * FROM users WHERE username= OR 1=1 --
AND password=anything;
300 ANEXA B
Prevenire
În general, pentru a realiza protecþia aplicaþiilor împotriva atacurilor SQL Injection, este
indicat:
sã se filtreze toate datele;
sã se punã toate datele între ghilimele simple la introducerea lor în baza de date;
sã se foloseascã o funcþie pentru escaping, de exemplu, mysql_escape_string(),
mysql_real_escape_string() sau addslashes();
sã se utilizeze valoarea Off a directivei magic_quotes_gpc.
Aplicaþia Web care utilizeazã baza de date trebuie sã aibã ca drepturi de acces la
bazele de date numai pe acelea care sunt necesare pentru buna ei funcþionare. De
exemplu, dacã modul de funcþionare a aplicaþiei nu implicã modificarea informaþiilor
stocate în baza de date utilizatã de aceºtia, atunci permisiunea UPDATE nu este necesarã!
Afiºarea erorilor
Fiºierul de configurare php.ini include câteva directive care se referã la modul în care
sunt afiºate ºi stocate erorile generate în cursul execuþiei scripturilor PHP. Dintre aceste
erori amintim:
error_reporting
Aceastã directivã permite stabilirea nivelului de raportare a erorilor. Sunt posibile
mai multe valori ale acestei directive (11 în PHP 4 ºi 13 în PHP 5), cât ºi combinaþii ale
acestora. Este preferabil ca, atât în cursul dezvoltãrii aplicaþiei, cât ºi în cel al exploatãrii
acesteia, sã setaþi aceastã directivã la valoarea E_ALL. În acest fel, vor fi raportate toate
erorile generate.
display_errors
Aceastã directivã determinã afiºarea (valoarea On) sau neafiºarea (valoarea Off) pe
ecran a erorilor. Este indicat ca, în cursul dezvoltãrii aplicaþiei, sã setaþi aceastã directivã
la valoarea On, iar în cursul exploatãrii, la valoarea Off (mesajele de eroare sunt extrem
de utile pentru un atacator ºi, din acest motiv, nu trebuie afiºate).
SECURITATEA APLICAÞIILOR WEB 301
log_errors
Aceastã directivã determinã dacã erorile sunt scrise (valoarea On) sau nu (valoarea
Off) într-un jurnal de erori. Este recomandat sã utilizaþi permanent valoarea On (în
cursul dezvoltãrii, dar ºi al exploatãrii aplicaþiei).
error_log
Aceastã directivã precizeazã fiºierul în care sunt scrise erorile. Este necesar ca PHP
sã aibã drept de scriere în fiºierul repectiv.
Dacã nu aveþi acces la php.ini, puteþi seta directivele enumerate anterior în interiorul
scriptului, folosind funcþia PHP ini_set(). De asemenea, nivelul de raportare a
erorilor poate fi setat cu funcþia dedicatã error_reporting():
int error_reporting([int level])
Nivelul de raportare level poate fi o constantã sau o mascã de biþi. Este încurajatã
utilizarea constantelor (pentru compatibilitatea cu versiunile anterioare). Funcþia seteazã
directiva error_reporting la valoarea level ºi întoarce valoarea anterioarã. În exemplul
urmãtor, directiva este setatã la valoarea E_ALL:
error_reporting(E_ALL);
header() 61 G tranzacþii 20
html_entity_decode()
272 GNOME, mediu desktop 114 N
htmlentities() 292 GoogleSearchService 198
newsletter 89
is_uploaded_file() 47 Graphical User Interface 15
NuSOAP, bibliotecã 187
is_writable() 47
H
mail() 79, 80 O
mb_convert_encoding() HTML
139 ºablon XSLT 116
limbaj de prezentare 99
md5() 59 octal, sistem de numeraþie 35
limitãri 99
mkdir() 35 ODBC
HTTP, protocol 44, 52
move_uploaded_file() caracteristici 19
47 I driver ODBC 19
myql_escape_string 292 identificator de conexiune 21
mysqli_multi_query() IANA 78 punte JDBC-ODBC 110
identificator de fiºier 37 sursã de date 19, 21
299
iframe 233 OpenOffice Calc 27
mysqli_query() 299
odbc_close() 21 IMAP4 15
Instant Saxon, procesor XSLT P
odbc_connect() 21
odbc_exec() 21 120 pachete PEAR
odbc_fetch_row() 21 intrare cron 276 DB 24, 25
odbc_result() 21 HTTP_Request 222
J
opendir() 36 manager de pachete 281
readdir() 36 JavaScript 294 PEAR Installer 285
rmdir() 35 JAXM 157 SOAP 187
session_destroy() 55 Spreadsheet_Excel_Writer 29
session_id() 55 L UDDI 226
session_save_path() 53 XML_Parser 143
LDAP, protocol 15
session_start() 54 XML_RPC 179
libxml, bibliotecã 114
session_unset() 55 XML_RSS 269
limbaje de programare Web
simplexml_load_{file| XML_Serializer 139
limbaje interpretate 13
string} 141 XML_SVG 284
pentru client 13
sort() 41 XML_Transformer 148
pentru server 13
strip_tags() 54, 292 PDA 100
ASP 14
stristr() 92 PECL 142
avantaje 13
strrpos() 50 PERL CPAN, proiect 281
JSP 14
tempnam() 42 PHP, server de aplicaþii
Perl 14
tmpfile() 41 avantaje 15
PHP 14
unlink() 42 domenii de utilizare 14
xml_error_string() 124 M interfeþe 14
xml_get_current_line_ interfeþe pentru SGBD-uri 15
number() 124 magazin virtual 61 istoric 14
xml_get_error_code() Microsoft UBR Node 226 modalitãþi de utilizare 15
124 Mozilla Thunderbird, client de modificarea drepturilor de
xml_parse 123 e-mail 84 acces 34
xml_parser_create() 123 MySQL suport pentru procesare XML
xml_parser_free() 124 caracteristici 19 115
xml_parser_set_option() driver ODBC 20 suport pentru protocoale
124 export de date în Excel 27 diverse 15
xml_set_character_data_ interfeþe de acces 20 PHP-GTK 15
handler() 124 phpMyAdmin 20 planificarea execuþiei scripturilor
xml_set_element_handler() proceduri stocate, vederi 20 278
123 tabele InnoDB 20 POSIX 15
xml_set_object() 124 tabele MyISAM 20 programare distribuitã 149
INDEX 305
Bibliografie generalã
Referinþe Web
1. ***, URIs, URLs and URNs: Clarifications and Recommendations 1.0, Report from
the joint W3C/IETF URI Planning Interest Group, notã a W3C din 21 septembrie
2001, http://www.w3.org/TR/2001/NOTE-uri-clarification-20010921.
2. ***, Extensible Markup Language (XML) 1.0 (Third Edition), recomandare W3C din
4 februarie 2004, http://www.w3.org/TR/2004/REC-xml-20040204.
3. ***, Namespaces in XML, World Wide Web Consortium, 14 ianuarie 1999, http://
www.w3.org/TR/1999/REC-xml-names-19990114.
4. ***, XHTML 1.0 The Extensible Hipertext Markup Language (Second Edition), A
Reformulation of HTML 4.0 in XML 1.0, recomandare W3C din 26 ianuarie 2000,
revizuit la 1 august 2002, http://www.w3.org/TR/2000/REC-xhtml1-20020801.
5. ***, XHTML Basic, recomandare W3C din 19 decembrie 2000, http://www.w3.org/
TR/2000/REC-xhtml-basic-20001219.
6. ***, XHTML Media Types, notã W3C din 1 august 2002, http://www.w3.org/ TR/
2002/Note-xhtml-media-types-20020801.
7. ***, XML Schema Part 1: Structures, recomandare W3C din 2 mai 2001, http://
www.w3.org/TR/2001/REC-xmlschema-1-20010502.
8. ***, XML Schema Part 2: Datatypes, recomandare W3C din 2 din mai 2001, http://
www.w3.org/TR/2001/REC-xmlschema-2-20010502.
BIBLIOGRAFIE GENERALÃ 309
9. ***, XSL Transformations (XSLT) Version 1.0, recomandare W3C din 16 noiembrie
1999, http://www.w3c.org/TR/1999/REC-xslt-19991116.
10. ***, XSL Transformations (XSLT) Version 2.0, schiþã de lucru W3C din 5 noiembrie
2004, http://www.w3.org/TR/2004/WD-xslt20-20041105.
11. ***, Cascading Style Sheets, level 2 (CSS2) Specification, 12 mai 1998, http://
www.w3.org/TR/REC-CSS2.
12. ***, SOAP Version 1.2, recomandare W3C din 24 iunie 2003, http://www.w3.org/
TR/soap.
13. ***, Web Services Description Language (WSDL) 1.1, http://www.w3.org/TR/wsdl.
14. Apache HTTP Server Project (http://httpd.apache.org/). Aici gãsiþi Apache (cel
mai popular server Web), dezvoltat de Apache Software Foundation.
15. CapeScience (http://www.capescience.com). Acest sit oferã tutoriale despre
serviciile Web.
16. Codewalkers (http://codewalkers.com). Situl este dedicat dezvoltatorilor PHP. Include
tutoriale ºi forumuri. Oferã utilizatorilor posibilitatea de a publica pe sit scripturi
PHP scrise de ei înºiºi.
17. Developers.net (http://www.developers.net). Situl oferã articole despre baze de
date, XML, servicii Web etc.
18. Dev Shed (http://www.devshed.com). Acest sit oferã o serie de articole introductive
în PHP, MySQL, CSS, XML, servicii Web etc.
19. HotScripts (http://www.hotscripts.com/PHP). Aici gãsiþi articole pentru învãþarea
limbajului PHP, scripturi ºi aplicaþii scrise în acest limbaj.
20. LinuxGuruz (http://www.linuxguruz.org). Aici gãsiþi articole, tutoriale, FAQ despre
Linux, Apache Web Server, XHTML, PHP, MySQL etc.
21. Melonfire (http://www.melonfire.com). Situl oferã articole despre PHP ºi MySQL,
dedicate mai ales începãtorilor.
22. MySQL AB (http://www.mysql.com). Aici gãsiþi sistemul de gestiune a bazelor de
date relaþionale MySQL, dezvoltat de firma suedezã MySQL AB. De asemenea,
gãsiþi clienþi MySQL cu interfaþã graficã ºi o serie de drivere MySQL.
23. OASIS UDDI (http://www.uddi.org). Acesta este situl oficial al proiectului UDDI.
24. Onlamp PHP Dev Center (http://www.onlamp.com/php). În acest sit gãsiþi o bogatã
colecþie de tutoriale Linux, Apache, PHP, MySQL etc.
25. PEAR (http://www.sitepoint.com). Acesta este situl unde pot fi gãsite pachetele
PEAR (PHP Extension and Application Repository).
26. PECL (http://pecl.php.net). Acesta este un depozit în care puteþi gãsi extensii PHP.
27. PerfectXML (http://www.perfectxml.com/default.asp). Situl oferã articole despre
servicii Web, XML etc.
28. PHP (http://www.php.net). Aici gãsiþi serverul de aplicaþii PHP ºi manualul oficial
PHP. Tot de aici puteþi obþine URL-urile unor situri care oferã documentaþie,
scripturi ºi aplicaþii PHP.
29. PHPBuilder (http://www.phpbuilder.com). Situl oferã utilizatorilor o mulþime de
resurse PHP ºi MySQL extrem de utile.
30. PHP Classes (http://www.phpclasses.org). Acesta este un sit extrem de util, care
oferã aplicaþii PHP scrise utilizând OOP. Utilizatorii îºi pot încãrca la aceastã adresã
propriile scripturi.
310 BIBLIOGRAFIE GENERALÃ
La Editura POLIROM
au apãrut:
Doina Hrinciuc Logofãtu C++. Probleme rezolvate ºi algoritmi
Marin Fotache SQL. Dialecte DB2, Oracle ºi Visual FoxPro
Sabin Buraga, Gabriel Ciobanu Atelier de programare în reþele de calculatoare
Marin Fotache, Ioan Brava, Cãtãlin Strîmbei, Liviu Creþu Visual FoxPro. Ghidul dezvoltãrii
aplicaþiilor profesionale
Sabin Buraga, Victor Tarhon-Onu, ªtefan Tanasã Programare Web în bash ºi Perl
Dragoº Acostãchioaie Administrarea ºi configurarea sistemelor Linux
Dragoº Acostãchioaie C ºi C++ pentru Linux
Sabin Buraga Proiectarea siturilor Web. Design ºi funcþionalitate
Cãlin Ioan Acu Optimizarea paginilor Web
ªtefan Tanasã, Cristian Olaru, ªtefan Andrei JAVA de la 0 la expert
Marin Fotache, Cãtãlin Strîmbei, Liviu Creþu Oracle 9i2. Ghidul dezvolt\rii aplica]iilor
profesionale
Mihai Cioatã ActiveX. Concepte ºi aplicaþii
ªtefan Trãuºan-Matu Programare `n Lisp. Inteligen]\ artificial\ [i Web semantic
Adrian Munteanu, Valeric\ Greavu-{erban, Gabriel Cristescu Re]ele Windows. Servere [i
clien]i. Exemple practice
Octavian Dospinescu Dezvoltarea aplica]iilor `n VisualBasic.NET
Cristian A. Giumale Introducere `n analiza algoritmilor. Teorie [i aplica]ie
Dragoº Acostãchioaie FreeBSD
Valeriu Iorga, Cristian Opincaru, Corina Stratan, Alexandru Chiri]\ Structuri de date [i
algoritmi. Aplica]ii `n C++ folosind STL
Traian Anghel, Dezvoltarea aplica]iilor WEB folosind XHTML, PHP [i MySQL
{tefan Tanas\, Cristian Olaru, Dezvoltarea aplica]iilor WEB folosind JAVA
Traian Anghel Programarea în PHP. Ghid practic
`n preg\tire:
Lenuþa Alboaie Servicii Web. Despre SOAP ºi NET
în pregãtire: