Sunteți pe pagina 1din 6

ATELIER O varianta extinsa e disponibila in cartea

'Tehnologii Web', Matrix Rom, Bucuresti, 2001:


PHP www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/

PHP, programare obiectualã ºi XML


Prezentul articol va ilustra implementarea principalelor concepte ale
programãrii obiectuale, în contextul procesãrii documentelor XML
– Sabin Corneliu Buraga

Programarea orientatã-obiect în PHP Pentru funcþiile membru, în loc de construcþia sintacticã „->”, în
Limbajul PHP, mai ales de la versiunea 4, oferã implementarea unora PHP 4 se permite „::”. Astfel, codul de mai sus este echivalent cu:
dintre cele mai importante aspecte ale programãrii obiectuale: încapsu-
larea datelor, moºtenirea, polimorfismul. Astfel, PHP dã posibilitatea $student_year = $stud::getYear();
programatorului sã exprime distincþia dintre proprietãþile generale ºi
cele specifice obiectelor cu care lucreazã. Vom ilustra aceste caracteris- Moºtenirea
tici prin câteva exemple concludente. Moºtenirea reprezintã posibilitatea folosirii datelor sau metodelor defi-
nite în prealabil de o anumitã clasã în cadrul unei clase derivate din
Încapsularea prima. Relaþia de derivare se specificã prin cuvântul-cheie extends:
Încapsularea datelor reprezintã un mecanism de protecþie a membrilor
de tip datã, accesul la ei realizându-se exclusiv prin intermediul unor <?php
metode specifice ºi nu în mod direct. Acest lucru se realizeazã prin in- class GoodStudent extends Student {
termediul claselor, dupã cum se poate remarca din fragmentul de cod // date-membru
PHP de mai jos: var $prizes; // premii
// metode
<?php function setPrizes($p) {
class Student { $this->prizes = $p;
// date-membru }
var $year; // an function getPrizes() {
var $age; // vârsta return $this->prizes;
var $name; // nume }
// metode }
function setYear($y) { ?>
$this->year = $y;
} Putem scrie urmãtoarele linii:
function getYear() {
return $this->year; $goodstud = new GoodStudent;
} // apel de metodã din clasa de bazã
... $goodstud->setAge(21);
} // apel de metodã din clasa derivatã
?> $goodstud->setPrizes(2);

Datele-membru se definesc utilizând var, putând avea orice tip (întreg, În PHP moºtenirea multiplã nu este încã posibilã. Nu putem avea, de
tablou sau chiar obiect). Metodele se specificã prin declaraþii de funcþii exemplu:
care prelucreazã membrii clasei. Pentru a accesa datele clasei, metodele
vor trebui sã se foloseascã de construcþia $this->variabila, altfel vari- class GreatStudent extends GoodStudent Genius {
abila se considerã a fi localã în cadrul acelei metode. ...
Vom crea un obiect prin operatorul new, exact ca în limbajul C++: }

$stud = new Student; Putem însã redefini o metodã în clasa derivatã. În PHP 3, dacã decla-
rãm un membru-datã în clasa derivatã având acelaºi nume ca un mem-
Accesarea metodelor clasei se realizeazã astfel: bru-datã al clasei de bazã, atunci nu mai putem accesa membrul-datã
din clasa de bazã, el fiind „ascuns” de membrul-datã al clasei derivate.
$stud->setYear(4); În PHP 4, pentru a accesa membri ai clasei pãrinte ne vom folosi de
$student_year = $stud->getYear(); construcþia „::” (exact ca ºi în C++).

Din pãcate, membrii datã ai clasei pot fi accesaþi direct, neputându-i Constructori
declara privaþi. La fel, metodele nu pot fi specificate private sau prote- PHP permite specificarea constructorilor, ei fiind metode cu acelaºi
jate, aºa cum se întâmplã la C++. Astfel, PHP oferã doar suport pentru nume al clasei din care aparþin, fiind apelaþi automat atunci când in-
încapsulare, dar nu o poate impune. stanþiem un obiect al clasei respective. Putem avea de exemplu:

38 NET REPORT • martie 2001


O varianta extinsa e disponibila in ATELIER
cartea
'Tehnologii Web', Matrix Rom, Bucuresti, 2001:P H P
www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/
<?php $stud = new Student(2, 20);
class Student {
// date-membru Atunci când un obiect al unei clase derivate este creat, numai construc-
var $year; // an torul lui propriu va fi apelat, constructorul clasei de bazã nefiind apelat
var $age; // vârsta implicit. Dacã dorim ca ºi constructorul clasei pãrinte sã fie apelat, o
var $name; // nume vom face într-o manierã explicitã:
// constructor
function Student($y, $a, $n) { <?php
$this->year = $y; function GoodStudent {
$this->age = $a; $this->prizes = 3;
$this->name = $n; // apel explicit al constructorului clasei de bazã
} $this->Student();
// metode }
function setYear($y) { ?>
$this->year = $y;
} Dacã în PHP 3, constructorii puteau avea orice tip de parametri, în-
function getYear() { cepând cu versiunea 4, tipurile permise pentru parametrii unui con-
return $this->year; structor sunt doar cele simple (întregi, ºiruri de caractere), deci nu vor
} putea fi executate transmiteri de tablouri sau de obiecte.
... Dacã o clasã derivatã nu posedã propriul ei constructor, va fi apelat
} implicit constructorul clasei pãrinte.
?> Mecanismul obiectual în PHP nu permite declararea destructorilor
ºi nici specificarea de clase abstracte (deºi se pot imagina metode mai
Aºadar, acum se poate crea un obiect de genul: mult sau mai puþin sofisticate pentru a le simula).

$stud = new Student(3, 24, "Radu Filip"); Supraîncãrcarea


Supraîncãrcarea (asocierea de semantici diferite unei aceleiaºi funcþii pe
Constructorii ºi metodele, fiind funcþii PHP obiºnuite, pot avea speci- baza tipurilor parametrilor specificaþi) nu este suportatã nici ea. Putem
ficate valori implicite pentru argumente (ca în C++): însã supraîncãrca, indirect, constructorii prin crearea de obiecte diferite
în funcþie de numãrul de argumente specificate:
function Student($y = "4", $a = "22", $n = "")
<?php
Dacã scriem în acest mod constructorul, atunci în urmãtoarele cazuri class Student {
vom avea: ...
function Student() {
// year = 4, age = 22, name = "" // contruim un ºir de apel
$stud = new Student(); $method_name = "Student" . func_num_args();
// year = 2, age = 22, name = "" $this->$method_name();
$stud = new Student(2); }
// year = 2, age = 20, name = "" function Student1($x) {

martie 2001 • NET REPORT 39


ATELIER O varianta extinsa e disponibila in cartea
'Tehnologii Web', Matrix Rom, Bucuresti, 2001:
PHP www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/
// cod $obj->draw();
} }
function Student2($x, $y) { // coordonate centru ºi raza
// cod $circle = new Circle(100, 100, 33);
} // coordonate stânga-sus ºi latura
... $square = new Square(100, 200, 74);
} // afiºeazã cele douã figuri
?> $board = drawing($circle); // apeleazã draw() din Circle
$board = drawing($square); // apeleazã draw() din Square
Vom putea scrie: ...
?>
$stud1 = new Student(‘1’); // va apela Student1
$stud2 = new Student(‘1’, ‘2’); // va apela Student2 Serializarea
PHP nu suportã obiecte serializate (care îºi pãstreazã starea ºi
Pentru a pasa un numãr variabil de parametri unei funcþii ºi a-i folosi funcþionalitatea de-a lungul mai multor invocãri ale aplicaþiei, prin
ulterior putem sã ne slujim de funcþiile predefinite func_get_args(), intermediul stocãrii într-un fiºier/bazã de date ºi încãrcarea lor ulte-
func_num_args() ºi func_get_arg(). Astfel, funcþia Student() de mai rioarã). În PHP prin serializare (funcþiile serialize() ºi unserial-
sus va putea afiºa toþi parametrii transmiºi prin codul urmãtor: ize()) se vor salva numai membrii-datã, nu ºi metodele, însã putem
serializa tablouri asociative sau indexate, ceea ce reprezintã totuºi un
function Student() avantaj.
{
$args_array = func_get_args(); Funcþii utile pentru manipularea obiectelor
for ($i = 0; $i < count($args_array); $i++) Începând cu PHP 4, se pune la dispoziþia programatorului o serie de
print ($i => $argument_array [$i]); funcþii folositoare:
} • get_class() va returna numele unui obiect, instanþã a unei clase;
• get_parent_class() furnizeazã clasa pãrinte din care provine un an-
Polimorfismul umit obiect;
Polimorfismul reprezintã abilitatea unui obiect de a determina care • method_exists() testeazã dacã existã o metodã pentru un anumit
metodã trebuie invocatã pentru un obiect pasat ca argument în mo- obiect specificat;
mentul rulãrii ºi acest lucru se realizeazã foarte uºor în limbaje inter- • class_exists() testeazã existenþa unei clase;
pretate ca PHP. • is_subclass_of() va determina existenþa unei relaþii de moºtenire
Vom ilustra acest concept ºi implementarea lui în PHP pre- dintre douã clase.
supunând cã avem o clasã Figure desemnând o figurã geometricã în O altã facilitate este cea a transmiterii prin referinþã a parametrilor
care se defineste metoda draw() ºi clasele derivate Circle ºi Square ºi nu prin valoare, cum se realizeazã în mod implicit. Pentru a fi trans-
unde vom rescrie metoda draw() în funcþie de figura doritã a fi mis prin referinþã, vom prefixa numele acelui parametru cu caracterul
desenatã: ampersand „&”.

<?php PHP ºi XML


... Cele de mai sus ne permit sã realizãm o procesare elegantã a docu-
function drawing($obj) { // metoda clasei Board mentelor XML, folosind SAX ori DOM.

40 NET REPORT • martie 2001


O varianta extinsa e disponibila in ATELIER
cartea
'Tehnologii Web', Matrix Rom, Bucuresti, 2001:P H P
www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/
Utilizarea lui Expat // seteazã numele fiºierului XML
Elaborat de James Clark, procesorul Expat este deja încorporat în function set_xml_file($file) {
serverul Apache începând cu versiunea 1.3.9, iar în PHP este inclus de $this->xml_file = $file;
la versiunea 3.0.6. Analiza XML este bazatã pe evenimente, fiecare tip }
de nod al arborelui asociat documentului XML declanºând un anumit // furnizeazã codul HTML generat
eveniment care va trebui tratat de o funcþie definitã de programator. function get_html_code() {
Pentru a ataºa funcþii evenimentelor XML, ne vom folosi de o serie de return $this->html_code;
funcþii predefinite: }
• xml_set_element_handler() stabileºte funcþiile care vor fi apelate // tratarea evenimentului de
pentru procesarea elementelor XML (pentru tag-urile de început ºi de // apariþie a unui tag de început
sfârºit); function start_element($parser, $name, $attrs) {
• xml_set_character_data_handler() stabileºte funcþia care va fi if ($format = $this->open_tags[$name])
apelatã atunci când analizorul întâlneºte un nod de tip CDATA (text); $this->html_code .= $format;
• xml_set_processing_instruction_handler() defineºte funcþia care }
va fi executatã la apariþia unei instrucþiuni de procesare. // tratarea evenimentului de
Alte funcþii importante puse la dispoziþie sunt: // apariþie a unui tag de sfârºit
• xml_parser_create() iniþializeazã analizorul XML ºi returneazã o function end_element($parser, $name) {
instanþã a sa; if ($format = $this->close_tags[$name])
• xml_parser_free() elibereazã memoria alocatã analizorului; $this->html_code .= $format;
• xml_set_object() stabileºte adresele funcþiilor care vor fi utilizate de }
analizor pentru a realiza procesarea documentului XML dorit; // tratarea evenimentului de
• xml_parser_set_option() se foloseºte la setarea unor opþiuni de // apariþie a unui element de tip CDATA
analizã XML (e.g. modul de tratare a scrierii cu majuscule sau mi- function character_data($parser, $data) {
nuscule a tag-urilor); $this->html_code .= $data;
• xml_get_error_code() furnizeazã codul de eroare în urma eºecului }
procesãrii. // tratarea evenimentului de
Pot fi amintite, de asemeni, funcþiile dând mai multe amãnunte // apariþie a unei instrucþiuni de procesare
despre erorile survenite în timpul analizei: xml_error_string() ºi function processing_instruction($parser, $target, $data) {
xml_get_current_line_number(). Funcþia xml_parse() returneazã, în switch (strtolower($target)) {
caz de eºec, o serie de coduri de eroare ale cãror constante simbolice case "php": eval($data);
predefinite pot fi consultate în manualul PHP. break;
Vom defini o clasã pe care o vom folosi ulterior la procesarea docu- }
mentelor XML (vom salva acest cod în fiºierul parseXML.php). }
// funcþia de analizã propriu-zisã
<?php function parse() {
// o clasã pentru prelucrarea documentelor XML // instanþiazã procesorul XML
class parseXML { $this->xml_parser = xml_parser_create();
var $xml_parser; /* instanþa analizorului XML */ // înregistreazã funcþiile de analizã
var $xml_file; /* numele fiºierului XML */ xml_set_object($this->xml_parser, &$this);
var $html_code; /* codul HTML generat */ // seteaza opþiunile de analizã
var $open_tags; /* mulþimea tag-urilor de început */ // (tag-urile nu sunt rescrise cu caractere mari)
var $close_tags; /* mulþimea tag-urilor de sfârºit */ xml_parser_set_option($this->xml_parser,
// constructor XML_OPTION_CASE_FOLDING, false);
function parseXML() { // seteazã funcþiile de procesare a elementelor XML
$this->xml_parser = ""; xml_set_element_handler($this->xml_parser,
$this->xml_file = ""; "start_element", "end_element");
$this->html_code = ""; xml_set_character_data_handler($this->xml_parser,
$this->open_tags = array(); "character_data");
$this->close_tags = array(); xml_set_processing_instruction_handler($this->xml_parser,
} "processing_instruction");
// destructor // deschide fiºierul XML
function destroy() { if (!($fp = fopen($this->xml_file, "r")))
if ($this->xml_parser) die("could not open XML source");
xml_parser_free($this->xml_parser); // proceseazã fiºierul
} while ($data = fread($fp, 4096)) {
// metode if (!xml_parse($this->xml_parser, $data, feof($fp))) {
// seteazã tag-urile de început // eroare de procesare
function set_open_tags($tags) { die(sprintf("XML error: %s at line %d",
$this->open_tags = $tags; xml_error_string(xml_get_error_code
} ($this->xml_parser)),
// seteazã tag-urile de sfârºit xml_get_current_line_number($this->xml_parser)));
function set_close_tags($tags) { }
$this->close_tags = $tags; } /* while */
} } /* parse() */

martie 2001 • NET REPORT 41


ATELIER O varianta extinsa e disponibila in cartea
'Tehnologii Web', Matrix Rom, Bucuresti, 2001:
PHP www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/
} /* class */ De multe ori însã ar fi de dorit sã realizãm anumite prelucrãri asu-
?> pra datelor stocate de fiºierele XML. Putem încã sã ne slujim de par-
seXML. De exemplu, am dori ca utilizatorul (sau autorul site-ului) sã
Folosind aceastã clasã, putem transforma un document XML în cod poatã trimite mesaje celor care ºi-au lãsat impresiile. Pentru aceasta
HTML, dupã cum se poate remarca din exemplul de mai jos, unde va fi vom defini o clasã derivatã din clasa parseXML ºi vom redefini funcþiile
prelucrat un fiºier XML conþinând impresii despre un anumit site Web: start_element() ºi end_element():

<?php <?php
// necesitã prezenþa clasei definite mai sus require("parseXML.php");
require("parseXML.php"); // folosirea moºtenirii pentru a defini un alt comportament
// substituþia tag-urilor XML cu cod HTML class parseXML2 extends parseXML {
// se folosesc douã tablouri asociative // indicã dacã existã atributul "email"
$open_tags = array( var $is_email = 0;
"impresii" => "\n<!— generat de parseXML —>\n" . // redefinirea metodelor
"<table cellpadding=\"5\" align=\"center\" function start_element($parser, $name, $attrs) {
border=\"1\">", // apeleazã metoda din clasa de bazã
"impresie" => "<tr align=\"center\">", parseXML::start_element($parser, $name, $attrs);
"nume" => "<td><h4>", // pune ºi link spre adresa e-mail
"ocupatia" => "<td><p style=\"color: blue\">", if (!strcmp($name, "nume")) {
"virsta" => "<td><p><i>", if ($attrs["email"]) {
"text" => "<td bgcolor=\"#EEEEEE\"><p $format = "<a title=\"Trimite mesaj la " .
align=\"justify\">"); $attrs["email"] .
$close_tags = array( "\" href=\"mailto:" . $attrs["email"] . "\">";
"impresii" => "</table>\n" . $this->html_code .= $format;
"<!— sfârºitul generãrii parseXML —>\n", $this->is_email = 1;
"impresie" => "</tr>", }
"nume" => "</h4></td>", else
"ocupatia" => "</p></td>", $this->is_email = 0;
"virsta" => "</i></p></td>", }
"text" => "</p></td>"); }
// instanþiazã ºi iniþializeazã analizorul function end_element($parser, $name) {
$parser = new parseXML(); // închide
$parser->set_xml_file("impresii.xml"); if (!strcmp($name, "nume")) {
$parser->set_open_tags($open_tags); if ($this->is_email) {
$parser->set_close_tags($close_tags); $format = "";
// ruleazã analizorul $this->html_code .= $format;
$parser->parse(); }
// afiºeazã rezultatul }
echo $parser->get_html_code(); // apeleazã metoda din clasa de bazã
// distruge obiectul parseXML::end_element($parser, $name);
$parser->destroy(); }
?> }
?>
Clasa definitã este suficient de generalã pentru a putea fi utilizatã pen-
tru orice tip de document XML. Tag-urile netratate de programul nos- Noul membru de tip datã is_email este folosit pentru a putea închide
tru vor fi ignorate. corect tag-urile elementului <a> (se poate întâmpla ca atributul email sã

42 NET REPORT • martie 2001


O varianta extinsa e disponibila in ATELIER
cartea
'Tehnologii Web', Matrix Rom, Bucuresti, 2001:P H P
www.infoiasi.ro/~busaco/books/web.html
Vezi si http://www.infoiasi.ro/~phpapps/
Afiºarea datelor XML prin utilizarea procesorului expat • dumpmem() - converteºte în ºir de caractere reprezentarea internã a ar-
borelui DOM.
Un script PHP care va genera documentul XML:

<?xml version="1.0" ?>


<nume>Sabin-Corneliu Buraga</nume>

va fi urmãtorul:

// un nou arbore DOM


nu aparã). Restul codului rãmâne acelaºi, în loc de $parser = new par- $doc = new_xmldoc("1.0");
seXML() trebuind a fi scrisã linia $parser = new parseXML2(). // insereazã nodul-rãdãcinã
O posibilã rulare a scriptului PHP de mai sus poate avea ca efect $root = $doc->add_root("nume");
urmãtoarea paginã Web: // adaugã nodului un conþinut
Desigur, dacã facem numai prelucrãri asupra documentelor XML, $root->content = "Sabin-Corneliu Buraga";
ne putem dispensa de definirea celor douã tablouri asociative // afiºeazã documentul XML generat
open_tags[] ºi close_tags[], iar în funcþiile startElement() ºi en- print (htmlspecialchars($doc->dumpmem()));
dElement() putem insera orice cod dorim.
Utilizând tehnicile descrise mai sus, studenþii Constantin Gheorghiþã, Dupã cum se poate remarca, putem foarte uºor construi prin program do-
Valentin Pãscãreanu ºi Dan Þorin au realizat o aplicaþie Web bazatã pe cumente sau fragmente de documente XML, ceea ce nu se putea cu expat.
PHP pentru managementul lucrãrilor de licenþã la Facultatea de Infor- Dupã cum am vãzut, în cadrul modelului DOM orice componentã
maticã a Universitãþii „Al.I.Cuza” din Iaºi. Datele referitoare la licenþã a unui document XML va fi reprezentatã prin intermediul unui nod al
sunt stocate într-un fiºier XML având formatul de mai jos (lãsãm citito- arborelui asociat. Un obiect de tip nod va avea metodele:
rului plãcerea de a construi DTD-ul pentru validarea acestui document): • parent() - desemneazã nodul pãrinte al nodului curent;
• children() - returneazã nodurile copii ale nodului curent;
<database> • attributes() - furnizeazã atributele asociate unui nod de tip element;
<record> • new_child() - genereazã un nod copil;
<!— Numele ºi contul studentului —> • getattr() - returneazã valoarea unui atribut, dacã existã;
<stud cont="..."> ... </stud>
<!— Numele ºi contul coordonatorului —> Constantele predefinite
<prof cont="..."> ... </prof> desemnând tipurile de noduri DOM
<!— Tema lucrãrii de licenþã —>
Constantã Valoare
<tema> ... </tema>
XML_ELEMENT_NODE 1
<!— Descrierea (opþionalã) a temei —>
XML_ATTRIBUTE_NODE 2
<desc> ... </desc>
XML_TEXT_NODE 3
<!— Legãturi (URI-uri) relevante —>
<link> ... </link>
XML_CDATA_SECTION_NODE 4
... XML_ENTITY_REF_NODE 5
</record> XML_ENTITY_NODE 6
</database> XML_PI_NODE 7
XML_COMMENT_NODE 8
PHP ºi libxml XML_DOCUMENT_NODE 9
PHP 4.0 include analizorul libxml elaborat de Daniel Veillard ºi inte- XML_DOCUMENT_TYPE_NODE 10
grat în motorul PHP de Uwe Steinman (vezi ºi capitolul referitor la XML_DOCUMENT_FRAG_NODE 11
DOM). PHP funcþioneazã cu libxml-2.0.0 sau o versiune superioarã. XML_NOTATION_NODE 12
Arborele abstract asociat unui document XML va putea fi creat de
una dintre funcþiile:
• xmldoc() - va genera arborele pornind de la un ºir de caractere • setattr() - modificã valoarea unui atribut.
reprezentând un document XML; Sunt disponibili ºi urmãtorii membri-datã:
• xmldocfile() - va încãrca un document XML de pe disc ºi va con- • type - desemneazã tipul de nod; pentru o manevrare mai facilã a
strui arborele; tipurilor nodurilor sunt predefinite constantele din tabel.
• new_xmldoc() - va genera un arbore vid. Aceste constante se pot folosi ºi în programele C folosind bibliote-
Arborele DOM va fi reprezentat în PHP printr-un obiect apar- ca libxml.
þinând clasei speciale „DOM document” având proprietãþile doc • name - reprezintã numele nodului (e.g. numele unui element XML);
(resursã), version (ºir de caractere, „1.0” în prezent) ºi type (întreg • content - desemneazã conþinutul unui anumit nod (dacã existã).
lung). Sunt puse la dispoziþie urmãtoarele metode: Pentru a parcurge întreg arborele de noduri sau numai pãrþi din el
• root() - returneazã nodul rãdãcinã al arborelui DOM; ne putem folosi de xmltree(), funcþie care va analiza documentul
• addroot() - adaugã un nod rãdãcinã la un arbore vid creat de XML dat ca parametru (sub formã de ºir de caractere) ºi va returna o
new_xmldoc(); structurã de obiecte PHP reprezentând acel document. Aceastã struc-
• dtd() - returneazã un obiect aparþinând clasei DTD, care nu posedã turã nu va putea fi însã modificatã.
metode, ci numai membrii-datã name (numele elementului rãdãcinã
al documentului XML), sysid (conþine un identificator sistem al Domnul Sabin-Corneliu Buraga este doctorand în Computer Science la
DTD-ului asociat documentului, e.g. impresii.dtd) ºi extid (reprezin- Universitatea „Al.I.Cuza” din Iaºi ºi poate fi contactat la adresa
tã un identificator extern); busaco@infoiasi.ro. n 66

martie 2001 • NET REPORT 43

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