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 ntro 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> Im374 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.tviper.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