Sunteți pe pagina 1din 28

350 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL

Funcia f ormat_entry () nu este prezentat aici. n esen, este asemntoare cu funcia f ormat_rtf_entry () din
scriptul gen_cat, dar fr cuvintele de control RTF.
Publicarea pe Internet a catalogului Ligii istorice
n seciunea urmtoare, Utilizarea DBI n aplicaiile Web", vom ncepe s scriem scripturi care se conecteaz la
serverul MySQL pentru a extrage informaii i pentru a scrie acele informaii n forma paginilor Web care apar n
browserul Web al unui client. Aceste scripturi genereaz cod HTML n mod dinamic, n conformitate cu cerinele
clientului, nainte de a ajunge la acel punct, s ncepem s ne gndim la HTML insernd un program DBI care
genereaz un document HTML static, ce poate fi ncrcat n arborele cu documente al unui server Web. Un
candidat bun n acest sens este un catalog al Ligii istorice pe care l creai n format HTML, deoarece unul din
scopurile noastre era oricum publicarea pe Internet a catalogului.
n general, un document HTML are o structur care este oarecum asemntoare cu urmtoarea:
<HTML>
<HEAD>
<TITLE>Titlul paginii mele</TITLE>
</HEAD>
<BODY>
<H1>Titlu de nivel 1</H1>
... coninutul corpului documentului...
nceputul documentului nceputul capului documentului titlul documentului sfritul capului documentului
nceputul corpului documentului un titlu de nivel l
</BODY> * sfritul corpului documentului
</HTML> * sfritul documentului
Pentru a genera catalogul n acest format, nu este necesar s scriei un ntreg script. V reamintii c, atunci cnd
am scris scriptul gen_cat, am folosit un cadru extensibil astfel nct s putem insera programul pentru generarea
catalogului n formate suplimentare. Aceasta nseamn c, pentru a aduga programe pentru generarea de date de
ieire n format HTML, trebuie s scriem funciile de iniializare i curenie" a documentului, precum i o
funcie pentru formatarea intrrilor individuale. Apoi, trebuie s crem un element de tip cutie de distribuie"
care s indice spre aceste funcii. Schia de document prezentat anterior se poate mpri destul de simplu n
seciuni de prolog si epilog, care pot fi manipulate de funciile de iniializare, respectiv curenie, precum i ntr-
o parte de mijloc, care poate fi generat de funcia de formatare a intrrilor. Funcia de iniializare HTML
genereaz toate elementele documentului, pn la titlul de nivel l, iar funcia de curenie genereaz etichetele de
nchidere </BODY> i </HTML>:
sub html_init
prin "<HTML>\n";
Capitolul? Interfaa API pentru Perl DBI 351
prin "<HEAD>\n";
prin "<TlTLE>Catalogul membrilor Ligii istorice</TITLE>\n";
prin "</HEAD>\n";
prin "<BODY>\n";
prin "<H1>Catalogul membrilor Ligii istorice</H1>\n";
sub html_cleanup
{
prin "</BODY>\n"; prin "</HTML>\n";
}
Adevrata munc, de obicei, rezid n formatarea intrrilor. Dar nici mcar aceasta nu este prea grea. Putem
copia funcia format_rtf_entry(), ne asigurm c toate caracterele speciale din cadrul intrrii sunt codificate i
nlocuim cuvintele de control RTF cu etichetele de marcare HTML:
sub format_html_entry
{
my ($entry_ref) = shift;
my ($adresa);
# codeaza caracterele speciale din HTML foreach my $key (keys (%{entry_ref }))
$entry_ref ->{key} $entry_ref ->{key} = $entry_ref->{key} $entry_ref->{key} =
- s/&/&amp;/g;
- s/\"/&quot;/g;
- s/>/&gt;/g;
- s//&lt;/g;
printf "<STRONG> Nume: %s</STRONG><BR>\n",
format_name ($entry_ref ) ; $adresa = " ";
$adresa .= $entry_ref->{strada} if $entry_ref ->{strada}; $adresa .= ", " . $entry_ref ->{oras} if $entry_ref-
>{oras}; $adresa .= ", " . $entry_ref->{stat} if $entry_ref ->{stat}; $adresa .= " " . $entry_ref ->{cod} if
$entry_ref ->{cod}; prin "Adresa: $adresa<BR>\n" if Sadresa; prin "Telefon: $entry_ref ->{telefon}<BR>\n"
if $entry_ref ->{telefon}; prin "Email: $entry_ref->{email}<BR>\n"
if $entry_ref ->{email}; prin "Interese: $entry_ref ->{interese}<BR>\n"
if $entry_ref ->{interese}; prin "</BR>n";
352 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Acum, vom aduga la cutia de distribuie un alt element, care indic spre funciile de scriere HTML, iar
modificrile aduse scriptului gen_cat au fost finalizate:
# cutie de distribuie care conine funcii de formatare
# pentru fiecare format al datelor de ieire my (%cutie_distributie) =
"banquet"
# funcii pentru lista de banchet
"init" => undef, # nu este necesara nici
# o iniializare
"entry" => \&format_banquet_entry, "cleanup" => undef # curenia nu este necesara
"rtf =>
# funcii pentru formatul RTF
"in it"
"entry"
"cleanup"
=> \&rtf_init,
=> \&format_rtf_entry,
=> \&rtf_cleanup
"html" =>
# funcii pentru formatul HTML
"init" => \&html_init,
"entry" => \&format_html_entry,
"cleanup" => \&html_cleanup
}
); . . -;i
Pentru a genera catalogul n format HTML, rulai urmtoarea comand i instalai l fiierul de ieire rezultant n
arborele cu documente al serverului dumneavoastr Web: l
% gen_cat html > catalog.html Cnd actualizai catalogul, putei rula comanda din nou, pentru a actualiza
versiunea! electronic a acestuia. O alt strategie este s configurai o sarcin a utilitarului cron care l s se
execute periodic. Astfel, catalogul n versiune electronic va fi actualizat automat. I De exemplu, pot folosi o
intrare crontab ca aceasta, pentru a rula gen_cat n fiecrei diminea la ora 4:
|
O 4 * * * /u/paul/samp_db/gen_cat > /usr/local/apache/htdocs/catalog.html! Utilizatorul n numele cruia
ruleaz aceast sarcin cron trebuie s aib att permisiuj| nea de a executa scripturile localizate n catalogul meu
samp_db, ct si permisiunea de al scrie fiiere n arborele cu documente al serverului Web.
',!
Capitolul 7 Interfaa APt pentru Perl DBI 353
Utilizarea DBI n aplicaii Web
Scripturile DBI pe care le-am scris pn acum au fost concepute pentru a fi utilizate din interpreter ntr-un mediu
bazat pe linii de comand, dar DBI este util i n alte contexte, cum ar fi n cazul dezvoltrii aplicaiilor bazate pe
Web. Cnd scriei scripturi DBI care pot fi invocate din browserul dumneavoastr Web, deschidei noi si
interesante posibiliti de a interaciona cu bazele dumneavoastr de date.
De exemplu, dac afiai date n format tabelar, putei transforma cu uurin fiecare titlu de coloan ntr-o
legtur (link), pe care o putei selecta pentru a sorta din nou datele din coloana respectiv. Aceasta v permite s
vizualizai datele ntr-un alt mod cu un singur clic, fr a introduce nici o interogare. Sau, putei furniza un
formular n care un utilizator poate introduce criterii pentru cutarea ntr-o baz de date, iar apoi afiai o pagin
care conine rezultatele cutrii. Funcionaliti simple ca aceasta pot avea un impact semnificativ asupra
nivelului de interactivitate pe care l furnizai pentru accesul la coninutul bazelor dumneavoastr de date. n
plus, de regul caracteristicile de afiare ale browserelor Web sunt superioare celor ale unei ferestre terminal,
deci datele de ieire arat i ele frecvent mai bine.
n aceast seciune, vom crea urmtoarele scripturi bazate pe Web:
Un browser general pentru tabelele din baza de date samp_db. Aceast aplicaie nu este legat de vreo operaie
specific pe care dorim s o efectum cu baza de date, dar ilustreaz numeroase concepte de programare n Web
i furnizeaz o modalitate convenabil de examinare a informaiilor pe care le conin tabelele.
Un browser de punctaje, care ne permite s examinm rezultatele obinute la orice test sau chestionar. Este un
mijloc rapid de examinare a rezultatelor de la evenimentele de tip examinare i este util atunci cnd trebuie s
stabilim curba rezultatelor la un test, astfel nct s putem acorda lucrrilor note de tip literal.
Un script pentru identificarea membrilor Ligii istorice care manifest interese comune. Aceast operaie se
poate realiza permind utilizatorului s introduc o expresie de cutare, iar apoi cutnd expresia respectiv n
cmpul interese al tabelului membru. Deja am scris un script n linie de comand care execut aceast operaie,
dar o versiune bazat pe Web furnizeaz un punct de referin instructiv, permind o comparaie ntre dou
abordri ale aceleiai sarcini.
Vom scrie aceste scripturi folosind modulul Perl CGLpm, care reprezint modalitatea cea mai simpl de a lega
scripturi DBI la Web. (Pentru instruciuni cu privire la procurarea modulului CGI.pm, vezi Anexa A.) Modulul
CGI.pm este astfel denumit deoarece v ajut s scriei scripturi care folosesc protocolul Common Gateway
Interface, care definete modul n care un server Web comunic cu alte programe. Modulul CGI.pm se ocup de
detaliile pe care le incumb un numr de operaii comune de ntreinere, cum ar fi colectarea valorilor
parametrilor transferai scriptului dumneavoastr de ctre serverul Web ca date de intrare. De asemenea, CGI.pm
furnizeaz metode convenabile de generare a datelor de ieire n format HTML, ceea ce reduce posibilitatea de a
scrie programe HTML cu malformaii n comparaie cu a scrie personal etichete HTML brute.
354 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Vei nva suficient de multe despre CGI.pm n capitolul de fa pentru a v scrie propriile dumneavoastr
aplicaii Web, dar, desigur, nu am descris toate funcionalitile sale. Pentru a nva mai multe despre acest
modul, consultai volumul Official Guide to Programming with CGI.pm, de Lincoln Stein (John Wiley, 1998)
sau examinai documentaia electronic la adresa:
http://stein.cshl.org/WWW/software/CGI/
Configurarea serverului Apache pentru scripturile CGI
n afar de DBI i CGI.pm, mai avem nevoie de nc o component pentru scrierea scripturilor bazate pe Web, i
anume un server Web. Instruciunile din aceast seciune sunt orientate n direcia utilizrii scripturilor cu
serverul Apache, dar probabil c putei folosi un alt server, dac dorii, printr-o oarecare adaptare a
instruciunilor.
Diferitele componente ale unei instalri a serverului Apache se gsesc, de obicei, n catalogul /usr/local/apache.
Pentru expunerea de fa, cele mai importante subcataloage ale acestui catalog sunt htdocs (pentru arborele de
documente HTML), cgi-bin (pentru scripturile executabile i programele care vor fi invocate de ctre serverul
Web) i conf (pentru fiierele de configurare). Aceste cataloage pot avea un alt amplasament pe sistemul
dumneavoastr, n acest caz, operai modificrile adecvate la notele care urmeaz.
Trebuie s v asigurai c cgi-bin nu se afl n interiorul arborelui de documente Apache, astfel nct scripturile
din cadrul su s nu poat fi solicitate sub form de text simplu. Aceasta este o msur de precauie. Nu dorii ca
programe client ru intenionate s v examineze scripturile n cutarea unor bree de securitate, prin aspirarea"
textului scripturilor si studierea acestuia.
Pentru a instala un script CGI n vederea utilizrii cu serverul Apache, inserai-1 n catalogul cgi-bin, dup care
transferai dreptul de proprietate a fiierului ctre utilizatorul pentru care ruleaz serverul Apache i modificai-i
modul astfel nct s fie executabil i s poat fi citit numai de ctr.e acel utilizator. De exemplu, dac serverul
Apache ruleaz ca un utilizator cu numele www, folosii urmtoarele comenzi:
% chown www nume_script
% chmod 500 nume_script Probabil c va trebui s rulai aceste comenzi ca www sau ca root. Dac nu avei
permisiunea de a instala scripturi n catalogul cgi-bin, putei cere administratorului de sistem si i efectueze
aceast operaie n numele dumneavoastr.
Dup ce scriptul a fost instalat, l putei solicita din browserul dumneavoastr expediindJ serverului Web adresa
URL adecvat, n mod caracteristic, adresa URL se prezint astfel:.]
http:/1 numele.gazdei.dv/cgi-binlnume_script
Solicitarea scriptului din browserul dumneavoastr de Web determin execuia acest de ctre serverul Web.
Datele de ieire ale scriptului v sunt trimise napoi, iar rezultat apare ca o pagin Web n browserul
dumneavoastr.
Dac dorii s folosii scripturi CGI cu mod_perl pentru a obine performane mai bunel iat ce avei de fcut:
Capitolul 7 Interfaa API pentru Perl DBI 355
1. Verificai dac dispunei cel puin de urmtoarele versiuni ale programelor necesare: Perl 5.004, CGI.pm 2.36
i modJDerl 1.07.
2. Asigurai-v c mod_perl este compilat n executabilul dumneavoastr Apache.
3. Configurai un catalog pentru stocarea scripturilor. Eu folosesc /usr/local/apache/cgi-perl. Catalogul cgi-perl
nu trebuie s se afle n interiorul arborelui de documente al serverului dumneavoastr Apache, din aceleai
motive de securitate ca i catalogul cgi-bin.
4. Cerei serverului Apache s asocieze scripturile care se gsesc n catalogul cgi-perl cu mod_perl:
Alias /cgi-perl/ /usr/local/apache/cgi-perl
<Location /cgi-perl>
SetHandler perl-script
PerlHandler Apache::Registry
Options ExecCGI </Location>
Dac folosii o versiune curent a serverului Apache care utilizeaz un singur fiier de configurare, plasai toate
aceste directive n fiierul httpd.conf. Dac versiunea dumneavoastr de Apache folosete abordarea mai veche
cu trei fiiere pentru informaia de configurare, plasai directiva Alias n fiierul srm.conf, respectiv liniile
Location n fiierul access.conf.
Nu activai directivele mod_perl, PerlSendHeader sau PerlSetupEnv pentru catalogul cgi-perl. Acestea sunt
manipulate automat de CGI.pm, iar activarea lor poate induce conflicte de prelucrare.
Adresa URL a unui script mod-perl este similar cu aceea a unui script CGI standard. Singura diferen este
aceea c specificai cgi-perl n loc de cgi-bin:
http://numele.gazdei. dv/cgi-perl/nume_script
Pentru mai multe informaii, vizitai regiunea mod_perl a sitului Web Apacl e, de la urmtoarea adres:
http://perl/apache/org/
O scurt introducere n CGI.pm
Pentru a scrie un script Perl care folosete modulul CGI.pm, plasai o linie use lng nceputul scriptului, apoi
creai un obiect CGI care v ofer acces la metodele si variabilele modulului CGI.pm:
use CGI;
my ($cgi) = new CGI;
Scripturile noastre CGI folosesc caracteristicile modulului CGI.pm prin invocarea metodelor utiliznd variabila
$cgi. De exemplu, pentru a genera un titlu de nivel l, vom folosi metoda h1 () astfel:
prin $cgi->h1 ("Titlul meu");
356 Partea a ll-a Utitizarea interfeelor de programare ale sistemului MySQL
De asemenea, CGI.pm accept un stil de utilizare care v permite s-i apelai, metodele ca funcii, fr
construcia iniial $cgi->. Nu folosesc aici sintaxa respectiv, deoarece notaia $cgi-> este mai asemntoare cu
modul de utilizare a modulului DBI, precum i . deoarece mpiedic apariia unor conflicte ntre numele
funciilor din modulul CGI.pm i numele oricror altor funcii pe care Ie definii.
Verificarea parametrilor de intrare i scrierea datelor de ieire
Una dintre operaiile pe care CGI.pm le execut automat este tratarea tuturor detaliilor urte" implicate n
colectarea informaiilor de intrare furnizate de serverul Web scrip-tului dumneavoastr. Pentru a obine acele
informaii, tot ce avei de fcut este s invocai metoda pa r am (). Putei obine numele tuturor parametrilor
disponibili astfel:
my (@param) = $cgi->param (); Pentru a regsi valoarea unui anumit parametru, denumii parametrul care v
intereseaz:
if (!$cgi->param ("parametruljneu"))
prin "parametruljneu nu este configurat\n" ;
else
printf "Valoarea parametrului parametruljneu: %s\n", $cgi->param ("parametruljneu");
De asemenea, CGI.pm furnizeaz metode pentru generarea datelor de ieire care trebuie trimise browserului
client. S considerm urmtorul document HTML:
<HTML>
<HEAD>
<TITLE>Titlul paginii mele</TITLE>
</HEAD>
<BODY>
<H1>Titlu de nivel 1</H1>
<P>Paragraf 1.
<P>Paragraf 2. %
</BODY>
</HTML> Acest program folosete $cgi pentru a produce un document echivalent:
prin $cgi->header ();
prin $cgi->start_htnrl (-title => "Titlul paginii mele");
prin $cgi->h1 ("Antetul paginii mele");
prin $cgi->p ();
prin "Paragraf 1.\n";
prin $cgi->p ();
prin "Paragraf 2.\n";
prin $cgi->end_html ();
Capitolul? Interfaa API pentru Perl DBI 357
Unul dintre avantajele utilizrii modulului CGLpm pentru generarea datelor de ieire n loc de a scrie personal
cod HTML brut sunt acelea c putei gndi n uniti logice, nu folosind etichete individuale de marcare, iar
codul dumneavoastr HTML are mai puine anse de a conine erori. (Motivul pentru care am spus c are mai
puine anse" este acela c CGI.pm nu v va mpiedica s facei lucruri bizare, cum ar fi includerea unei liste
ntr-un titlu.) De asemenea, pentru textele (altele dect etichetele) pe care le scriei, CGI.pm furnizeaz
modificarea automat a semnificaiei unor caractere (escaping) precum < sau >, care sunt speciale pentru HTML.
Utilizarea metodelor modulului CGI.pm de generare a datelor de ieire nu v mpiedic s scriei personal cod
HTML brut, dac dorii. Putei combina cele dou metode, combinnd apelurile la metodele CGI.pm cu
instruciuni de afiare care genereaz etichete literale.
Modificarea automat a semnificaiei textelor HTML si URL
Dac scriei texte - diferite de textele etichetelor - cu ajutorul mefoddor CGI.pm, precum start_html() sau ti1 (),
programul modific automat semnificaia caracterelor speciale din text. De exemplu, dac generai un titlu
folosind urmtoarea instruciune, caracterul & din textul titlului va fi convertit n &amp; de ctre CGI.pm:
prin $cgi->strt_html {-title => "A, B & C");
Dac scriei texte - altele dect textele etichetelor - fr a folosi metoda modulului CGI.pm de generare a datelor
de ieire, probabil c trebuie s le trecei mai nti prin funcia escapeHTML(), pentru a v asigura c
semnificaia tuturor caracterelor speciale a fost modificat n mod corespunztor. Acest fapt este de asemenea
adevrat atunci cnd construii adrese URL care pot conine caraqtere speciale, dei, n acest caz, trebuie s
folosii n schimb metoda escape (). Este important s folosii metoda de codare adecvat, deoarece fiecare
metod trateaz diferite seturi de caractere drept caractere speciale si codific acele caractere speciale folosind
formate care difer unele de altele. S lum n considerare urmtorul script Perl scurt:
#! /usr/bin/perl
use CGI;
$cgi = new CGI;
$s = "x<= y, corect?";
prin $cgi->escapeHTML ($s) ."\n";
prin $cgi->escape ($s) ."\n";
# codificare pentru
# utilizare ca text HTML
# codificare pentru
# utilizare intr-un URL
Dac rulai acest script, va produce urmtoarele date de ieire, de unde putei observa c procedeul de codificare
pentru textul HTML nu este acelai ca n czu! adreselor URL:
x&lt;=y, corect?
x%3C%3Dy%2C%20corect%3F

358 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL Scrierea paginilor cu utilizri
multiple
Unul din principalele motive pentru scrierea scripturilor bazate pe Web care genereaz cod HTML, n loc de a
scrie documente HTML statice, este acela c un script poate produce diferite categorii de pagini, n funcie de
modul n care este invocat. Toate scripturile CGI pe care le vom scrie au aceast proprietate. Fiecare din aceste
scripturi funcioneaz astfel:
1. Cnd solicitai pentru prima dat scriptul de la browserul dumneavoastr, acesta genereaz o pagin iniial,
care v permite s selectai tipul de informaie dorit.
2. Cnd efectuai o selecie, scriptul este re-invocat, dar de data aceasta regsete i afieaz ntr-o a doua pagin
informaiile specifice pe care le-ai solicitat.
Principala problem, n acest caz, este c dorii ca selecia pe care o efectuai din prima pagin s determine
coninutul celei de-a doua pagini, dar, n mod normal, paginile Web sunt independente unele de altele, dac nu
realizai unele aranjamente speciale. Trucul este de a determina scriptul s genereze pagini care atribuie unui
parametru o valoare care indic urmtoarei invocri a scriptului ceea ce dorii. Cnd invocai scriptul pentru
prima dat, parametrul nu are nici o valoare; acest fapt indic scriptului s prezinte pagina sa iniial. Cnd
indicai informaiile pe care dorii s le vedei, pagina invoc din nou scriptul, dar cu parametrul configurat la o
valoare care indic scriptului ce are de fcut.
Exist diferite moduri de transfer al instruciunilor de la o pagin napoi la un script. O modalitate este de a
furniza utilizatorului un formular pe care acesta s-1 completeze. Cnd utilizatorul trimite formularul, coninutul
formularului este trimis serverului Web. Serverul transmite informaiile scriptului, care poate afla datele trimise
de utilizator invocnd metoda param (). Astfel vom proceda pentru cel de-al treilea script CGI al nostru (cel care
per- mite utilizatorului s introduc un cuvnt cheie pentru a cuta n catalogul Ligii istorice). \
Un alt mod de specificare a instruciunilor ctre un script este de a transfera informaiile l ca parte a adresei URL
pe care o trimitei serverului Web atunci cnd solicitai scriptul. ] Astfel vom proceda pentru scripturile de
navigare n tabelele din baza de date samp_db, J respectiv de navigare prin tabelele cu punctaje. Aceast metod
se bazeaz pe faptul c l scriptul genereaz o pagin care conine hiperlegturi. Prin selectarea unei legturi se l
invoc din nou scriptul, dar se specific o valoare a parametrului care indic scriptului ce | are de fcut, n
schimb, scriptul se invoc singur n diferite moduri, pentru a furniza l diferite categorii de rezultate, n funcie de
legtura pe care o selecteaz utilizatorul. l
Un script i poate permite s se auto-invoce expediind browserului o pagin care conine l o hiperlegtur spre
propriul su URL. De exemplu, un script denumit scriptuljneu J poate scrie o pagin care s conin aceast
legtur: i
<A HREF="/cgi-bin/scriptul_meu">Clic pe mine!</A> J
Cnd utilizatorul execut clic pe textul Clic pe mine!", browserul utilizatorului trimitf i o cerere pentru
scriptuljneu napoi la serverul Web. Desigur, aceast aciune nu va fcea dect s determine scriptul s trimit
din nou aceeai pagin, deoarece nu au fost furnizatesl alte informaii. Totui, dac ataai un parametru la URL,
acel parametru este trimis napoi! serverului Web atunci cnd utilizatorul selecteaz legtura. Serverul invoc
scriptul, iaifl scriptul poate apela funcia param () pentru a detecta c parametrul a fost configurat sil poate
ntreprinde o aciune n funcie de valoarea parametrului. m
Capitolul 7 Interfaa API pentru Perl DBI 359
Pentru a ataa un parametru la sfritul adresei URL, adugai un caracter ? urmat de o pereche nume-valoare.
Pentru a ataa mai muli parametri, separai-i prin caractere &. De exemplu:
/cgi-bin/scriptul_meu?nume=valoare
/cgi-bin/scriptul_meu?nume=valoare&nume2=valoare2
Pentru a construi o adres URL cu parametri ataai care face referire la sine nsi, un script CGI trebuie s
nceap prin a apela metoda script_nane () pentru a-si obine propriul URL, iar apoi trebuie s ataeze parametri
la adres, astfel:
$url = $cgi->script_name (); # obine URL pentru script
$url .= "?nume=valoare"; # adaug primul parametru
$url .= "&nume2=valoare2"; # adaug al doilea parametru
Dup ce adresa URL a fost construit, putei genera o etichet de hiperlegtur <A> care conine adresa, folosind
metoda a() a modulului CGI.pm:
prin $cgi->a ({+href => $url}, "Clic pe minei");
Pentru a vedea mai limpede care este modul de funcionare al acestei metode, vom examina un scurt script CGI.
Cnd este invocat pentru prima dat, scriptul urmtor, denumit f lip_f lop, prezint o pagin denumit Pagina A,
care conine o singur hiperlegtur. Prin selectarea legturii scriptul va fi invocat din nou, dar parametrul pagina
este astfel configurat nct i indic s afieze pagina B. i pagina B conine o legtur cu scriptul, dar fr nici o
valoare pentru parametrul pagina. Ca atare, selectarea legturii n pagina B determin afiarea din nou a paginii
iniiale. Invocrile ulterioare ale scriptului determin afiarea alternativ a paginilor A si B.
use CGI;
my ($cgi) = new CGI;
my ($url) = $cgi->script_name (); # propriul URL al acestui script
print $cgi->header ();
if ($cgi->param ("pagina") ne "b") # afieaz pagina A
print $cgi->start_html (-title => "Flip-Flop: Pagina A"); prin "Aceasta este pagina A.<BR>Pentru a selecta
pagina B, "; $url .= "?pagina=b"; # ataeaz parametrul
# pentru a selecta pagina B print $cgi->a ({-href => $url}, "clic aici");
else # afieaz pagina B
print $cgi->start_html (-title => "Flip-Flop: Pagina B"); prin "Aceasta este pagina B.<BR>Pentru a selecta
pagina A, "; print $cgi->a ({-href => $url}, "clic aici");
prin $cgi->end_html();
360 Partea a It-a Utilizarea interfeelor de programare ale sistemului MySQL
Dac apare nc un client care solicit scriptul f lip_f lop, va fi prezentat pagina iniial, deoarece diferitele
browsere ale clienilor nu interacioneaz unele cu altele.
Valoarea variabilei $url a fost configurat ntr-o manier destul de cavalereasc" n exemplele precedente. Este
preferabil s folosii metoda escape () pentru a codifica numele i valorile parametrilor dumneavoastr atunci
cnd le ataai la un URL, n cazul n care conin caractere speciale. Iat o modalitate mai bun de a construi un
URL cu parametri ataai:
# obine URL pentru script
# adaug primul parametru $cgi->escape ("valoare"));
# adaug al doilea parametru
$url = $cgi->script_name () $url .= sprintf ("?%s=%s",
$cgi->escape ("nume"), $url .= sprintf ("&%s=%s",
$cgi->escape ("nume2"), $cgi->escape ("valoare2"));
Conectarea la serverul MySQL din scripturile Web
Scripturile n linie de comand pe care le-am prezentat n seciunea anterioar, Utilizarea DBI", foloseau un
preambul identic pentru stabilirea unei conexiuni cu serverul MySQL. Scripturile noastre CGI partajeaz si ele o
anumit parte de program, ns puin diferit:
use DBI;
use CGI;
use strict;
# Toi parametrii de conexiune prestabilii lipsesc
my ($host_name, $user_name, $password) = (undef, undef, undef); my (db_name) = "samp_db";
# construiete sursa de date
my ($dsn) = "DBI:mysql:$db_name";
$dsn .= ":hostname=$host_name" if $host_name;
$dsn .= ";mysql_read_default_file=/usr/local/apache/conf/samp_db.cnf";
# conectare la server
my (%attr) = ( RaiseError = 1 );
my ($dbh) = DBI->connect ($dsn, $user_name, Spassword, \%attr); Acest preambul difer de cel folosit
pentru scripturile n linie de comand din urm- j toarele motive:
Acum, prima seciune conine o instruciune use CGI.
Nu mai analizm argumentele din linia de comand.
Programul caut parametrii de conexiune ntr-un fiier cu opiuni, dar nu folosete fiie
. my. cnf din catalogul de baz al utilizatorului care ruleaz scriptul (cu alte cuvinte, cat^j logul de baz al
utilizatorului serverului Web). Serverul Web poate rula scripturi pent obinerea accesului la alte baze de date si
nu avem nici un motiv s presupunem c

Capitolul 7 Interfaa API pentru Perl DBI 361


acele scripturi vor folosi aceiai parametri de conexiune, n schimb, vom examina fiierul cu opiuni stocat ntr-o
alt locaie (/usr/local/apache/conf /samp_db.cnf). Dac dorii s folosii un alt fiier, modificai calea de acces la
fiierul cu opiuni.
Scripturile invocate printr-un server Web ruleaz ca utilizator al serverului Web, nu ca dumneavoastr personal.
Acest fapt ridic unele probleme de securitate, deoarece dumneavoastr pierdei controlul dup ce acesta este
preluat de serverul Web. Trebuie s atribuii proprietatea asupra fiierului cu opiuni utilizatorului n numele
cruia ruleaz serverul Web (poate www, nimeni sau altcineva similar) si s configurai modul la valoarea 400
sau 600, astfel nct nici un alt utilizator s nu-l mai poat citi. Din pcate, fiierul poate fi totui citit de orice
persoan care poate instala un script ce va fi executat de serverul Web. Tot ce are de fcut respectivul este s
scrie un script care deschide n mod explicit fiierul cu opiuni si i afieaz coninutul ntr-o pagin Web.
Deoarece scriptul persoanei respective ruleaz ca utilizator al serverului Web, va avea permisiuni complete de
citire a fiierului.
Din acest rnotiv, este prudent s creai un utilizator MySQL care are privilegii numai de citire (SELECT) asupra
bazei de date samp_db, apoi menionai n fiierul samp_db.cnf numele i parola utilizatorului respectiv, nu pe
acelea ale dumneavoastr. Astfel, nu riscai s permitei scripturilor s se conecteze la baza dumneavoastr de
date ca utilizator care are permisiunea de a modifica tabelele din baza de date respectiv. Capitolul 11,
Administrarea general a sistemului MySQL", expune modul de creare a unui cont de utilizator MySQL cu
privilegii limitate.
Alternativ, putei aranja ca execuia scripturilor s aib loc prin mecanismul suEXEC din Apache. Acesta v
permite s executai un script ca un anumit utilizator de ncredere, dup care scrie scriptul pentru a obine
parametrii de conexiune dintr-un fiier cu opiuni care poate fi citit numai de ctre utilizatorul respectiv. Putei
proceda astfel cu scripturile care necesit acces de scriere la baza de date, de exemplu.
O alt metod const n a scrie un script care s solicite un nume de utilizator i o parol de la utilizatorul
clientului, respectiv s foloseasc aceste valori pentru a stabili o conexiune cu serverul MySQL. Aceast soluie
este mai adecvat pentru scripturi pe care le creai n scopuri administrative dect pentru scripturile de uz
general, n orice caz, reinei c unele metode de solicitare a numelui i a parolei pot fi atacate de orice persoan
care poate insera un program de spionaj (n original sniffer - N.T.) n reea ntre dumneavoastr i server.
Aa cum v-ai dat seama din paragrafele anterioare, securitatea scripturilor Web poate fi un aspect delicat.
Categoric, constituie un subiect despre care trebuie s citii mai mult, deoarece este un subiect de mare
anvergur, care nu poate fi tratat aici dup cum o merit. Putei ncepe cu materialul dedicat securitii din
manualul Apache. De asemenea, este util seciunea cu ntrebri frecvente (FAQ) despre Web, de la adresa:
http://www.w3.org/Security/Faq/
362 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Un browser pentru baza de date samp_db
Pentru prima noastr aplicaie bazat pe Web, vom crea un script simplu - denumit samp_browse - care v
permite s vedei care sunt tabelele din baza de date samp_db, precum i s examinai n mod interactiv
coninutul acestor tabele din browserul dumneavoastr de Web. Scriptul sampjjrowse funcioneaz astfel:
Cnd solicitai pentru prima dat samp_browse de la browserul dumneavoastr, acesta se conecteaz la serverul
MySQL, regsete o list cu tabelele din baza de date samp_db i trimite browserului o pagin n care fiecare
tabel este prezentat sub forma unei legturi pe care o putei selecta. Cnd selectai numele unui tabel din aceast
pagin, browserul dumneavoastr trimite o cerere serverului Web, solicitnd scriptului samp_browse s afieze
coninutul acelui tabel.
Dac samp_browse primete un nume de tabel de la serverul Web atunci cnd este invocat, regsete coninutul
tabelului respectiv i prezint informaiile browserului dumneavoastr Web. Titlul fiecrei coloane de date este
numele coloanei din tabel. Titlurile sunt prezentate sub form de legturi; dac selectai una din ele, browserul
dumneavoastr trimite o cerere serverului Web pentru a afia acelai tabel, dar tabelul va fi acum sortat n funcie
de coloana pe care ai selectat-o.
Un avertisment: tabelele din baza de date samp_db sunt relativ mici, deci trimiterea ntregului coninut al unui
tabel ctre browserul dumneavoastr nu reprezint o problem. Dac editai samp_db astfel nct s afieze
tabele dintr-o alt baz de date, care conine tabele mari, trebuie s luai n considerare adugarea unei clauze
LIMIT la instruciunile de regsire a rndurilor.
n corpul principal al scriptului samp_browse, crem obiectul CGI i scriem partea iniial a paginii Web. Apoi,
verificm dac trebuie sau nu s afim un anumit tabel, n funcie de valoarea parametrului numejtabel: my
($cgi) = new CGI;
# scrie partea iniiala a paginii
my (Stitlu) = "$db_name browser de baze de date"; prin $cgi->header ();
print $cgi->start_html (-title => $titlu); prin $cgi->h1 ($title);
# parametri de cutat in URL
my ($nume_tabel) = $cgi->param ("numejtabel");
my ($coloana_sortare) = $cgi->param ("coloana_sortare");
# daca $nume_tabel nu are nici o valoare, afieaz o lista
# cu tabele pe care se poate executa clic. Altfel, afieaz
# coninutul tabelului dat. Parametrul $nume_coloana, daca
# are o valoare, arata coloana in funcie de care se va sorta, if (!$nume_tabel)
Capitolul 7 Interfaa API pentru Perl DBI 363
afiseaza_lista_tabele() } else
{
afiseaza_tabel ($nume_tabel, $coloana_sortare); } prin $cgi->end_html ();
Este uor de aflat valoarea unui parametru, deoarece CGI.pm execut toate operaiile de determinare a
informaiilor pe care serverul Web le transmite scriptului. Trebuie numai s apelm funcia param () cu numele
parametrului care ne intereseaz, n corpul principal al scriptului samp_browse, parametrul respectiv este
nume_tabel. Dac parametrul nu este definit sau este vid, aceasta este invocarea iniial a scriptului si se afieaz
lista cu tabele, n caz contrar, se afieaz coninutul tabelului desemnat de parametrul nume_tabel, sortat n
funcie de valorile din coloana determinat de parametrul coloana_sortare. Dup afiarea informaiilor adecvate,
apelm funcia end_html() pentru a insera etichetele HTML de nchidere.
Funcia afiseaza_lista_tabele() genereaz pagina iniial. Funcia afiseaza_lista_ta-bele () regsete lista cu tabele
si scrie un tabel HTML cu o singur coloan, care conine n fiecare celul numele unui tabel din baza de date:
sub afiseaza_list_tabele
{
my ($ary_ref, $url);
prin "Selectai un tabel executnd clic pe numele lui:<BR><BR>\n";
# regsete referina la un tablou dintr-o singura coloana,
# alctuit din numele tabelelor
$ary_ref = $dbh->selectcol_arrayref (qq{ SHOW TABLES FROM $db_name });
# afieaz tabelul cu un chenar prin "<TABLE BORDER>\n"; prin "<TR>\n";
afiseaza_celula ("TH", "Nunele tabelului", 1);
prin "</TR>\n";
foreach my $nume_tabel (@{$ary_ref>)
{
$url = $cgi->script_name ();
$url .= sprintf ("?nume_tabel=%s", $cgi->escape ($nume_tabel));
prin "<TR>\n";
afiseaza_celula ("TD", $cgi->a ({-href => $url}, $nume_tabel), 0);
prin '</TR>\n";
}
prin "</TABLE>\n";
364 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Pagina generat de funcia afiseaza_lista_tabele() conine legturi care se prezint astfel:
/cgi-bin/samp_browse?nume_tabel = absente
/cgi-bin/samp_browse?nume_tabel = eveniment
/cgi-bin/samp_browse?nume_tabel = membru
Dac parametrul nume_tabel are o valoare atunci cnd este invocat scriptul samp_browse, scriptul transfer
valoarea funciei afiseaza_tabel(), alturi de numele coloanei n funcie de care se vor sorta rezultatele. Dac nu
este specificat nici o coloan, sortarea se va efectua n funcie de prima coloan (putem face referire la coloane
dup poziia acestora, deci aceast operaie se poate executa uor folosind o clauz ORDER BY 1 ): sub
afiseaza_tabel()
{
my ($nume_tabel, $coloana_sortare) = e_;
my ($sth, $url);
# daca nu este specificata coloana de sortare,
# folosete prima coloana $coloana_sortare = "1" unless $coloana_sortare;
# prezint o legtura care readuce utilizatorul la
# pagina cu lista tabelelor
print $cgi->a ({-href => $cgi->script_name ()}, "Afieaz lista cu tabele"); prin "<BR><BR>\n";
$sth = $dbh->prepare (qq{
SELECT * FROM $nume_tabel ORDER BY $coloana_sortare
$sth->execute() ;
prin "<B> Coninutul tabelului $nume_tabel:</B><BR>\n" ;
# afieaz tabelul cu un chenar prin "<TABLE BORDER>\n";
# folosete numele coloanelor pentru capetele de tabel;
# transforma fiecare capt de tabel intr-o legtura, care
# sorteaz datele in funcie de coloana corespunztoare prin "<TR>\n";
foreach my $nume_coloana (@{$sth->{NAME}>)
{
$url = $cgi->script_name ();
$url .= sprintf ("?nume_tabel=%s" , $cgi->escape ($nume_tabel));
$url .= sprintf ( " &coloana_sortare=%s " , $cgi->escape ($nume_coloana));
S^jlljygigjij^i
Caprtohil 7 Interfaa API pentru Perl DBI 365
afiseaza_celula ("TH", $cgi->a ({-href => $url}, $nume_coloana), 0); } print "</TR>\n";
# afieaz rndurile din tabel
while (my @ary = $sth->fetchrow_array())
{
print "<TR>\n";
foreach my $val (@ary)
{
afiseaza_celula ("TD", $val, 1);
}
print "</TR>\n";
$sth->finish (); print "</TABLE>\n";
O pagin de afiare a tabelelor asociaz capetele de coloan cu legturile care determin afiarea din nou a
tabelului; aceste kgturi includ un parametru $coloana_sortare, care specific n mod explicit coloana n funcie
de care se va efectua sortarea. De exemplu, pentru o pagin care afieaz coninutul tabelului eveniment,
legturile de tip cap de coloan se prezint astfel:
/cgi-bin/samp_browse?nume_tabel ? eveniment&coloana_sortare=data /cgi-bin/samp_browse?nume_tabel =
eveniment&coloana_sortare=tip /cgi-bin/samp_browse?nume_tabel =
eveniment&coloana_sortare=eveniment_id Att af iseaza_lista_tabel(),, ct i af iseaza_tabel() folosesc funcia
af iseaza_celu-la (), o funcie utilitar care afieaz o valoare sub forma unei celule ntr-un tabel HTML. Aceast
funcie folosete un mic truc, prin care transform valorile vide ntr-un spaiu nentreruptibil (&nbsp;), deoarece,
ntr-un tabel cu chenar, celulele vide nu afieaz chenarele n mod adecvat. Inseria n celul a unui spaiu
nentreruptibil rezolv problema, af iseaza_celula() mai preia un al treilea parametru, care controleaz
codificarea sau nu a valorii din celuia. Acest lucru este necesar deoarece afiseaza_celula() este apelat pentru a
afia unele valori din celule care au fost deja codificate, cum ar fi capetele de coloan care conin informaii
despre URL.
# afieaz o valoare in celula unui tabel; insereaz spatii l # neintreruptiteile in celulele "vide" pentru a
permite afiarea chenarelor
l sub
I my
sub afieaz celula
my ($eticheta, $valoare, $codare) = @_;
Continuare
366 Partea a 11-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
$valoare = $cgi->escapeHTML ($valoare) if $codare;
$valoare = "&nbsp;" unless Svaloare;
prin "<$eticheta$valoare/$eticheta>\n"; }
Dac dorii s scriei un script mai general, putei modifica scriptul samp_browse astfel nct s parcurg mai
multe baze de date. De exemplu, scriptul poate ncepe prin a afia o list cu bazele de date din server, nu o lista
cu tabele dintr-o anumit baz de date. Apoi, putei alege o baz de date pentru a obine o list a tabelelor sale i
putei continua ncepnd din acest punct.
Browserul de parcurgere a punctajelor
din cadrul proiectului de eviden a rezultatelor colare
De fiecare data cnd introducem punctajele obinute la un test, trebuie s generm o list ordonat cu punctaje,
astfel nct s putem determina curba de notare i s putem atribui note literale. Reinei c nu vom face dect s
afim aceast list, pentru a determina unde se gsete limita de acordare a fiecrei note literale. Apoi, vom
trece notele pe lucrrile elevilor nainte de a le napoia acestora. Nu vom nregistra notele elevilor n baza de
date, deoarece notele de la sfritul perioadei de examinare se bazeaz pe punctaje numerice, nu pe note literale.
Mai reinei c, ntr-un sens strict, trebuie s dispunem de o modalitate de a introduce punctajele nainte de a crea
o metod de a le regsi. Am lsat scriptul pentru introducerea punctajelor n capitolul urmtor, ntre timp, deja
avem n baza de date numeroase seturi de punctaje, nc din prima parte a perioadei de examinare. Ne putem
folosi scriptul cu aceste punctaje, chiar n absena unei metode convenabile de introducere a punctajului.
Scriptul nostru de parcurgere a punctajelor, score_browse, prezint unele asemnri cu samp_browse, dar este
destinat scopului mai concret de examinare a punctajelor pentru un test sau pentru un chestionar dat. Pagina
iniial prezint o list a posibilelor eveni^ mente de examinare din care se poate alege i permite utilizatorului s
l selecteze pe oricare dintre acestea, pentru a vedea punctajele asociate evenimentului. Punctajele pentru un
eveniment dat sunt sortate n funcie de valoare, n ordine descresctoare, deci putei j afia rezultatul i l putei
folosi pentru a determina curba de notare.
Scriptul score_browse trebuie s examineze un singur parametru, i anume eveni-J ment_id, pentru a determina
dac a fost specificat sau nu un anumit eveniment. Dac nu,f score_browse afieaz rndurile tabelului
eveniment, astfel nct utilizatorul s poatl selecta un eveniment, n caz contrar, afieaz punctajele asociate
evenimentului ales:
# parametrul care ne indica evenimentul pentru care
# trebuie afiate punctajele
my ($eveniment_id) = $cgi-param ("eveniment_id");
# daca eveniment_id are o valoare, afieaz lista cu evenimente;
# altfel, afieaz punctajele pentru evenimentul dat.
Capitolul 7 Interfaa API pentru Perl DBI 367
if ( !$eveniment_id)
{
afiseaza_evenimente ()
}
else
{
afiseaza_punctaje ($eveniment_id) ;
}
Funcia afiseaza_evenimente() extrage informaii din tabelul eveniment i le afieaz n form tabelar, folosind
numele coloanelor din interogare drept capete de coloan pentru tabel, n cadrul fiecrui rnd, valoarea
eveniment_id este afiat sub forma unei legturi care poate fi selectat pentru a declana o interogare care
regsete punctajele corespunztoare evenimentului. Adresa URL pentru fiecare eveniment este pur si simplu
calea de acces spre scriptul score_browse, la care este ataat un parametru ce specific numrul evenimentului:
/cgi-bin/score_browse?eveniment_id=numar Funcia afiseaza_evenimente( ) se scrie astfel:
sub afiseaza_evenimente
{
my ($sth, $url);
prin "Selectai un eveniment executnd clic pe numrul sau:<BR><BR>\n";
# obine lista cu evenimente $sth = $dbh->prepare (qq{
SELECT eveniment_id, data, tip
FROM eveniment
ORDER BY eveniment_id
$sth->execute ();
# afieaz tabelul cu un chenar prin "<TABLE BORDER>\n";
# folosete numele coloanelor drept capete de coloana in tabel prin "<TR>\n";
foreach my $nume_coloana (@{$sth->{NAME}>)
{
afiseaza_celula ("TH", $nume_coloana, 1);
}
prin "</TR>\n";
# asociaz fiecare identificator de eveniment cu o legtura
# care va afia punctajele de la acel eveniment; returneaza
# rndurile folosind un hash pentru a facilita referirea la
Continuare
368 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
# valoarea coloanei eveniment_id in funcie de nume. while (my $hash_ref = $sth->fetchrow_hashref ())
{
prin "<TR>\n";
$url = $cgi->script_name();
$url .= sprintf ("?eveniment_id=%s",
$cgi->escape ($hash_ref->{eveniment_id})); afiseaza_celula ("TD", $cgi->a ({-href => $url},
$hash_ref->{eveniment_id}), 0); afiseaza_celula ("TD", $hash_ref->{data}), 1); afiseaza_celula ("TD",
$hash_ref->{tip}), 1); prin "</TR>\n";
}
$sth->finish ();
prin ("</TABLE>\n")
}
Cnd utilizatorul selecteaz un eveniment, browserul trimite o cerere pentru scriptul score_browse, la care este
ataat o valoare a identificatorului de eveniment. score_browse gsete setul de parametri eveniment_id i
apeleaz funcia af iseaza_punctaje() pentru a afia toate punctajele obinute la evenimentul specificat. De
asemenea, pagina afieaz textul "Afieaz lista cu evenimente" ca o legtur orientat napoi la pagina iniial,
astfel nct utilizatorul poate reveni cu uurin la pagina care conine lista cu evenimente. Adresa URL pentru
aceast legtur face referire la scriptul score_browse, dar nu specific nici o valoare pentru parametrul
eveniment_id. Funcia afiseaza_punctaje() este prezentat n listingul urmtor:
sub afiseaza_punctaje
{
my ($eveniment_id) = shift;
my ($sth);
# un URL fr nici un parametru eveniment_id va determina
# afiarea listei cu evenimente.
print $cgi->a ({-href => $cgi->script_name ()},
"Afieaz lista cu evenimente"); prin "<BR><BR>\n";
# selecteaz punctajele pentru evenimentul dat $sth = $dbh->prepare (qq{
SELECT
elev.nume, eveniment.data, puncte.puncte, eveniment.tip FROM
elev, puncte, eveniment
Capitolul 7 Interfaa API pentru Perl DBI 369
WHERE
elev.elev_Id = puncte. elev_id AND puncte. eveniment_id = eveniment. eveniment_id AND eveniment.
eveniment_id = ? ORDER BY
eveniment. data ASC, eveniment. tip ASC, puncte. puncte DESC } $sth->execute ($eveniment_id) ; #
transfera identificatorul
# de eveniment ca valoare
# pentru cmpul de inlocuire
prin "<B>Punctaje pentru evenimentul $eveniment_id/<B><BR>\n" ;
# afieaz tabelul cu un chenar prin "<TABLE BORDER>\n";
# utilizeaz numele coloanelor drept capete de coloana ale tabelului prin "<TR>\n";
foreach my $nume_coloana (@{$sth->{NAME}>)
{
afiseaza_celula ("TH", $nume_j;oloana, 1);
}
prin "</TR>\n";
while (my @ary = $sth->fetchrow_array ())
{
prin "<TR>\n";
afiseaza_celula ("TD", shift (@ary), 1) while @ary;
prin "</TR>\n";
$sth->finish ();
prin "</TABLE>\n"; }
Interogarea pe care o ruleaz scriptul afiseaza_punctaje() este foarte similar celei pe care am creat-o cu mult
timp n urm, n capitolul l, n seciunea Regsirea informaiilor din mai multe tabele", care prezenta metode de
scriere a unirilor, n acel capitol, am cerut punctajele obinute la o anumit dat, deoarece datele sunt mai
semnificative dect valorile identificatorilor de eveniment. Dimpotriv, cnd folosim score_browse, cunoatem
cu exactitate identificatorul de eveniment. Aceasta nu deoarece folosim ca reper identificatorul de eveniment
(ceea ce nu facem), ci fiindc scriptul ne prezint o list cu asemenea identificatori, din care trebuie s alegem.
Se poate observa c acest dp de interfa reduce necesitatea de a cunoate anumite detalii. Nu trebuie s
cunoatem un identificator de eveniment; trebuie numai s recunoatem evenimentul de care avem nevoie.
370 Pate* a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Identifier membrilor Ligii istorice care manifest interese comune
Scripturile sim[P_browse i score_browse permit utilizatorului s opereze o selecie prin prezentarea ine2i liste
cu opiuni ntr-o pagin iniial, unde fiecare opiune este o legtur care re-invo:: scriptul, de data aceasta cu o
anumit valoare a unui parametru. O alt modalitate de a- permite utilizatorilor s opteze este de a insera ntr-o
pagin un formular care coninem cmp ce poate fi editat. Acest procedeu este mai adecvat atunci cnd domeniul
opiunilor posibile nu este limitat la un set de valori uor de determinat. Urmtorul nostru script demonstreaz
aceasta metod de a solicita date de intrare de la utilizator.
n seciuneaUJtilizarea DBI", am construit un script n linie de comand pentru a gsi pe membrii Lijiilistorice
care manifest unmumit interes. Totui, membrii Ligii nu au acces la acest script; secretarul Ligii trebuie si
ruleze scriptul i apoi s expedieze prin e-mail rezultatul nemdbrului care a solicitat lista, Ar fi interesant ca
aceast posibilitate de cutare s fie pus li dispoziia mai multor membri, astfel nct acetia s o poat folosi
personal. Scrierea unniscript Web este o modalitate de a rezolva problema.
Scriptul defa:, intitulat interese, afieaz un mic formular n care utilizatorul poate introduce ti Cuvnt cheie,
iar apoi caut n tabelul membru pentru a gsi membrii corespunztori ?i afBeaz rezultatele. Cutarea se
execut adugnd caracterul de nlocuire % (procent) l ambele extremiti ale oritului cheie, astfel nct acesta
s poat fi gsit oriunde ncadflrul valorilor din coloanainterese.
Formularul ca*re folosete cuvntul cheie este afiat n fiecare pagin, astfel nct utilizatorul s patSi introduce
imediat o noua cutare, chiar din acele pagini care afieaz rezultatelecu-itrii. n plus, irul de cutare din
pagina anterioar este afiat sub form de cuvnt the -ie, astfel nct, dac utilizatorul dorete s ruleze o cutare
similar, irul s poat ficdi-tat. Astfel, nu este necesara reintroducerea unei mari cantiti de text:
# paratet rul de cutat
my ($iite res) = $cgi->param ("interes");
# Afisiazsa un formular pentru introducerea cuvintelor cheie. In plus, daca
# varifcla $interes este definita, caut si afieaz o lista a membrilor
# carenaimifesta acel interes. Observai ca valoarea curenta a variabilei!
# $intres este afiat ca valoare prestabilita a cmpului "interes"
# din formular.
prin Ujgi^starr^form (-methoi=> "POST"); prin Scgi-^extfield ( -name => "interes",
value => $interes,
size => 40);
print Scgji-^ubmit ( -name => 'buton", -value => "Caut"); print $c60i->end_f orm ();
# rulaz*a o cutare daca a fost specificat un cuvnt cheie cautajeflnbri ($interes) if Sinteres;

Capitolul 7 Interfaa API pentru Perl DBI 371


Scriptul i comunic lui nsui informaiile ntr-un mod uor diferit fa de samp_browse sau score_browse.
Parametrul interes nu este adugat la sfritul unui URL. n schimb, informaiile din formular sunt codificate de
ctre browser i trimise ca parte a unei cereri POST. Totui, pentru CGI. pm nu are nici o importan modul de
transmitere a informaiei; valoarea parametrului este obinut tot prin apelarea funciei param().
Funcia pentru executarea cutrii i pentru afiarea rezultatelor este prezentat n listin-gul urmtor. Funcia care
formateaz intrarea, f ormat_html_entry (), nu a fost prezentat, deoarece este similar celei din scriptul gen_cat:
sub cautajnembri
{
my ($interes) = shift; my ($sth, $numar);
printf "Rezultatele cutrii pentru cuvntul cheie: %s<BR><BR>\n", $cgi->escape_html ($interes);
$sth = $dbh->prepare (qq{
SELECT * FROM membru WHERE interese LIKE ? ORDER BY nume, prenume
});
# caut irul oriunde in cmpul "interese"
$sth->execute ("%" . $interes . "%");
$numar = 0;
while (my $hash_ref = $sth->fetchrow_nashref ())
{
format_html_entry ($hash_ref);
++$numar; } prin $cgi->p ("Au fost gsite $numar intrri");

CAPITOLUL o
Interfaa API pentru PHP
PHP este un limbaj de scripting ce v permite s scriei pagini Web care conin programe nglobate, programe ce
se execut la fiecare deschidere a paginii i care pot genera un coninut dinamic care va fi inclus ca parte a
datelor de ieire trimise browserului Web al unui client. Acest capitol descrie modul de utilizare a limbajului
PHP pentru a scrie aplicaii bazate pe Web care folosesc MySQL. Pentru o comparaie ntre PHP i interfeele
API pentru programarea n MySQL scrise n C, respectiv Perl DB, consultai capitolul 5, Introducere n
programarea MySQL".
Exemplele din capitolul de fa pornesc de la baza noastr de date demonstrativ, samp_db, folosind tabelele pe
care le-am creat pentru proiectul de eviden a rezultatelor colare i pentru Liga istoric n capitolul l,
Introducere n MySQL si SQL". Vom discuta despre PHP 3, dei PHP 4 se afl deja la versiunea beta n
momentul scrierii acestei cri i poate c va deveni deja disponibil pn cnd vei ajunge s citii rndurile de
fa. Compatibilitatea cu PHP 3 este unul dintre scopurile de proiectare specificate cu claritate ale versiunii PHP
4, deci aproape toate cele prezentate aici pentru PHP 3 sunt valabile i pentru PHP 4. Un set de note pentru
trecerea la PHP 4 descrie modificrile n raport cu PHP 3. Dac folosii PHP 4, trebuie s citii aceste note.
Acest capitol a fost scris pornind de la presupunerea c vei folosi PHP n conjuncie cu serverul Web Apache.
Biblioteca client pentru MySQL scris n C i fiierele antet trebuie s fie de asemenea instalate; aceste fiiere
sunt necesare atunci cnd construii PHP; n caz contrar, PHP nu va ti cum s obin accesul la bazele de date
MySQL. Dac trebuie s v procurai oricare dintre aceste programe, consultai Anexa A, Obinerea i
instalarea programelor". Instruciunile pentru obinerea scripturilor date ca exemplu, l create n acest capitol, se
gsesc de asemenea n anexa respectiv. Putei descrca scrip- l turile pentru a evita s le introducei personal de
la tastatur.
Sub UNIX, PHP se poate folosi cu Apache fie ca modul ncorporat care este legat n j fiierul binar executabil
Apache, fie ca interpreter autonom, folosit ca program CGli tradiional. Sub Windows, PHP poate rula numai ca
program autonom n momentul del fa, dei se lucreaz la dezvoltarea unui modul PHP 4 Apache, care va rula
sumj Windows NT.
n majoritatea cazurilor, acest capitol descrie funciile PHP numai n msura n care sunt necesare n expunere.
Pentru un listing mai comprehensiv al tuturor funciilor legate MySQL, vezi Anexa H, Referin API PHP". De
asemenea, probabil c vei dori consultai manualul PHP, care descrie gama complet de funcii pe care le
furnizeaz PHP, inclusiv funciile pentru utilizarea sistemelor de baze de date diferite de MySQI
Capitolul 8 Interfaa API pentru PHP 373
(PHP, ca si DBI, nu este constrns s funcioneze numai cu MySQL.) Manualul este disponibil din situl Web
PHP, la adresa http://www.php.net/. De asemenea, acest sit Web conine notele pentru trecerea de la PHP 3 la
PHP 4.
Caracteristicile scripturilor PHP
Numele fiierelor care conin scripturile PHP sunt scrise, n mod caracteristic, cu o extensie care permite
serverului dumneavoastr de Web s le recunoasc i s execute interpretorul PHP pentru a le prelucra. Dac
folosii o extensie care nu este recunoscut, scripturile dumneavoastr PHP vor fi servite n format text simplu.
Extensia folosit n acest capitol este .php. Alte extensii frecvent folosite sunt .php3 i .phtml. Pentru instruciuni
de configurare a serverului Apache astfel nct s recunoasc extensia pe care dorii s o folosii, consultai
Anexa A. Dac nu putei controla instalarea serverului Apache pe calculatorul dumneavoastr, aflai de la
administratorul de sistem care este extensia adecvat pentru a fi utilizat.
Noiuni fundamentale despre PHP
Funcia elementar a limbajului PHP este de a interpreta un script pentru a genera o pagin Web care este trimis
unui client, n mod caracteristic, scriptul conine o combinaie ntre coduri HTML, care sunt trimise literal
clientului, si linii de program PHP, care sunt executate ca program. Datele de ieire produse de program sunt
trimise clientului, indiferent de natura lor, deci clientul nu vede niciodat programul, ci numai datele de ieire
rezultante.
Cnd PHP ncepe s citeasc un fiier, pur i simplu copiaz tot ceea ce gsete acolo n datele de ieire, cu
presupunerea c fiierul conine HTML literal. Cnd interpretorul PHP ntlnete o eticheta special de
deschidere, comut din modul HTML n modul program PHP i ncepe s interpreteze fiierul drept linii de
program PHP care trebuie executate. Sfritul programului este indicat de o alt etichet special, moment n
care interpretorul comut napoi din modul program n modul HTML. Aceasta v permite s combinai un text
static (partea de HTML) cu rezultate generate dinamic (date de ieire ale programului din partea de PHP) pentru
a produce p pagin care variaz n funcie de circumstanele n care este apelat.
De exemplu, putei folosi un script PHP pentru a prelucra rezultatul unui formular n care un utilizator a introdus
parametri pentru o cutare ntr-o baz de date. Parametrii de cutare pot fi diferii pentru variante diferite de
completare a formularului, deci, arunci cnd scriptul execut cutri, fiecare pagin rezultant va reflecta o alt
cutare. S vedem cum funcione'az aceste procedeu, examinnd un script PHP extrem de simplu:
<HTML>
<BODY>
Salut, lume
</BODY>
</HTML>
Im-
374 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Acest script nu este foarte interesant, deoarece nu conine nici o linie de program PHP! La ce bun? vei ntreba.
Aceasta este o ntrebare logic. Rspunsul este c uneori este util configurarea unui script care conine numai
cadrul HTML pentru pagina pe care dorii s o generai, care apoi va fi adugat la liniile de program PHP. Este
un procedeu absolut corect i interpretorul PHP nu are nici o obiecie.
Pentru a include linii de program PHP ntr-un script, acestea se deosebesc de textul nconjurtor cu ajutorul a
dou etichete speciale, n spe ?php pentru a ncepe scriptul, respectiv ? pentru a-1 ncheia. Cnd interpretorul
PHP ntlnete eticheta de deschidere ?php, comut din modul HTML n modul program PHP si interpreteaz tot
ceea ce gsete drept linii de program PHP pn cnd vede eticheta de nchidere ?. Scriptul cuprins ntre cele
dou etichete este interpretat i nlocuit cu datele de ieire pe care le genereaz. Exemplul anterior poate fi scris
din nou, pentru a include un mic exemplu de program PHP, astfel:
<HTML>
<BODY>
<?php prin ("Salut, lume\n"); ?>
</BODY>
</HTML>
n acest caz, partea de program este redus la minimum, fiind alctuit dintr-o singur linie. Cnd programul este
interpretat, genereaz rezultatul Salut, lume, care devine parte a datelor de ieire trimise browserului clientului.
Astfel, pagina Web produs de acest script este similar celei produse n exemplul anterior, unde scriptul era
alctuit n totalitate din linii HTML.
Putei folosi linii de program PHP pentru a genera orice component a unei pagini Web. Deja am vzut una
dintre extreme, n care ntregul script era alctuit din HTML literal i nu coninea nici o linie de program PHP.
Cealalt extrem este un script alctuit n totalitate din linii de program PHP si care nu conine HTML literal:
<?php
prin ("<HTML>\n"); prin ("<BODY>\n"); prin ("Salut, lume\n"); prin ("</BODY>\n"); prin ("</HTML>\n');
Acest fapt demonstreaz c PHP v ofer o mare flexibilitate n ceea ce privete generarea! datelor de ieire.
PHP las la latitudinea dumneavoastr alegerea combinaiei celei adecvate ntre HTML i liniile de program
PHP. O alt dovad a flexibilitii limbajuk PHP const n aceea c nu este necesar s inserai toate liniile de
program ntr-un sing loc. Putei comuta ntre modul HTML si modul program PHP pe tot parcursul script lui,
oricum si oricnd dorii.
Capitolul 8 Interfaa API pentru PHP 375
Etichetele scripturilor PHP
n afar de etichetele folosite pentru exemplele din acest capitol, PHP nelege i alte etichete de script. Le putei
vedea n liniile de program PHP pe care le scriu alte persoane sau le putei folosi personal. PHP recunoate patru
stiluri de etichete:
Stilul de etichet prestabilit. Acesta este stilul pe care PHP este configurat s-l foloseasc n mod prestabilit:
<?php prin ("Salut, lume\n"); ?>
Stil cu etichet de deschidere scurt. Acesta este asemntor stilului prestabilit, cu excepia faptului c eticheta
de deschidere este mai scurt:
<? prin ("Salut, lume\n11); ?>
Stil compatibil ASP. Acest stil este comun n mediile Active Server Pages:
<% print ("Salut, lume\n"); %>
Stil de etichet SCRIPT. Acest stil este util dac folosii un editor HTML care nu agreeaz nici unul din
celelalte stiluri de etichete. Categoric se folosete mai mult text, dar stilul poate fi necesar cnd un editor nu v
accept programul PHP deoarece folosii alte stiluri de etichete:
<SCRIPT LANGUAGE="php"> prin ("Salut, lume\n"); </SCRIPT>
Stilul cu etichet de deschidere scurt i stilul compatibil ASP nu sunt activate n mod prestabilit. Pentru mai
multe instruciuni pentru activarea acestor etichete, consultai Anexa H.
Scripturi PHP autonome
Este posibil s scriei scripturi PHP autonome, pe care le putei invoca din linia de comand, aa cum procedai
cu un script de interpreter sau cu un script Perl, lat un exemplu:
#! /usr/local/bin/php -q
<?php prin ("Salut, lume\n"); ?>
Scriptul anterior poate fi denumit salut. php, poate deveni executabil folosind comanda chmod +x i poate fi
invocat din interpreter:
% salut.php
Salut, lume
n acest capitol nu vom scrie nici un script autonom. Toate exemplele au fost scrise pornind de la premisa c vor
fi invocate de ctre un server Web pentru a genera o pagin Web.
Scriptul urmtor este ceva mai substanial, dar relativ scurt, i prezint modul simplu n care putei obine acces
la o baz de date MySQL din PHP, respectiv modul n care putei folosi rezultatele unei interogri ntr-o pagin
Web. Scriptul urmtor a fost prezentat foarte succint n capitolul 5 si reprezint un fundament simplu al unei
pagini de baz a sitului Ligii istorice. Pe parcurs, vom aduga scriptului un plus de complexitate, dar, pentru
moment, nu face dect s afieze un scurt mesaj de salut si numrul actual al membrilor Ligii:
<HTML>
<HEAD>
<TITLE>Liga istorica americana</TITLE>
</HEAD>
Continuare
376 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
<BODY>
<P>Bine ai venit la situl Web al Ligii istorice.
<?php
$link = @mysql_pconnect ("pit-viper.snake.net", "paul", "secret")
or exit (); mysql_select_db ("samp_db")
or exit (); $result = mysql_query ("SELECT COUNT(*) FROM membru")
or exit (); if ($row = mysql_fetch_array ($result))
echo "<P>In prezent, Liga are " . $row[0] . " membri"; mysql_free_result ($result);
</BODY></HTML>
Mesajul de ntmpinare este numai un text static, deci cel mai uor este s-1 scriei pur i simplu sub form de
HTML literal. Pe de alt parte, numrul membrilor este dinamic i se modific n timp,'deci trebuie determinat
instantaneu prin interogarea tabelului membru din baza de date samp_db.
Textul programului delimitat de etichetele de deschidere i de nchidere a scriptului execut o operaie simpl.
Mai nti, deschide o conexiune cu serverul MySQL i transform samp_db n baz de date prestabilit. Apoi,
trimite o interogare serverului pentru a determina numrul de membri ai Ligii istorice din momentul respectiv
(pe care l evalum ca fiind numrul de rnduri din tabelul membru). Rezultatul interogrii este afiat ca parte a ,
unui mesaj care conine numrul de membri, iar apoi este eliminat.
Dac n orice moment al acestui proces se produce vreo eroare, scriptul i ncheie pur si simplu execuia. Nu
sunt generate nici un fel de date de ieire privind eroarea produsfl deoarece acestea pot fi derutante pentru cei
care viziteaz situl Web.1
S defalcm scriptul pe componente, pentru a vedea care este modul de funcionare an
fiecreia. Prima etap const n conectarea la server folosind funcia mysql_pconnect (yll
$link = @mysql_pconnect '("pit-viper.snake.net", "paul", "secret")
or exit ();
Funcia mysql_pconnect() preia ca argumente numele gazdei, numele utilizatorului sil parola si returneaz un
identificator de legtur n cazul n care conexiunea a fost sta-1 bilit cu succes, respectiv FALSE dac se
produce vreo eroare. Dac nu reuete ncercarel de conexiune, scriptul nostru apeleaz funcia exit (), care
termin imediat execuii scriptului, caz n care nu mai sunt generate alte date de ieire.
1 Dac generai ntreaga pagin Web prin intermediul liniilor de program PHP, oprirea programului caz de
eroare fr afiarea nici unor date de ieire i poate agasa pe cei care v citesc paginile, de unele browsere vor
afia o caset de dialog cu mesajul aceast pagin nu conine nici un fel de datc*| caset care trebuie eliminat. -
N.A.
Capitolul 8 Interfaa API pentru PHP 377
Dar ce nseamn caracterul din faa apelului la funcia mysql_pconnect() ? Acesta este caracterul Linite, v
rog". Unele funcii PHP scriu un mesaj de eroare n caz de eec, pe lng returnarea unui cod de stare, n cazul
funciei mysql_pconnect(), o ncercare ratat de conexiune determin apariia n pagina Web care este trimis
ctre browserul clientului a unui mesaj ca acesta:
Warning: MySQL Connection Failed: Access denied for user:
'paul@pit-viper.snake.net' (Using password: YES)2.
Asta nu-i frumos; persoana care ne viziteaz situl nu va ti cum s procedeze cu acest mesaj. Inseria caracterului
@ n faa apelului la funcia mysql_pconnect() elimin acest mesaj de eroare, astfel nct s putem alege singuri
modul de tratare a erorilor, pe baza valorii returnate. Pentru scriptul curent, cel mai bun lucru n cazul unei erori
este de a nu genera nici un fel de date de ieire legate de numrul membrilor, n acest caz, pagina va conine
numai mesajul de ntmpinare. ,.
Putei folosi caracterul @ cu orice funcie PHP> dar, dup experiena mea, apelul iniial la funcia
mysql_pconnect( )are cele mai mari anse s eueze; ca atare, exemplele din acest capitol elimin numai
mesajele generate de funcia respectiv.
Poate c nu v convine c numele i parola sunt nglobate n script, n vzul tuturor. Ar cam fi cazul... Este
adevrat c numele i parola nu apar n pagina Web care este transmis clientului, deoarece coninutul scriptului
este nlocuit de datele sale de ieire. Totui, dac serverul Web sufer o eroare de configurare si nu reuete s
recunoasc faptul c scriptul dumneavoastr trebuie prelucrat de PHP, va trimite scriptul sub form de text
simplu, iar parametrii dumneavoastr de conexiune vor fi expui. Vom aborda n curnd aceast problem, n
seciunea Utilizarea funciilor si a fiierelor include".
Identificatorul de legtur returnat de funcia mysql_pconnect () poate fi transferat ctre alte numeroase apeluri
la funcii conexe MySQL din interfaa API pentru PHP. Totui, pentru asemenea, apeluri, identificatorul este
ntotdeauna opional.
mysql_pconnect()sau mysql_connect()
O funcie care este similar cu mysql_pconnect () este mysql_connect (). Ambele preiau ca argumente numele de
gazd, numele utilizatorului i parola i ambele returneaz FALSE pentru a indica reuita sau eecul ncercrii de
conexiune. Diferena dintre cele dou apeluri este c mysql_pcon-nect () stabilete o conexiune persistent, n
timp ce mysql_connect () stabilete o conexiune non-persistent. O conexiune persistent difer de o conexiune
non-persistent n sensul c prima nu este nchis la terminarea scriptului. Dac un alt script PHP este executat
ulterior de acelai proces Apache copil i apeleaz funcia mysql_pconnect () cu aceleai argumente, conexiunea
este refolosit. Acest procedeu este cu mult mai eficient dect stabilirea fiecrei conexiuni pornind de la zero.
De exemplu, putei apela mysql_select_db() folosind oricare din urmtoarele forme: mysql_select_db
($db_name, $link); mysql_select_db ($db_name);
2 n traducere Avertisment: conexiune MySQL ratat: accesul interzis pentru utilizatorul... (Folosete parol:
DA). - N.T.
378 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Dac omitei argumentul link din orice apel PHP legat de MySQL care preia un asemenea argument, apelul
folosete conexiunea deschis cel mai recent. Astfel, dac scriptul' dumneavoastr deschide numai o singur
conexiune, nu trebuie s specificai niciodat n mod explicit un argument link n nici unul din apelurile
dumneavoastr MySQL - conexiunea respectiv va fi cea prestabilit. Situaia este complet diferit de
programarea n MySQL folosind interfeele API n C sau DBI, pentru care nu exist o asemenea valoare
prestabilit.
Am scris programul de conexiune n scriptul nostru de creare a unei pagini de baz simple dup cum se poate
vedea mai jos, pentru a clarifica tipul de valoare returnat de funcia mysql_pconnect():
$link = @mysql_pconnect ("pit-viper.snake.net", "paul", "secret")
or exit ();
Totui, nu folosim parametrul $link nicieri n cadrul scriptului, deci programul ar fi putut fi scris mai simplu
astfel:
@mysql_pconnect ("pit-viper.snake.net", "paul", "secret")
or exit ();
Presupunnd c stabilirea conexiunii a reuit, urmtoarea etap este selectarea unei baze de date:
mysql_select_db ("samp_db-)
or exit {);
Dac apelul la funcia mysql_select_db() eueaz, scriptul i ncheie execuia n tcere. O eroare este
improbabil n acest moment, dac am reuit s ne conectm la server i dac baza de date exist, dar este
prudent s verificm apariia eventualelor probleme i s lum msurile adecvate. Dup ce ai selectat baza de
date, putem trimite interogarea spre server, extragem rezultatul, l afim i eliberm memoria alocat setului de
rezultate: $result = mysql_query ("SELECT COUNT(*) FROM membru")
or exit (); if ($row = mysql_fetch_array ($result))
echo "<P>In prezent, Liga are " . $row[0] . " membri"; mysql_free_result ($result);
Funcia mysql_query() trimite interogarea la server pentru a fi executat. Interogarea nitJ trebuie s se termine cu
un caracter punct i virgul i nici cu grupul de caractere \q?'. Funcia mysql_query() returneaz FALSE dac
interogarea a fost ilegal sau dac nu ^\ putut fi executat dintr-un motiv oarecare, n caz contrar, returneaz un
identificator s setului de rezultate. Acest identificator este o valoare pe care o putem folosi pentru obine
informaii despre setul de rezultate. Pentru interogarea noastr, setul de rezultai | const dintr-un singur rnd cu
o singur valoare din coloan, reprezentnd numrul membri. Pentru a obine aceast valoare, transferm
identificatorul setului de rezultat funciei mysql_fetch_array() pentru a prelua rndul, atribuim rndul variabilei
$row sil obinem accesul la primul element (care se ntmpl s fie si singurul) sub forma $row[0].f
Cnd am terminat cu setul de rezultate, eliberm memoria alocat acestuia transferndu-funciei mysql_f
ree_result(). De fapt, acest apel nu este necesar n scriptul nost deoarece PHP elibereaz automat memoria
alocat tuturor seturilor de rezultate active l terminarea execuiei unui script.
Capitolul 8 Interfaa API pentru PHP 379
Funcia mysql_free_result() este util mai ales n scripturile care execut interogri foarte mari sau un numr
mare de interogri i previne utilizarea unei cantiti excesive de memorie.
Pentru a folosi scriptul nostru, trebuie s-1 instalm undeva, n acest capitol, am adoptat convenia prin care Liga
istoric american i are propriul su catalog, denumit lisua3, situat la nivelul cel mai de sus al arborelui de
documente Apache, deci scriptul paginii de baz poate fi instalat sub forma lisua/index.php n arborele respectiv.
Vom crea scripturi si pentru proiectul de eviden a rezultatelor colare, deci i vom atribui acestuia catalogul
evid. Dac gazda silului Web este pit-viper.snake.net, paginile din aceste dou cataloage au adrese URL care
ncep astfel:
http://pit - viper.snake.net/lisua/
http://pit-viper.snake.net/evid/
De exemplu, paginile de baz din fiecare catalog pot fi denumite index. php i pot fi accesibile astfel:
http://pit-viper.snake.net/lisua/index.php
http://pit-viper.snake.net/evid/index.php
Utilizarea funciilor i a fiierelor include
Scripturile PHP difer de scripturile DBI, prin aceea c scripturile PHP sunt localizate n arborele cu documente
al serverului dumneavoastr de Web, n timp ce scripturile DBI se gsesc, n mod caracteristic, ntr-un catalog
cgi-bin care se afl n afara arborelui cu documente. Astfel, apare o problem de securitate: o eroare de
configurare a serverului poate determina afiarea accidental pentru clieni, sub form de text simplu, a paginilor
localizate n arborele cu documente. Aceasta nseamn c numele de utilizator i parolele pentru stabilirea
conexiunilor cu serverul MySQL sunt ntr-un pericol mai mare de a fi expuse lumii exterioare dac sunt folosite
ntr-un script PHP dect dac se folosesc ntr-un script DBI.
Variabile n PHP
n PHP putei declara variabile prin simpla utilizare a acestora. Scriptul nostru de creare a unei pagini de baz
folosete trei variabile, i anume $link, $result i $row, din care nici una nu este declarat nicieri. (Exist
contexte n care trebuie s declarai variabilele, cum ar fi atunci cnd facei referire la o variabil global n
interiorul unei funcii, dar despre aceasta vom discuta ulterior.)
Variabilele sunt simbolizate printr-un identificator precedat de simbolul dolarului ($). Aceast notaie este
valabil indiferent de valoarea pe care o reprezint variabila, dei pentru tablouri i obiecte trebuie s mai
adugai i alte elemente pentru a obine accesul la elementele individuale ale unei valori. Dac o variabil $x
reprezint o singur valoare, cum ar fi un numr sau un ir, se poate obine acces la aceasta sub forma $x. Dac
$x reprezint un tablou cu indici numerici, elementele sale sunt accesibile sub forma $x[O], $x[ 1 ] etc. Dac $x
reprezint un tablou cu indici asociativi precum "galben" sau "mare", elementele sale sunt accesibile sub forma
$x [' galben" ] sau $x [" mare" J.
Tablourile PHP pot ayea att elemente numerice, ct i elemente asociative. De exemplu, att $x [ 1 ] ct i $x ["
m re" ] pot reprezenta elemente ale aceluiai tablou. Dac $x reprezint un obiect, are proprieti care sunt
accesibile sub forma $x-nume_proprietate. De exemplu, $x-galben i $x-mare pot fi proprieti ale variabilei $x.
Numerele nu se pot folosi ca nume de proprieti, deci $x-1 nu este o construcie corect n PHP.
' Abreviere personal pentru Liga Istoric a Statelor Unite ale Americii. - N.T.
380 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Scriptul nostru iniial pentru construcia paginii de baz a Ligii istorice este expus la aceast problem, deoarece
conine valorile literale ale numelui de utilizator si ale parolei MySQL. S mutm aceti parametri de conexiune
n afara scriptului, folosind dou dintre caracteristicile limbajului PHP: funcii i fiiere include. Vom scrie o
funcie samp_db_connect () pentru a stabili conexiunea, respectiv vom plasa funcia mr-un fiier include - un
fiier care nu face pane din scriptul nostru principal, dar la care se poate face referire din acesta. Unele avantaje
ale aceste abordri sunt:
Programul de stabilire a conexiunii este mai uor de scris. Nu avem nevoie s scriem toi parametrii i putem
cere funciei samp_db_connect () s selecteze baza de date automat dup conectare, astfel nct o funcie s
execute atribuiile a dou funcii PHP. Astfel, scripturile devin mai uor de neles, deoarece atenia
dumneavoastr se poate concentra asupra aspectelor caracteristice ale scriptului, fr a fi deturnat de programul
de configurare a conexiunii.
Fiierul include este accesibil din scriptul nostru, dar poate fi deplasat n afara arborelui cu documente Apache.
Ca atare, coninutul su devine inaccesibil clienilor i acetia nu mai pot avea acces la parametrii de conexiune,
chiar dac serverul Web sufer o eroare de configurare. Utilizarea unui fiier include este o bun strategie pentru
ascunderea oricror categorii de informaii importante, care nu dorii s fie trimise n afara sitului de ctre
serverul dumneavoastr de Web.
Aceasta nu nseamn, totui, c numele si parola sunt sigure din toate punctele de vedere. Utilizatorii care pot
deschide o sesiune de lucru la gazda serverului Web pot fi capabili s citeasc fiierul include n mod direct, dac
nu luai nici o msur de precauie. Consultai seciunea Conectarea la serverul MySQL din scripturile Web" din
capitolul 7, Interfaa API pentru Perl DBI" i reinei msurile de precauie descrise acolo pentru instalarea
fiierelor de configurare DBI astfel nct acestea s fie protejate de ali utilizatori. Aceleai msuri de precauie
se aplic i n cazul utilizrii fiierelor < include din PHP.
Influente lingvistice n PHP
Dac avei experien cu programarea n C, probabil ai observat c numeroase construcii sintactice din j scriptul
nostru sunt foarte similare celor pe care le folosii pentru programarea n C. De fapt, sintaxa PHfH deriv n mare
msur din C, deci asemnarea nu este o coinciden. Dac avei oarecare cunotine de Cv \ le vei putea
transfera n mare msur n PHP. De fapt, dac nu suntei sigur cu privire la modul de scriegf | a unei expresii sau
structuri de control n PHP, ncercai s o scriei n C i este posibil s fie corect. 1f
Dei PHP deriv cu precdere din C, sunt prezente i elemente de Java i Perl. Putei constata acest lucrtf ] din
sintaxa comentariilor, care permite oricare din urmtoarele trei forme:
# Comentariu in stil Perl.de la # pana la sfritul liniei
// Comentariu in stil C++ sau Java, de la // pana la sfritul liniei
/* Comentariu in stil C, intre slash-stea pana la sea-slash */
Alte asemnri cu Perl includ operatorul . de concatenare a irurilor (inclusiv .= drept operator de i catenare
aditiv), precum i modalitatea n care referinele la variabile i secvenele escape sunt in pretate n interiorul
ghilimelelor duble, dar nu i n interiorul ghilimelelor simple.
Capitolul 8 Interfaa API pentru PHP 381
Fiierul include poate fi folosit n mai multe scripturi. Astfel este promovat reutilizarea programelor, iar liniile
de program devin mai uor de ntreinut. De asemenea, permite efectuarea cu uurin de modificri globale n
fiecare script care obine acces la fiier. De exemplu, dac mutm baza de date samp_db de la pit-viper la boa, nu
trebuie s modificm o mulime de scripturi individuale, ci numai argumentul numele gazdei" din apelul la
funcia mysql_pconnect() din fiierul include care conine funcia samp_db().
Pentru a folosi fiiere include, trebuie s avei unde s le punei i trebuie s cerei limbajului PHP s le caute.
Dac sistemul dumneavoastr dispune deja de o asemenea locaie, o putei folosi. Dac nu, folosii urmtoarea
procedur pentru a stabili o locaie a fiierului include:
1. Creai un catalog n care vei stoca fiierele PHP include. Acest catalog nu trebuie s se afle n interiorul
arborelui cu documente al serverului Web! Eu folosesc un catalog include pentru PHP /usr/local/apache/php, care
se afl la acelai nivel ca i arborele meu cu documente (/usr/local/apache/htdocs), dar nu n interiorul acestuia.
2. Creai referine la fiierele include cu numele complet al cii de acces sau indicai limbajului PHP cataloagele
n care s le caute. Ultima metod este mai convenabil, deoarece, dac folosim numele de baz al fiierului,
PHP l va gsi automat.4 Pentru a indica limbajului PHP unde s caute, modificai fiierul de iniializare PHP
(/usr/local/lib/php3.ini n sistemul meu) pentru a schimba valoarea parametrului include_path. Dac acesta nu are
nici o valoare, atribuii-i calea de acces complet a noului catalog include:
include_path = "/usr/local/apache/php"
Dac include_path are deja o valoare, adugai noul catalog la valoarea respectiv: include_path =
"valoare_curenta:/usr/local/apache/php"
3. Creai fiierul include pe care dorii s-1 folosii si inserai-1 n catalogul include. Fiierul trebuie s aib un
nume distinctiv; pentru noi, numele samp_db. inc este suficient. Coninutul fiierului este prezentat n listingul
urmtor. Pentru scripturile pe care le scriem aici, cnd ne conectm la serverul MySQL, vom folosi ntotdeauna
baza de date samp_db, deci funcia de conexiune samp_db_connect () poate selecta automat acea baz de date.
Funcia returneaz un identificator de legtur dac reuete s se conecteze i dac selecteaz baza de date,
respectiv FALSE dac se produce vreo eroare. Funcia nu afieaz nici un mesaj dac se produce o eroare, ceea
ce permite apelantului s-i ncheie discret execuia sau s afieze un mesaj, n funcie de circumstane:
<?php
# samp_db.inc
# funciile comune ale bazei de date demonstrative samp_db
# se conecteaz cu serverul MySQL folosind numele si parola
# noastr ultrasecrete function sarop_db_connect ()
Continuare
[ Fiierele include dii? PHP sunt oarecum asemntoare cu fiierele antet din C, inclusiv sub aspectul c PHP le
va cuta n numeroase cataloage, exact aa cum pre-procesorul C caut fiierele antet C n mai multe cataloage. -
N.A.
382 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
i
$link = @my sql_pconnect(" pit -viper. snake. net ", "paul", "secret"); if (Slink && mysql_select_db
("samp_db"))
return ($link); return (FALSE);
Observai c fiierul samp_db.inc este delimitat prin etichetele Ophp si ?>. Aceasta deoarece PHP ncepe citirea
fiierelor include n modul HTML Fr etichete, PHP va trimite fiierul ca text simplu, fr a-1 interpreta drept
program PHP. Dac intenionai s includei cod HTML literal n fiier, nici o problem. Dar, dac fiierul conine
linii de program PHP, trebuie s delimitai codul folosind etichetele de script.
4. Pentru a face referire la un fiier dintr-un script, folosii o linie a aceasta: include ("samp_db.inc") ;
Cnd PHP vede linia respectiv, caut fiierul i i citete coninutul. Toate componentele fiierului devin
accesibile pentru componentele urmtoare ale scriptului.
Dup ce am configurat fiierul nostru include samp_db . inc, puten modifica pagina de baz a Ligii istorice astfel
nct s fac referire la fiierul includea s se conecteze la serverul MySQL apelnd funcia samp_db_connect( ):
<HTML>
<HEAD>
<TITLE>Liga istorica americana</TITLE>
</HEAD>
<BODY>
<P>Bine ai venit in situl Web al Ligii istorice.
<?php
include ("samp_db.inc");
samp_db_connect ( )
or exit () ; $result = mysql_query ("SELECT COUNT(*) FROM membru")
or exit () ; if ($row = mysql_fetch_array ($result))
include () sau require ()
PHP are o funcionalitate require ( ) care este similar cu include ( ) . Owebirea ntre ele este < n cazul funciei
include ( ) , fiierul este citit i evaluat la fiecare execuie a iistruciunii include ( ) . l cazul funciei require (),
fiierul este prelucrat o singur dat (de fapt, cottutul fiierului nlocuie instruciunea require ()). Aceasta
nseamn c, dac dorii s citii un alt fijir de fiecare dat cnd i executai programul sau avei un ciclu care
parcurge un set de fiiere, avtinevoie de include (J deoarece putei configura o variabil cu numele fiierului pe
care dorii s-l iridei i s folosii vri la ca argument al funciei include ( ) .
lHji&^M'.-t:V-,-u^-::^xai',;;.-,,^.,VX,_
r
echo "<p>in prezent, Liga are mysql_free_result ($result);
Capitolul 8 Interfaa API pentru PHP " . $row[0] . " membri";
383
</BODY></HTML>
Poate credei c nu am economisit un numr chiar att de mare de linii de program n pagina de baz folosind un
fiier include. Ateptai puin... Fiierul samp_db.inc va fi util i pentru alte funcii si l putem folosi ca un
depozit convenabil pentru diverse alte lucruri. De fapt, mai putem crea chiar acum alte dou funcii care vor fi
inserate n fiierul respectiv. Fiecare script pe care l scriem va genera un set oarecum stereotip de etichete
HTML la nceputul paginii, respectiv un alt set la sfritul acesteia, n loc de a le scrie literal n fiecare script,
putem scrie funcii html_begin ( ) si html_end ( ) care scriu etichetele automat. Funcia html_begin() poate
prelua dou argumente, care specific titlul paginii si antetul acesteia. Codul pentru cele dou funcii se prezint
astfel:
# Scrie etichetele HTML iniiale pentru pagina. Se presupune
# ca eventualele caractere speciale din variabilele $titlu si
# $antet, daca exista, sunt codificate in mod corespunztor.
function html_begin ($titlu, $antet)
{
prin ("<HTML>\n"); prin ("<HEAD>\n"); if (Stitlu)
prin ( "<TITLE>$titlu*/TITLE>\n" ) ; prin ("</HEAD>\n"); prin ("<BODY>\n"); if ($antet)
prin ("<H2>$antet</H2>\n");
# scrie etichetele HTML finale ale paginii.
function html_end ()
{
prin ("</BODY></HTML>\n");
}
Apoi, putem modifica pagina de baz a Ligii istorice astfel nct s foloseasc cele dou noi funcii si s aib ca
atare urmtorul aspect:
<?php
include ( " samp_db . inc " ) ;
$titlu = "Liga istorica americana";
html_begin ($titlu, $titlu);
?>
Continuare
384 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare
<P>Bine ai venit in situl Web al Ligii istorice.
<?php samp_db_connect ()
or exit (); $result = mysql_query ("SELECT COUNT(*) FROM membru")
or exit (); if ($row = mysql_fetch_array ($result))
echo "<P>In prezent, Liga are " . $row[0] . " membri"; mysql_free_result ($result);
html_end ();
?>
Observai c programul a fost mprit n dou componente, iar textul HTML literal al mesajului de ntmpinare
este inserat ntre cele dou pri.
Utilizarea funciilor pentru generarea componentelor iniial i final ale paginii ne ofer o posibilitate
important. Dac dorim s modificm aspectul antetului sau al subsolului tuturor paginilor care folosesc aceste
funcii, putem include pur i simplu n funcii unele linii de program. Fiecare script care va folosi funciile va fi
automat afectat. De exemplu, putei dori s scriei un mesaj Drepturile de autor rezervate LISUA" n partea de
jos a fiecrei pagini din situl Web al Ligii istorice. O funcie de final de pagin, cum este html_end (), constituie
o modalitate simpl de a face aceast schimbare.
O pagin cu o interogare simpl
Scriptul pe care 1-am nglobat n pagina de baz a silului Ligii istorice ruleaz o interogare , care returneaz un
singur rnd. Urmtorul nostru script prezint modul de prelucrare a \ unui set de rezultate compus din mai multe
linii. Scriptul preia i afieaz coninutul tabelu- ] lui membru. Acesta este echivalentul PHP al scriptului DBI
dumpjnembers pe care 1-am creatj n capitolul 7, deci l vom denumi dumpjnembers. php. Acest script este
diferit de versiunea i DBI prin aceea c este destinat utilizrii ntr-un mediu Web, nu de la linia de comand.
Din\| acest motiv, trebuie s produc date de ieire n format HTML, nu doar s scrie un simplul text delimitat
prin tabulatori. Pentru a alinia n mod estetic liniile si coloanele, vom seriei nregistrrile membrilor ntr-un tabel
HTML. Scriptul se prezint astfel:
<?php
# dumpjnembers.php - afieaz lista membrilor Ligii istorice
include ("sampjdb.inc");
$titlu = "Lista membrilor Ligii istorice americane"; html_begin (Stitlu, $titlu);
sampjdb_connect ()
or die ("Nu se poate conecta la server");
Capitolul 8 Interfaa API pentru PHP 385
# emite interogarea
Squery = "SELECT nume, prenume, sufix, email,"
. "strada, ora, stat, cod_postal, telefon"
. "FROM membru ORDER BY nume"; $result = mysql_query ($query)
or die ("Nu poate executa interogarea");
prin ("<TABLE>\n");
# citete rezultatele interogrii, apoi face curenie while ($row = mysql_fetch_row ($result))
{
prin ("<TR>\n");
for ($i = 0; $i < mysql_num_fields (Sresult); $i++)
{
# modifica semnificaia caracterelor speciale si afieaz printf ("<TD>%s</TD>\n", htmlspecialchars ($row(i]));
}
prin ("</TR>\n");
}
mysql_free_result ($result);
prin ("</TABLE>\n");
html_end ();
?>
Acest script folosete funcia die() pentru a afia un mesaj si pentru a termina execuia scriptului dac se produce
vreo eroare.5 Aceasta este o alt modalitate de tratare a erorilor dect aceea folosit n pagina de baz a Ligii
istorice, unde execuia scriptului s-a ncheiat n tcere, n scriptul dumpjnembers.php, ne ateptm s vedem un
anumit rezultat, deci este logic s afim un mesaj de eroare prin care se indic apariia unei probleme.
Scriptul poate fi instalat n catalogul lisua si este accesibil sub forma http://pi.t-
viper.snake.net/lisua/dump_members.php. Apoi, putem aduga o legtur la noul nostru script n pagina de baz
a Ligii istorice, astfel nct publicul s fie la curent cu acesta:
<?php
include ("samp_db.inc");
$titlu = "Liga istorica americana"; html_begin ($titlu, $titlu);
<P>Bine ai venit la situl Web al Ligii istorice.
Continuare
1 Funcia die () este similar cu funcia exit (), dar afieaz un mesaj nainte de a-i ncheia execuia. - N.A.
386 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Continuare <?php samp_db_connect ()
or exit (); $result = mysql_query ("SELECT COUNT(*) FROM membru")
or exit (); if ($row = mysql_fetch_array ($result))
echo "<P>In prezent, Liga are " . $row[0] . " membri"; mysql_free_result (Sresult);
Putei consulta catalogul cu membri <A HREF="dump_members.php">aici</A>.
<?php html_end ();

Prelucrarea rezultatelor interogrii


n aceast seciune, vom examina mai detaliat modul de executare a interogrilor j MySQL si manipularea
seturilor de rezultate, n PHP, toate interogrile sunt emise prin j apelarea funciei mysql_query ( ), care preia ca
argumente un ir de interogare i un iden- tificator de legtur. Identificatorul de legtur este opional, deci
pufcei invoca funcial mysql_query ( ) folosind oricare dintre urmtoarele forme:
$result = mysql_query ($query, link); # folosete conexiunea
# explicita $result = mysql_query ($query); #foloseste conexiunea
# prestabilita
Pentru interogrile care nu returneaz rnduri (interogri de tip non-SELECT, prec DELETE, INSERT,
REPLACE si UPDATE), funcia mysql_query() returneaz TRUE sau FALS pentru a indica reuita sau eecul
interogrii. Pentru o interogare reuit, putei apeli funcia mysql_af f ected_rows ( ) pentru a determina cte
rnduri au fost modifica (adic terse, inserate, nlocuite sau actualizate, dup caz).
Pentru instruciunile SELECT, mysql_query() returneaz un identificator al setului rezultate sau FALSE pentru a
indica reuita, respectiv eecul interogrii. Pentru o int gare reuit, folosii identificatorul setului de rezultate
pentru a obine alte informat; despre setul de rezultate. De exemplu, putei afla numrul de rnduri i de coloane
< setul de rezultate, respectiv putei prelua rndurile incluse n set.
Cnd mysql_query() returneaz FALSE (adic zero), nseamn c o interogare a euai cu alte cuvinte, s-a
produs o eroare i interogarea nu a putut fi nici mcar executat. interogare poate eua din mai multe motive:
Capitolul 8 Interfaa API pentru PHP 387
Poate conine malformaii sau o eroare de sintax.
Interogarea poate fi corect sub aspect sintactic, dar poate fi lipsit de sens din punct de vedere semantic, cum
ar fi ncercarea de a selecta dintr-un tabel o coloan pe care acesta nu o conine.
Nu avei suficiente privilegii pentru a efectua interogarea.
Gazda serverului MySQL a devenit inaccesibil, datorit unor probleme n reea.
n fiecare din aceste cazuri (mai sunt i altele), funcia mysql_query () returneaz FALSE. Dac dorii s
cunoatei motivul erorii, apelai una din funciile mysql_error() sau mysql_errno() pentru a obine irul
mesajului de eroare sau codul numeric al erorii. (Vezi Tratarea erorilor".)
Dou din cele mai comune greeli care se comit n utilizarea funciei mysql_query () sunt aceea de a crede c
valoarea returnat este un numr de rnduri, respectiv c valoarea returnat conine datele returnate de
interogarea dumneavoastr. Nici una din aceste afirmaii nu este adevrat.
Tratarea interogrilor care nu returneaz nici un set de rezultate
Programul urmtor folosete funcia DELETE pentru a ilustra modul de prelucrare al unei interogri care nu
returneaz nici un rnd:
$result = mysql_query ("DELETE FROM membru WHERE membru_id = 149");
if (!$result)
print ("interogarea a esuat\n");
else
printf("numr de rnduri terse: %d\n", mysql_affected_rows ()); Dac exist un membru al crui identificator
este 149, MySQL terge nregistrarea i funcia mysql_query () returneaz TRUE. Ce se ntmpl, ns, dac un
asemenea membru nu exist? n acest caz, mysql_query () returneaz tot TRUE! Acest fapt i va surprinde pe
aceia care persist n greeala de a crede c valoarea returnat de mysql_query () este un numr de rnduri. Nu
este. Valoarea returnat este TRUE n ambele situaii deoarece interogarea este corect, indiferent dac terge sau
nu vreun rnd. Numrul de rnduri afectat de interogare este cu totul si cu totul altceva. Pentru a obine valoarea
respectiv dup o interogare reuit, apelai funcia mysql_affected_rows().
Tratarea interogrilor care returneaz un set de rezultate
Exemplul urmtor furnizeaz o schi general a modului de prelucrare a interogrilor SELECT:
Sresult = mysql_query ("SELECT * FROM membru");
if (!$result)
prin ("interogarea a esuat\n");

else {
printf ("numrul de rnduri returnate: %d\n" mysql_num_rows (Sresult));
Continuare

388 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL


Continuare
while ($row = mysql_fetch_row (Sresult))
{
for ($i = 0; $i < mysql_num_fields ($result); $i++)
{
if ($i > 0)
prin (V); prin ($row[$i]);
}
prin ("\n");
}
mysql_free_result (Sresult);
Nu presupunei ca mysql_query ( ) va reui
n lista de coresponden referitoare la PHP, utilizatorii nceptori de PHP ntreab de ce apare urmtorul mesaj
de eroare la execuia unui script:
Warning: O is not a MySQL result index in file on line n
Acest mesaj arat c o valoare egal cu zero a identificatorului setului de rezultate a fost transferat unei funcii
oarecare (cum ar fi o funcie de preluare a rndurilor) care presupune un set de rezultate valid. Aceasta nseamn
c un apel anterior la my sql_query ( ) a retumat zero, adic FALSE. Cu alte cuvinte, interogarea mysql_query ( )
a euat si autorul scriptului nu s-a deranjat s verifice valoarea returnat nainte de a o folosi la alte funcii. Cnd
folosii mysql_query ( ), testai ntotdeauna valoarea returnat.
Dac interogarea eueaz, rezultatul este FALSE i afim un mesaj n acest sens. (Este posibil s existe i alte
rspunsuri mai adecvate la apariia unei erori, n funcie de cir' cumstane.) Dac interogarea reuete, funcia
mysql_query ( ) returneaz un identificator al setului de rezultate. Aceast valoare returnat este util n anumite
moduri (dar nu ca numr de rnduri!). Identificatorul setului de rezultate poate fi folftsit n oricare din
urmtoarele scopuri:
Se transfer funciei mysql_num_rows ( ) pentru a determina numrul de rnduri din. setul de rezultate.
Se transfer funciei mysql_num_f ields ( ) pentru a determina numrul de coloane di$ setul de rezultate.
,1
Se transfer unei rutine de preluare a rndurilor, pentru a prelua rnduri succesive din j setul de rezultate.
Exemplul nostru folosete funcia mysql_f etch_row( ), dar exist i alte opiuni, despre care vom discuta curnd.
Se transfer funciei mysql_free_result(), pentru a elibera memoria alocat setului rezultate si pentru a permite
limbajului PHP s elibereze toate resursele alocate ao
PHP furnizeaz numeroase funcii de preluare a rndurilor pentru regsirea unui set di rezultate dup ce funcia
mysql_query ( ) a executat cu succes o interogare SELECT (vi tabelul 8.1). Fiecare din aceste funcii preia un
identificator al setului de rezultate argument si returneaz FALSE atunci cnd nu mai exist rnduri.
Capitolul 8 Interfaa API pentru PHP 389
Tabelul 8.1 Funcii PHP de preluare a rndurilor
Numele funciei Valoare retumat
mysql_f etch_row() Un tablou; elementele sunt accesibile prin intermediul indicilor numerici
mysql_f etch_array () Un tablou; elementele sunt accesibile prin intermediul indicilor numerici sau
asociativi
mysql_f etch_ob j ect () Un obiect; elementele sunt accesibile prin intermediul proprietilor
Cel mai elementar apel este cel la funcia mysql_f etch_row(), care returneaz urmtorul rnd al setului de
rezultate sub forma unui tablou. Elementele tabloului sunt accesibile cu ajutorul indicilor numerici cu valori
cuprinse ntre O i mysql_num_fields() - 1. Exemplul urmtor prezint modul de utilizare a funciei
mysql_fetch_row() ntr-un ciclu simplu, care preia i afieaz valorile din fiecare rnd:
$query = "SELECT * FROM preedinte";
$result = mysql_query ($query) or die ("Interogarea a euat");
while ($row = mysql_fetch_row ($result))
{
for ($i = 0; $i < mysql_num_fields ($result); $i++)
{
if ($i > 0)
prin C\f); prin ($row[$i]);
}
prin C\n")j
}
Variabila $row este un tablou. Elementele din acest tablou sunt accesibile sub forma $row[$i], unde $i este
indexul numeric al coloanei. Dac suntei familiarizat cu funcia count () din PHP, putei fi tentat s o folosii
pentru a determina numrul de elemente din tablou n loc de a folosi mysql_num_fields(). Nu o facei. Funcia
count () numr numai elementele din tablou care sunt configurate, iar PHP nu configureaz elemente care
corespund unor valori din coloane egale cu NULL. Funcia count () este o msur inadecvat a numrului de
coloane returnate, deoarece funcia nu are aceast finalitate. Consideraia este valabil si pentru celelalte dou
funcii de preluare a rndurilor.
Cea de-a doua funcie de preluare' a rndurilor prezentat n tabelul 8.1, si anume mysql_f etch_array (), este
similar cu mysql_f etch_row(), cu deosebirea c elementele din tabloul pe care l returneaz sunt accesibile att
cu ajutorul indexului numeric, ct i prin intermediul indexului asociativ. Cu alte cuvinte, putei obine acces la
elemente att prin numr, ct si dup nume:
$query = "SELECT nume, prenume FROM preedinte";
Sresult = mysql_query ($query) or die ("Interogarea a euat");
while ($row = mysql_fetch_array ($result))
Continuare
390 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL Continuare
printf ("%s %s\n"), $row[0], $row[1]); printf ("%s %s\n"), $row["prenume"], $row["nume"]); }
Informaiile returnate de funcia mysql_fetch_array() reprezint un superset al informaiilor returnate de mysql_f
etch_row(). n ciuda acestui fapt, diferenele de performan ntre cele dou funcii sunt neglijabile si putei apela
la funcia mysql_f etch_array () fr nici o diminuare semnificativ a performanelor.
Cea de-a treia funcie de preluare a rndurilor, mysql_fetch_object(), returneaz urmtorul rnd al setului de
rezultate sub forma unui obiect. Aceasta nseamn c obinei accesul la elementele rndului folosind sintaxa
$row-nume_coloana. De exemplu, dac regsii valorile nume i prenume din tabelul preedinte, putei obine
accesul la aceste coloane folosind sintaxa $row-nume i $row- prenume:
$query = "SELECT nume, prenume FROM preedinte";
$result = mysql_query ($query) or die ("Interogarea a euat");
while ($row = mysql_fetch_object (Sresult))
printf ("%s %s\n"), $row->prenume, $row->nume);
Testarea existenei valorilor NULL n rezultatele interogrii
Pentru a verifica dac o valoare dintr-o coloan retumat de o interogare SELECT este sau nu NULL, folosii
funcia isset(). De exemplu, dac rndul dumneavoastr este inclus n tabloul $row, funcia isset($row[$i]) are
valoarea FALSE dac expresia $row[$i]corespunde unei valori NULL, respectiv TRUE dac expresia este
diferit de NULL. O funcie conex este empty (), dar aceasta returneaz acelai rezultat pentru NULL ca i
pentru irurile vide, deci nu este util ca test pentru detectarea unei valori NULL.
Dac interogarea dumneavoastr conine coloane cu valori calculate? De exemplu,' putei emite o interogare care
returneaz valori calculate ca rezultat al unei expresii:
SELECT CONCAT(prenume, " ", nume) FROM preedinte O interogare scris astfel nu poate fi utilizat cu
funcia mysql_f etch_object(). Numele] coloanei selectate este expresia nsi, care nu constituie un nume valabil
de proprietate.! Totui, putei furniza un nume valabil atribuind coloanei un alias. Interogarea urmtoarfti atribuie
coloanei aliasul nume_complet, care permite accesul la coloan folosind sintaxei $row-nume_complet, dac
preluai rezultatele cu ajutorul funciei tnysql_fetch_object( )f,
SELECT CONCAT(prenume, " ", nume) AS nume_complet FROM preedinte :,
Tratarea erorilor
Pentru tratarea erorilor, PHP v pune la dispoziie trei mijloace:
Utilizarea caracterului e pentru a elimina mesajele de eroare. Putei folosi car terul @ cu orice funcie care
poate afia un mesaj. Am procedat astfel cnd am ape funcia mysql_pconnect() pentru a mpiedica apariia
mesajelor de eroare generate < funcia respectiv n pagina trimis clientului.
r
Capitolul 8 Interfaa API pentru PHP 391
Utilizarea funciei error_reporting(). Aceast funcie activeaz sau dezactiveaz caracteristica de raportare a
erorilor la oricare din urmtoarele nivele:
Nivel de eroare
12
Tipuri de erori raportate
Erori normale de funcii Avertismente normale
4 Erori ale analizorului
8 ntiinri
Pentru a controla caracteristica de raportare a erorilor, apelai funcia error_reporting ( ) cu un argument egal cu
suma nivelelor pe care dorii s le activai. Dezactivarea avertismentelor de nivel l i 2 este suficient pentru a
elimina mesajele provenite de la funciile MySQL:
error_reporting(4 + 8);
Probabil c nu dorii s dezactivai avertismentele de nivel 4 cu privire la erorile de analiz; dac o facei, vei
avea mari probleme la depanarea oricror modificri pe care le efectuai n scripturile dumneavoastr! Deseori,
avertismentele de nivel 8 pot fi ignorate, dar uneori indic o problem n scriptul dumneavoastr la care trebuie
s fii atent, deci este de dorit ca si acest nivel s rmn activat. Exist si nivelele de eroare 16 si 32, dar acestea
provin de la mecanismul de baz al limbajului PHP si nu de la funcii, deci n mod normal nu trebuie s v
preocupe.
Utilizarea funciilor mysql_error() i mysql_errno(). Aceste funcii raporteaz informaiile de eroare care sunt
returnate de serverul MySQL. Funciile respective sunt similare cu funciile din interfaa API pentru C.
mysql_error() returneaz un mesaj de eroare sub form de ir (un ir vid dac nu s-a produs nici o eroare).
mysql_errno( ) returneaz un numr de eroare (O dac nu s-a produs nici o eroare). Ambele funcii preiau ca
argument un identificator de legtur, care specific o conexiune cu serverul MySQL, si returneaz informaii de
eroare pentru funcia MySQL cel mai recent invocat din acea conexiune care returneaz o stare. Identificatorul
de legtur este opional; dac lipsete, se va folosi conexiunea cel mai recent deschis. De exemplu, putei
raporta o Eroare din mysql_query ( ) n urmtorul mod: if (!($result = mysql_query (...)))
prin ("errno: print ("error:
mysql_errno()) ; mysql_error() ) ;
Versiunile PHP ale funciilor mysql_error ( ) i mysql_errno ( ) difer de omoloagele lor din C dintr-un important
punct de vedere, n C, putei obine informaii despre eroare chiar i cnd o ncercare de conectare la server
eueaz. Prin contrast, apelurile PHP nu returneaz informaii utile pentru o conexiune dect dup stabilirea cu
succes a conexiunii. Cu alte cuvinte, dac o ncercare de conexiune eueaz, nu putei folosi mysql_errno() sau
mysql_error( ) pentru a afla motivul. Dac dorii ca pentru eecul conexiunii s raportai un anumit motiv, nu un
mesaj generic, trebuie s luati msuri speciale. Consultai Anexa H, Referin API PHP", pentru instruciuni
privind modul n care s procedai.
392 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Scripturile din acest capitol afieaz mesaje de eroare cu un coninut destul de general, precum interogarea a
euat", atunci cnd detecteaz vreo eroare. Totui, cnd dezvoltai un script, deseori vei gsi util adugarea
unui apel la funcia mysql_error(), pentru a afla motivul specific al unei erori.
Probleme legate de ghilimele
Cnd construii iruri de interogri n PHP, este necesar s cunoatei problemele legate de ghilimele, ca i n C
sau Perl. Modalitatea n care sunt rezolvate problemele privind ghilimelele este, de asemenea, similar, dei
numele funciilor sunt diferite n limbaje diferitei
S presupunem c dumneavoastr construii o interogare pentru inseria unei noi nregistrri ntr-un tabel; putei
delimita prin ghilimele o valoare care urmeaz a fi inserat ntr-o coloan ir:
$nume = "O'Malley";
Sprenume = "Brian";
$data_expirare = "2002-9-1";
$query = "INSERT membru (nume,prenume,data_expirare)" . "
VALUES('$nume','$prenume','$data_expirare')"
n acest caz, problema este c una dintre valorile ncadrate ntre ghilimele conine alte ghilimele (" O' Malley"),
ceea ce are ca rezultat o eroare de sintax dac trimitei interoga- j rea la serverul MySQL. Pentru a rezolva
problema n C, am fi apelat funcia l mysql_escape_string(). ntr-un script Perl DBI, am fi folosit quote(). PHP
are 6 l funcie denumit addslashes (), care execut cam aceeai operaie. De exemplu, un apel f la funcia
addslashes ("O'Malley") returneaz valoarea "0\ 'Malley". Exemplul anteri- J or trebuie scris dup cum urmeaz,
pentru a se preveni problemele legate de ghilimele:
$nume = addslashes ("O'Malley");
$prenume = addslashes ("Brian");
$data_expirare = addslashes ("2002-9-1");
$query = "INSERT membru (nume,prenume,data_expirare)" . "
VALUES('$nume','$prenume','$data_expirare')"
Metoda DBI quote () adaug ghilimele de delimitare la irul de interogare. Func addslashes () nu procedeaz
astfel, deci trebuie s precizm ghilimelele n mod explic n irul de interogare, n jurul valorilor care vor fi
inserate.
Probleme privind ghilimelele se produc, de asemenea, cnd se scriu informaiile care vc fi prezentate n paginile
Web. Dac scriei un ir care trebuie s apar sub form HTML sau ca pane a unui URL, cel mai bine este s l
codificai, dac irul poa conine caractere care sunt speciale pentru HTML sau n adrese URL. Funciile PI
htmlspecialchars () si urlencode() pot fi folosite n acest scop. Cele dou funcii su asemntoare cu metodele
escapeHTML(), respectiv escape() din modulul CGI.pm.
Utilizarea PHP
n continuarea acestui capitol, vom aborda scopurile pe care ni le-am stabilit n capitolt i pe care le mai avem de
ndeplinit:
Capitolul 8 Interfaa API pentru PHP 393
Pentru proiectul de eviden a rezultatelor colare, trebuie s, scriem un script care s ne permit s introducem
i s editm punctajele obinute la teste i chestionare.
Pentru Liga istoric, dorim s crem un chestionar n format electronic despre preedinii Statelor Unite si s-i
imprimm un caracter interactiv, astfel nct ntrebrile s poat fi generate imediat pentru vizitatorii sitului Web.
De asemenea, dorim s permitem membrilor Ligii istorice s-i editeze rubricile aferente din catalogul n
versiunea electronic, pentru a pstra informaiile actualizate i pentru a reduce volumul editrilor pe care le
executm personal.
Fiecare dintre aceste scripturi genereaz mai multe pagini Web i comunic de la o invocare a scriptului la alta
prin intermediul informaiilor nglobate n paginile pe care le creeaz. Dac nu cunoatei conceptul de
comunicaii inter-pagini, putei citi seciunea Scrierea paginilor cu destinaie multipl" din capitolul 7.
Introducerea punctajelor elevilor
n aceast seciune, ne vom ndrepta atenia spre proiectul de eviden a rezultatelor colare. Adresa URL a
acestei zonen situl nostru de Web este http: / /pit -viper, snake . net/evid/ si probabil c vom scrie pentru aceasta
o scurt pagin de baz index . php. Deocamdat, pagina urmtoare este suficient. Pagina conine o legtur
spre scriptul score_browser pe care 1-am scris n capitolul 7, deoarece acel script are legtur cu proiectul de
eviden a rezultatelor colare:
<?php
include ("samp_db.inc") ;
$titlu = "Proiect de evidenta a rezultatelor colare"; html_begin (Stitlu, $titlu);
<A HREF="/cgi-bin/score_browse">Vizualizarea</A> punctajelor obinute la chestionare si teste
<?php
html_end ()
?>
Acum, s lum n considerare proiectarea si implementarea scriptului score_entry .php, care ne va permite s
introducem un set nou de punctaje obinute la chestionare sau teste, respectiv s editm un set de nregistrri
existent. Ultima funcionalitate este necesar pentru manipularea punctajelor elevilor care susin un test sau un
chestionar mai trziu dect restul clasei, datorit unei absene pe motiv de boal sau dintr-un alt motiv (sau -
Doamne ferete! - n caz c am introdus incorect punctajul). Schia scriptului pentru introducerea punctajelor
este urmtoarea:
l . Pagina iniial prezint o list cu evenimente de examinare cunoscute i v permite s alegei unul, respectiv
s specificai necesitatea de creare a unui eveniment nou.

394 Partea a 11-a Utilizarea interfeelor de programare ale sistemului MySQL


2. Dac optai pentru crearea unui eveniment nou, scriptul prezint o pagin care v permite s specificai data i
tipul evenimentului. Dup crearea nregistrrii aferente evenimentului, scriptul afieaz din nou pagina cu lista
de evenimente, pentru a prezenta noul eveniment.
3. Cnd alegei un eveniment, scriptul prezint o pagin de introducere a punctajului, care conine informaiile
despre eveniment n partea de sus (identificator de eveniment, dat, tip), urmat de o list cu o intrare pentru
fiecare elev. Pentru evenimentele noi, intrrile vor fi vide. Pentru evenimentele existente, intrrile vor prezenta
punctajul fiecrui elev. Cnd selectai butonul Trimite", punctajele vor fi introduse n tabelul puncte.
Scriptul trebuie s execute numeroase operaii diferite, ceea ce nseamn c va trebui s transferm o variabil de
stare de la o pagin la alta, pentru ca scriptul s tie ce are de fcut atunci cnd este invocat, n PHP, aceast
operaie este simpl, deoarece PHP prelucreaz variabilele transferate ca parametri URL si le convertete n
variabile cu acelai nume ca i parametrii. De exemplu, putem codifica un parametru aciune situat la sfritul
adresei URL a scriptului nostru astfel:
http://pit-viper.snake.net/evid/score_entry.php?actiune=valoare Cnd scriptul score_ent ry. php este invocat n
acest mod, parametrul aciune este codificat sub forma variabilei $actiune, care este accesibil direct. Acest fapt
este valabil si pentru cmpurile din formulare. S presupunem c un formular conine cmpuri denumite nume i
adresa. Cnd un client trimite formularul, serverul Web invoc un script pentru a prelucra coninutul
formularului. Scriptul poate afla valorile introduse n formular prin verificarea valorilor variabilelor $nume i
Sadresa. Pentru formulare care conin un numr mare de cmpuri, nu este convenabil ca toate s aib nume
unice. PHP simplific transferul tablourilor nuntrul i n afara formularelor. Dac folosii nume de cmpuri
precum x [ O ], x [ 1 ] i aa mai departe, PHP le va codifica sub form de elemente ale tabloului $x. Aceste
element sunt accesibile sub forma $x[0], $x[1 ] etc.
Vom comunica informaiile de la o invocare a scriptului score_entry. php la urmtoarea folosind n paginile
noastre un parametru denumit $actiune, iar valoarea sa va fi verifi-1 cat folosind variabila Sactiune din scriptul
nostru. Cadrul scriptului se prezint astfel:! <?php
# score_entry.php - script de introducere a punctajelor pentru
# proiectul de evidenta a rezultatelor colare
include ("samp_db.inc");
# definete constantele de aciune define (INITIAL_PAGE, 0);
define (SOLICIT_EVENT, 1);
define (ADD_EVENT, 2);
define (DISPLAY_SCORES, 3);
define (ENTER_SCORES, 4);
/* ... funciile se insereaz aici ... */
Capitolul 8 Interfaa API pentru PHP 395
$titlu = "Proiect de evidenta a rezultatelor colare -
introducere punctaje"; html_begin ($titlu, $titlu);
samp_db_connect()
or die ("Nu se poate conecta la serverul de baze de date")
if (empty ($actiune))
$actiune = INITIAL_PAGE;
switch (Sactiune)
{
case INITIAL_PAGE:
display_events ();
break; case SOLICIT_EVENT:
solicit_event_info
break; case ADD_EVENT:
add_new_event ();
display_events ();
break; case DISPLAY_SCORES:
display_scores (); break (); case ENTER_SCORES: ()
# ce avem de fcut?
# prezint pagina iniiala
# solicita informaii privind noul
# eveniment
# adaug un eveniment nou la baza de date
# afieaz punctajele pentru
# evenimentul selectat
# introduce punctajele noi
# sau pe cele editate enter_scores ();
display_events (); break; default:
die ("Cod de aciune ($actiune) necunoscut");
html_end ( )
?>
Variabila $actiune poate lua numeroase valori, pe care le testm n instruciunea switch (). (Pentru a evita s
folosim numere literale n script, vom folosi construcia PHP define() pentru definirea constantelor.)
Instruciunea switch () din PHP este similar cu omoloaga sa din C. n scriptul score_entry.php, aceast
instruciune este folosit pentru a determina aciunea care trebuie ntreprins si pentru apelarea funciilor care
implementeaz aciunea.
398 Partea a ll-a Utilizarea interfeelor de programare ale sistemului MySQL
Formularul generat de funcia solicit_event_inf o() conine un cmp de editare pentru introducerea datelor, o
pereche de butoane radio pentru a specifica dac intrarea este un test sau un chestionar, precum i un buton
Trimite". Cnd trimitei formularul, este invocat scriptul score_entry.php cu aciunea ADD_EVENT. Funcia
add_new_event() este apelat pentru introducerea unui nou rnd n tabelul eveniment: function add_new_event()
global $data, $tip;
if (empty ($data)) # verifica introducerea unei date
die ("Nu a fost specificata nici o data"); $query = sprintf ("INSERT INTO eveniment (data.tip)
VALUES(\"%s\",\"%s\'), addslashes ($data), addslashes ($tip); if (!mysql_query ($query))
die ("Nu a putut adaug evenimentul");
n funcia add_new_event(), folosim cuvntul cheie global pentru a obine accesul la cmpurile care sunt utilizate
n formularul de introducere a evenimentelor noi (data i tip, accesibile sub forma variabilelor $data i $tip).
Dup o verificare minimal de siguran, pentru a ne asigura c datele nu sunt vide, introducem o nou
nregistrare n j tabelul eveniment. Programul principal afieaz din nou lista de evenimente dup ce am j
introdus nregistrarea de eveniment, astfel nct s putei selecta noul eveniment i s J putei ncepe introducerea
punctajelor.
Funcia display_scores () se ocup de cutarea tuturor punctajelor existente pentru uiil eveniment dat si afieaz
un formular care afieaz punctajele, alturi de numele elevilorj function display_scores()
# selecteaz punctele pentru evenimentul dat $query = " SELECT
elev.elev_id, elev.nume, eveniment.data, puncte.puncte" AS puncte, eveniment.tip FROM elev, eveniment
LEFT JOIN puncte ON elev.elev_id = puncte.elev_id
AND eveniment.eveniment_id = puncte.eveniment_id WHERE eveniment.eveniment_id = $eveniment_id
ORDER BY elev.nume
Sresult = mysql_query ($query)
or die ("Nu poate executa interogarea");
printf ("<FORM METHOD=\"post\" ACTION=\"%s?actiune=%d&eveniment_id=%d\">\n| $PHP_SELF,
ENTER_SCORES, $eveniment_id);
r
Capitolul 8 Interfaa API pentru PHP 399
# afieaz punctajele intr-un tabel si afieaz data si tipul
# evenimentului anterior tabelului. Totui, nu putem afia data si
# tipul dect dup preluarea primului rnd din setul de rezultate
$needheading = 1 ;
while ($row = mysql_fetch_array ($result))
{
if ($needheading)
{
printf ("Identificator eveniment: %s,
Data eveniment: %s, Tip eveniment: %s\n, $eveniment_id, $row["data"] , $row["tip"]);
prin ("<BR><BR>\n") ;
prin ("<TABLE BORDER>\n");
prin ("<TR>\n");
display_cell ("TH", "Nume", 1);
display_cell ("TH", "Puncte", 1);
prin ("</TR>\n");
$needheading = 0; }
prin ("<TR>\n");
display_cell ("TD", $row["nume"], 1); $col_val = sprintf ("<INPUT TYPE=text NAME=\" puncte [%s] \"" ,
$row["elev_id"]); $col_val .= sprintf (" VALUE=\"%s\" SIZE=5><BR>\n",
$row[ "puncte"]) ;
display_cell ("TD", $col_val, 0); prin ("</TR>\n");
prin ("</TABLE>\n");
prin ("<BR>\n");
prin ("<INPUT TYPE=\"Trimite\" NAME=\" buton \" VALUE=\"Trimite\">\n") ;
prin ("</FORM>\n"); }
Interogarea pe care funcia display_scores( ) o folosete pentru regsirea informaiilor privind punctajele nu este
o simpl unire ntre tabele, deoarece aceasta nu va selecta un rnd al unui elev care nu a obinut nici un punctaj la
evenimentul respectiv, n particular, pentru un eveniment nou, unirea respectiv nu va selecta nici o nregistrare
si vom avea un formular de introducere vid! Dorim s folosim un LEFT JOIN pentru a fora regsirea unui rnd
pentru fiecare elev, indiferent dac elevul are sau nu un punctaj n tabelul puncte. Elementele fundamentale ale
unei interogri similare celei pe care funcia display_scores( ) o folosete pentru regsirea nregistrrilor cu
punctaje din MySQL au fost date n capitolul 3, Sintaxa si utilizarea SQL n MySQL", n seciunea

S-ar putea să vă placă și