7 Elaborazione di dati XML con PHP
Transcript
7 Elaborazione di dati XML con PHP
PHP_CAP07.qxd 19/05/2006 7 11.14 Pagina 163 Elaborazione di dati XML con PHP Nei precedenti capitoli è stata illustrata l’elaborazione di dati XML sul lato client utilizzando lo standard DOM e il linguaggio XSLT. Verrà ora descritto come elaborare dati XML sul lato server utilizzando strumenti quali SAX, DOM e SimpleXML. In linea di massima, esistono due modi per gestire l’elaborazione di dati XML: lo standard DOM (Document Object Model), che è stato già illustrato, e la tecnologia SAX (Simple API for XML). Lo standard DOM consente di creare una struttura ad albero gerarchica alla quale è possibile fare riferimento ripetutamente. Con SAX, i documenti XML vengono gestiti come una serie di eventi, un evento per ciascun elemento o attributo, e la risposta a questi eventi viene fornita durante l’analisi del documento. Naturalmente, ogni approccio presenta i propri vantaggi e svantaggi. Il modello DOM, ad esempio, crea strutture ad albero che richiedono un uso esteso della memoria, ma una volta create, fornisce numerosi strumenti per spostarsi, elaborare e manipolare queste strutture ad albero. La tecnologia SAX consente una gestione lineare dei documenti XML, quindi si tratta di una tecnologia meno flessibile, ma che è anche più semplice e rapida da apprendere. PHP 5.0 fornisce un terzo approccio alla gestione dei documenti XML, un approccio che nel corso di questo manuale è stato già adottato. SimpleXML crea una struttura di oggetti gerarchica simile al modello DOM. La differenza sta nel fatto che questa struttura viene ottimizzata per l’estrazione e l’elaborazione delle informazioni, mentre il modello DOM fornisce una API più efficace, per un uso più generale, per manipolare la composizione della struttura ad albero. PHP_CAP07.qxd 19/05/2006 11.14 Pagina 164 Capitolo 7: Elaborazione di dati XML con PHP In questo capitolo verranno illustrati, nell’ordine riportato, SAX, DOM e SimpleXML. Al termine, si disporrà di una buona conoscenza del modo in cui è possibile utilizzare le diverse API per gestire documenti XML. Utilizzo di SAX Si immagini di creare un documento XML, quindi di scomporlo nelle singole parti e disporre queste parti come in una catena di montaggio. In tal modo, è possibile vedere istruzioni di elaborazione, tag di inizio, contenuto, tag di fine e così via, ossia tutti gli elementi che compongono il documento sistemati all’interno di un’unica lunga riga. Si preme un pulsante e il documento comincia a spostarsi lungo la catena di montaggio sotto l’attenta sorveglianza di un omino con un megafono. I singoli elementi del documento (un commento, un’entità, un’istruzione di elaborazione oppure un blocco di testo) scorrono l’uno dopo l’altro e la loro presenza viene annunciata di volta in volta dall’omino con il megafono. Questo è più o meno ciò che accade con il parser SAX. Sfortunatamente, per poter fare qualcosa di interessante con SAX, occorre creare manualmente funzioni personalizzate per gestire i tag di inizio, i tag di fine e i dati di tipo character, eseguendo un’azione immediata in risposta a ciascun di essi. Se si desidera memorizzare informazioni dal documento in una struttura dati temporanea da utilizzare successivamente, è bene ricordare che SAX non esegue questa operazione. Non per altro si tratta di un’API semplice per XML! E allora, per quale motivo si dovrebbe utilizzare questo parser? L’uso di SAX risulta conveniente nelle seguenti situazioni: ❑ Si sta gestendo un documento XML di grandi dimensioni che se trasformato in una struttura DOM richiederebbe una quantità eccessiva di memoria. ❑ Occorre attivare ed eseguire il parser rapidamente. ❑ Si sta eseguendo un’attività di elaborazione limitata oppure si sta eseguendo un tipo di elaborazione molto semplice e lineare (ad esempio, la conversione di determinati elementi XML in elementi HTML). ❑ Non occorre modificare il documento XML originale. Nelle circostanze appropriate come, ad esempio, un documento XML di grandi dimensioni, è sicuramente più opportuno utilizzare SAX che non DOM. Tuttavia, è importante comprendere che in alcuni casi, che verranno illustrati a breve, è più appropriato utilizzare una struttura ad albero. 164 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 165 Utilizzo di SAX Il parser SAX predefinito per PHP è Expat[1], una libreria C affidabile e testata, che per impostazione predefinita viene abilitata nella maggior parte delle installazioni PHP. Non occorre installare altre librerie: tutto è già stato creato in PHP. Di seguito ne viene descritto l’uso analizzando i singoli passi di una breve esercitazione. Il primo passo consiste nel creare un semplice file XML per l’elaborazione. Il file che verrà utilizzato in questa sezione è il seguente: Esempio 7.1. keyword-data.xml <?xml version=”1.0” encoding=”iso-8859-1”?> <keywords> <keyword>XML</keyword> <keyword>PHP</keyword> <keyword>Perl</keyword> <keyword>JavaScript</keyword> <keyword>ASP</keyword> </keywords> A questo punto, è possibile creare del codice PHP. Tuttavia, sarebbe opportuno, prima di iniziare, avere già un’idea del risultato finale che si desidera conseguire. In questo caso specifico, si desidera trasformare questo elenco di elementi di parole chiave in un elenco puntato HTML, come illustrato nella Figura 7.1, “Elenco di parole chiave elaborato mediante SAX”. Figura 7.1. Elenco di parole chiave elaborato mediante SAX [1] http://expat.sourceforge.net/ 165 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 166 Capitolo 7: Elaborazione di dati XML con PHP Creazione di gestori Prima di poter proseguire, occorre creare funzioni che “gestiscano” i tag di inizio, i tag di fine e i dati di tipo character. Queste funzioni verranno richiamate dal parser SAX mentre elabora il documento XML. Il primo gestore, a cui verrà assegnato il nome start_element, sarà responsabile della gestione di tutti i tag di inizio contenuti nel documento XML. È possibile scegliere per questo gestore il nome che si desidera; i nomi assegnati ai singoli gestori verranno poi comunicati a PHP. La funzione gestore per i tag di inizio deve accettare tre argomenti: un riferimento al parser SAX (che verrà creato a breve), il nome dell’elemento il cui tag di inizio è stato rilevato e un array di attributi dell’elemento (e dei relativi valori). Come è possibile notare di seguito, in questo caso il gestore non è altro che un’istruzione switch che ricerca un nome di un tag XML e che risponde emettendo il codice HTML appropriato. Esempio 7.2. saxdemo.php (estratto) function start_element($parser, $element_name, $element_attrs) { switch ($element_name) { case ‘KEYWORDS’: echo ‘<h1>Keywords</h1><ul>’; break; case ‘KEYWORD’: echo ‘<li>’; break; } } In SAX tutti i nomi dei tag sono maiuscoli Per impostazione predefinita, il parser SAX in PHP esegue un processo chiamato case folding, in cui qualsiasi carattere minuscolo contenuto nei nomi degli attributi o dei tag viene sostituito con l’equivalente maiuscolo. Questo è il motivo per il quale i nomi degli elementi ‘KEYWORDS’ e ‘KEYWORD’ in questo esempio sono tutti in maiuscolo. Qualora si desideri disabilitare il case-folding, il manuale di PHP contiene informazioni[2] su come eseguire questa operazione. [2] http://www.php.net/xml#xml.case-folding 166 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 167 Creazione del parser ed elaborazione del documento XML Al prossimo gestore verrà assegnato il nome end_element. Questo gestore viene richiamato per rispondere ai tag di fine presenti nel documento e utilizza la stessa istruzione switch che è stata introdotta precedentemente per emettere il codice HTML appropriato quando i singoli tag di fine vengono rilevati. Esempio 7.3. saxdemo.php (estratto) function end_element($parser, $element_name) { switch ($element_name) { case ‘KEYWORDS’: echo ‘</ul>’; break; case ‘KEYWORD’: echo ‘</li>’; break; } } Infine, occorre trovare un modo per gestire i dati di tipo character. A tale scopo, viene creata una funzione character_data che visualizzi il valore di ciascun nodo che rileva. È bene ricordare che in XML, con “dati di tipo character” si indica tutto il testo che non sia markup; questo gestore visualizza tutte le informazioni che si trovano tra i tag di inizio e di fine di un elemento. Esempio 7.4. saxdemo.php (estratto) function character_data($parser, $data) { echo htmlentities($data); } Dopo aver definito le funzioni personalizzate per la gestione del contenuto e dei tag, è necessario creare un’istanza del parser. Creazione del parser ed elaborazione del documento XML I passi successivi sono relativamente semplici, anche perché gran parte del lavoro viene coordinata dalle funzioni di gestione appena create. Tutto ciò che occorre fare è creare il parser e indicargli quali funzioni sono state create per gestire i tag di inizio, i tag di fine e i dati di tipo character: Esempio 7.5. saxdemo.php (estratto) $parser = xml_parser_create(); xml_set_element_handler($parser, ‘start_element’, ‘end_element’); xml_set_character_data_handler($parser, ‘character_data’); 167 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 168 Capitolo 7: Elaborazione di dati XML con PHP Dopo aver fatto ciò, è possibile utilizzare la funzione PHP fopen standard per aprire il documento XML: Esempio 7.6. saxdemo.php (estratto) $fp = fopen(‘keyword-data.xml’, ‘r’) or die (“Cannot open keyword-data.xml!”); Con il documento XML, è possibile utilizzare un semplice loop while per leggere blocchi di codice gestibili (4 KB è una dimensione ragionevole) ed eseguirli attraverso il parser con la funzione xml_parse. In caso di errore, viene utilizzata la funzione die di PHP per visualizzare il messaggio di errore fornito da xml_error_string sulla base del codice di errore fornito da xml_get_error_code. xml_get_current_line_number viene utilizzato per puntare alla riga in cui si è verificato l’errore. Esempio 7.7. saxdemo.php (estratto) while ($data = fread($fp, 4096)) { xml_parse($parser, $data, feof($fp)) or die(sprintf(‘XML ERROR: %s at line %d’, xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser))); } Una volta terminato il loop, liberare il parser utilizzando xml_parser_free. Esempio 7.8. saxdemo.php (estratto) xml_parser_free($parser); Per assimilare quanto illustrato finora, è opportuno ricapitolare tutte le singole attività che sono state completate: 1. Creazione di gestori per tag di inizio, tag di fine e dati di tipo character. 2. Avvio del parser. 3. Registrazione dei gestori personalizzati con il parser. 4. Apertura del file XML. 5. Esecuzione di un loop nel file, inviando singoli blocchi di dati attraverso il parser. 6. Utilizzo della funzione di rilevazione errori incorporata per identificare eventuali problemi. 7. Disimpegno del parser una volta completate tutte le operazioni. Come è possibile notare, l’approccio SAX è molto semplice e chiaro. Tuttavia, non presenta alcune delle più efficaci funzioni che sono invece disponibili con altri approcci come, ad esempio, il modello DOM. 168 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 169 Utilizzo di DOM Utilizzo di DOM In questa sezione verrà illustrato come opera la funzionalità DOM in PHP. Il modello DOM è una tecnologia robusta e affidabile che consente di gestire svariate situazioni. Infatti, il principale punto di forza di questo standard è proprio la sua grande flessibilità. Per quanto concerne gli svantaggi, invece, è possibile citare l’estrema complessità, la vastità e il fatto che, quando si carica in memoria un documento di grandi dimensioni, può addirittura bloccare le risorse. L’utilizzo di DOM sul lato client è stato già illustrato. Per quanto riguarda il lato server, il DOM è un’interfaccia standardizzata. Il modo più semplice per illustrare l’utilizzo di DOM sul lato server consiste nel confrontarlo con le caratteristiche di SAX descritte nella sezione precedente. Come si è visto, è possibile utilizzare SAX per elaborare documenti XML in maniera molto lineare. La funzionalità DOM di PHP adotta un approccio differente. Carica un documento XML in memoria e lo converte in una propria struttura gerarchica di oggetti, fornendo due funzioni molto importanti che in SAX mancano. La prima è la possibilità di manipolare la struttura XML in memoria, consentendo l’aggiunta, la rimozione, la modifica e la ridisposizione dei nodi. La seconda è quella che consente di esaminare il documento continuamente, con accesso casuale, e non solo in maniera lineare. Per un confronto dettagliato dei due approcci, utilizzare di nuovo il file XML contenuto nella sezione dedicata al parser SAX: Esempio 7.9. keyword-data.xml <?xml version=”1.0” encoding=”iso-8859-1”?> <keywords> <keyword>XML</keyword> <keyword>PHP</keyword> <keyword>Perl</keyword> <keyword>JavaScript</keyword> <keyword>ASP</keyword> </keywords> A questo punto, creare un parser DOM e caricare il file XML. Creazione di un parser DOM Per creare il parser, occorre creare un oggetto DOMDocument. Prima di utilizzarlo, è necessario disattivarne la funzione di gestione degli spazi vuoti per impedire che spazi, tab e interruzioni di riga tra i tab vengano gestiti come nodi di testo: 169 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 170 Capitolo 7: Elaborazione di dati XML con PHP Esempio 7.10. domdemo.php (estratto) $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; Successivamente, occorre caricare il documento XML e assegnare un riferimento all’elemento documento: Esempio 7.11. domdemo.php (estratto) $doc->load(“keyword-data.xml”); $root = $doc->documentElement; Recupero degli elementi Dopo aver creato il parser e caricato il file XML, è possibile richiamare informazioni dal documento. Uno dei modi più semplici di utilizzare il modello DOM consiste nel richiamare elementi servendosi del metodo getElementsByTagName. Il metodo getElementsByTagName consente di acquisire tutti gli elementi contenuti in un altro elemento che presentano un determinato nome. È possibile utilizzare questo metodo per acquisire tutti i singoli elementi keyword nel documento di esempio e memorizzarli in un array: Esempio 7.12. domdemo.php (estratto) $keywords = $root->getElementsByTagName(‘keyword’); Dal momento che il metodo è stato richiamato sull’elemento root del documento, l’array $keywords contiene ora ogni singolo elemento keyword del documento XML. A questo punto è possibile eseguire un’iterazione nell’array $keywords per assicurarsi che ciascun elemento sia valido. Successivamente, è possibile eseguire attività di elaborazione su quell’elemento. Esempio 7.13. domdemo.php (estratto) echo ‘<ul>’; foreach ($keywords as $kw) { echo ‘<li>’ . htmlentities($kw->nodeValue) . ‘</li>’; } echo ‘</ul>’; Il risultato viene visualizzato nel browser Web come illustrato nella Figura 7.2, “Esempio di elaborazione di parole chiave”. 170 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 171 Recupero degli elementi Figura 7.2. Esempio di elaborazione di parole chiave Finora i risultati ottenuti sono praticamente gli stessi di quelli conseguiti con SAX. La differenza principale sta nell’approccio: in SAX, è stato necessario creare funzioni personalizzate che gestissero i vari tipi di tag mano a mano che venivano rilevati dal parser; con DOM, invece, è possibile accedere ai diversi livelli della struttura gerarchica del nodo nell’ordine desiderato. Per quanto concerne l’uso di XML e DOM, vi è ancora un altro aspetto da sottolineare. Se si desidera modificare ciò che viene emesso come output oppure il formato delle modifiche XML, di solito è preferibile eseguire queste modifiche in DOM piuttosto che in SAX. Ad esempio, si supponga che ciascuno degli elementi keyword disponga di un attributo status, come riportato di seguito: Esempio 7.14. keyword-data2.xml (estratto) <?xml version=”1.0” encoding=”iso-8859-1”?> <keywords> <keyword status=”live”>XML</keyword> <keyword status=”in progress”>PHP</keyword> <keyword status=”live”>Perl</keyword> <keyword status=”live”>Javascript</keyword> <keyword status=”in progress”>ASP</keyword> </keywords> A questo punto, si desidera visualizzare l’attributo status di keyword insieme alla parola chiave. A tale scopo, occorre apportare solo una semplice modifica: 171 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 172 Capitolo 7: Elaborazione di dati XML con PHP Esempio 7.15. domdemo2.php (estratto) echo ‘<ul>’; foreach ($keywords as $kw) { echo ‘<li>’ . htmlentities($kw->nodeValue) . ‘ (‘ . htmlentities($kw->getAttribute(‘status’)) . ‘)</li>’; } echo ‘</ul>’; Il risultato di questa piccola modifica viene illustrato nella Figura 7.3, “Modifica dell’elenco delle parole chiave”. Figura 7.3. Modifica dell’elenco delle parole chiave Per visualizzare solo le parole chiave “live”, è sufficiente aggiungere un semplice test if: Esempio 7.16. domdemo3.php (estratto) echo ‘<ul>’; foreach ($keywords as $kw) { if ($kw->getAttribute(‘status’) == ‘live’) { echo ‘<li>’ . htmlentities($kw->nodeValue) . ‘</li>’; } } echo ‘</ul>’; Il risultato viene illustrato nella Figura 7.4, “Visualizzazione delle sole parole chiave ‘live’”. 172 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 173 Creazione di nodi Figura 7.4. Visualizzazione delle sole parole chiave ‘live’ Creazione di nodi Come evidenziato precedentemente, il modello DOM consente di aggiungere nuovi nodi e di manipolare quelli esistenti. Creare, dunque, un nuovo elemento keyword e aggiungerlo alla struttura DOM esistente. È possibile aggiungere un nuovo nodo utilizzando il metodo create_element dell’oggetto DOMDocument. A tale scopo è sufficiente passare il nome del nuovo nodo e, facoltativamente, il relativo valore del nodo (il testo che dovrebbe contenere): Esempio 7.17. domdemo4.php (estratto) $newKW = $doc->createElement(‘keyword’, ‘XSLT’); Successivamente occorre associare il nuovo nodo parola chiave alla struttura DOM. A tale scopo, è possibile utilizzare il metodo appendChild, ma è necessario indicare a DOM il punto esatto in cui si desidera aggiungere il nodo. In questo caso specifico, è sufficiente aggiungere il nuovo elemento parola chiave alla variabile $root stabilita (è bene ricordare che questa variabile rappresenta l’elemento root del documento, in questo esempio, keywords). Esempio 7.18. domdemo4.php (estratto) $root->appendChild($newKW); Con un documento modificato a disposizione, è possibile effettuare numerose operazioni. Una delle operazioni più comuni consiste nell’emettere l’output del codice XML del documento modificato in un file sul server oppure in un browser Web. 173 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 174 Capitolo 7: Elaborazione di dati XML con PHP Visualizzazione di codice XML da DOM La visualizzazione di una struttura DOM come codice XML è piuttosto semplice. Infatti, è sufficiente utilizzare il metodo saveXML della classe DOMDocument. Impostando come prima cosa la proprietà formatOutput dell’oggetto su true, è possibile generare il codice XML desiderato. Esempio 7.19. domdemo4.php (estratto) $doc->formatOutput = true; echo ‘<p>Updated XML source code:</p>’; echo ‘<pre>’ . htmlentities($doc->saveXML()) . ‘</pre>’; Questa rapida analisi ha illustrato in maniera molto generica la funzionalità del modello DOM. Maggiori dettagli verranno forniti nel corso di questo capitolo e in quelli successivi, soprattutto quando sarà necessario ricorrere a questo modello per la creazione immediata di documenti XML. Utilizzo di SimpleXML Prima dell’introduzione di SimpleXML in PHP 5, gli sviluppatori potevano manipolare ed elaborare i documenti XML solo con SAX e DOM. Entrambi gli approcci erano consolidati e affidabili, ma il loro uso presupponeva una conoscenza approfondita da parte dello sviluppatore dei processi che avrebbe creato. Con SimpleXML, come implica il nome, si semplifica il modo in cui uno sviluppatore PHP interagisce, elabora e manipola i dati XML. SimpleXML offre, infatti, grandi vantaggi. Sostanzialmente, SimpleXML carica i dati XML all’interno di una gerarchia di oggetti e array di oggetti, quindi consente di accedere all’array utilizzando metodi familiari quali i loop foreach e gli indici di array. In altre parole, gli sviluppatori di SimpleXML si erano resi conto che XML veniva utilizzato prevalentemente per l’estrazione e l’elaborazione di informazioni, quindi studiarono un modo che rendesse più semplice eseguire questo tipo di operazioni. Come è stato evidenziato nel Capitolo 4, Visualizzazione di XML in un browser, SimpleXML assegna a ciascun tag presente in un file XML una proprietà che corrisponde al relativo nome dell’elemento. Esaminare il seguente documento XML: <document> <msg>Hello</msg> </document> 174 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 175 Caricamento di documenti XML In questo caso è possibile accedere al valore del tag <msg> utilizzando la notazione freccia orientata agli oggetti di PHP, come mostrato di seguito: echo $xml->msg; Le modifiche dei nomi degli elementi influiscono sul codice PHP Uno dei grandi svantaggi di questo approccio è rappresentato dal fatto che se, per un qualsiasi motivo, i nomi degli elementi XML subiscono una modifica, sarà necessario ritornare indietro e modificare la logica SimpleXML perché vi sia corrispondenza con i nuovi nomi. Il modo migliore per evitare questo tipo di problemi consiste nell’assicurarsi che i documenti XML siano definitivi e strutturati nella maniera richiesta. Se si apportano modifiche ai documenti XML dopo aver iniziato la codifica PHP, occorre intervenire anche nel codice per garantire la corrispondenza con le nuove modifiche apportate. Nelle sezioni successive verranno illustrate tutte le principali funzioni di SimpleXML. Caricamento di documenti XML Per utilizzare SimpleXML, come prima cosa occorre identificare il documento XML che si desidera elaborare. SimpleXML può caricare un file in memoria oppure lavorare sul codice XML sotto forma di una stringa PHP. Per aprire un file, utilizzare la funzione simplexml_load_file come riportato di seguito: Esempio 7.20. sxmldemo.php (estratto) $keywords = simplexml_load_file(‘keyword-data2.xml’); È anche possibile fornire il proprio XML come variabile, quindi caricare questa variabile in memoria utilizzando la funzione simplexml_load_string: Esempio 7.21. sxmldemo2.php (estratto) $xml = <<<XML <?xml version=”1.0” encoding=”iso-8859-1”?> <keywords> <keyword status=”live”>XML</keyword> <keyword status=”in progress”>PHP</keyword> <keyword status=”live”>Perl</keyword> <keyword status=”live”>JavaScript</keyword> <keyword status=”in progress”>ASP</keyword> </keywords> XML; $keywords = simplexml_load_string($xml); 175 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 176 Capitolo 7: Elaborazione di dati XML con PHP Dichiarazione di stringhe PHP con heredoc La stringa PHP che contiene il codice XML nell’esempio sopra riportato viene dichiarata utilizzando la sintassi heredoc. Coloro che non conoscono questa sintassi possono consultare il manuale di PHP[3]. Indipendentemente dall’approccio utilizzato, il risultato sarà una gerarchia di oggetti e di array chiamata $keywords. È ora possibile accedere alle diverse parti di questa struttura utilizzando la sintassi di array o di oggetti PHP standard. Gerarchia degli elementi XML Quando un elemento XML contiene più elementi figlio con lo stesso nome (come accade per l’elemento keywords di questo esempio), questi elementi figlio vengono memorizzati in un array nella gerarchia degli oggetti. Pertanto è possibile utilizzare indici di array per accedere alle informazioni necessarie. Ad esempio, è possibile utilizzare la seguente sintassi per accedere alle varie parole chiave contenute nel documento XML: echo $keywords->keyword[0]; // prints “XML” echo $keywords->keyword[1]; // prints “PHP” Si noti che, in ogni caso, si utilizza l’oggetto $keywords, che rappresenta l’elemento root del documento, seguito dall’operatore freccia e dal nome del nodo figlio a cui si è interessati (in questo caso, keyword). Poiché si tratta di un array, si utilizza keyword[0] per accedere al primo elemento keyword. Per accedere al secondo, si utilizza keyword[1] e così via. Per ottenere, invece, tutti i valori di un array di elementi XML creato da SimpleXML, è possibile utilizzare un semplice loop foreach PHP: Esempio 7.22. sxmldemo.php/sxmldemo2.php (estratto) echo ‘<ul>’; foreach ($keywords->keyword as $kw) { echo ‘<li>’ . htmlentities($kw) . ‘</li>’; } echo ‘</ul>’; Quando un elemento contiene solo un elemento figlio di un determinato tipo, non viene creato alcun array. Infatti, è possibile accedere all’elemento direttamente. Si supponga che il file XML che si sta utilizzando sia più complesso e che contenga un altro livello di elementi: nodi figlio di ciascun elemento keyword. Il file potrebbe risultare come riportato di seguito: [3] http://www.php.net/language.types.string#language.types.string.syntax.heredoc 176 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 177 Gerarchia degli elementi XML <keywords> <keyword status=”live”> <name>XML</name> <url>http://www.example.com/xml/</url> </keyword> <keyword status=”in progress”> <name>PHP</name> <url>http://www.example.com/php/</url> </keyword> … </keywords> Per visualizzare i valori degli elementi name e url per il primo elemento keyword, procedere come riportato di seguito: echo $keywords->keyword[0]->name; // “XML” echo $keywords->keyword[0]->url; // “http://www.example.com/xml/” Come è possibile notare, il procedimento è davvero molto semplice. Infatti, è molto più semplice lavorare con SimpleXML che non utilizzare una struttura DOM oppure dichiarare gestori SAX personalizzati. Gli oggetti elemento SimpleXML possono essere utilizzati laddove sono richieste stringhe e si comporteranno come tali, includendo il valore di testo contenuto nel corrispondente elemento XML. Tuttavia, è importante ricordare che, di fatto, questi oggetti non sono stringhe. Questa distinzione scatta nei casi in cui non è richiesta una stringa e pertanto PHP non eseguirà la conversione. Segue un esempio: if ($keywords->keyword[0] == ‘XML’) { echo ‘The first keyword is XML.’; } Analizzando il documento XML di esempio ci si aspetterebbe che questa l’istruzione if faccia eseguire l’istruzione successiva e visualizzare il messaggio, ma ciò non si verifica in quanto $keywords->keyword[0] è un oggetto e come tale non è uguale alla stringa ‘XML’. Perché questo codice si comporti come previsto, è necessario eseguire la conversione attraverso l’esecuzione del cast dell’oggetto in una stringa: if ((string)$keywords->keyword[0] == ‘XML’) { echo ‘The first keyword is XML.’; } 177 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 178 Capitolo 7: Elaborazione di dati XML con PHP Se non si conosce bene PHP, potrebbe risultare alquanto difficile capire quando occorre eseguire la conversione e quando invece questa operazione verrà eseguita da PHP. Gli esempi contenuti in questo manuale dovrebbero chiarire questo aspetto, ma in caso di dubbi, si consiglia di eseguire il cast del valore in una stringa: in tal modo, si avrà sempre la certezza che PHP gestirà il valore nella maniera prevista. Valori degli attributi XML È possibile accedere al valore di un attributo gestendo l’oggetto elemento come array e utilizzando il nome dell’attributo come chiave di array. Ad esempio, per visualizzare il valore dell’attributo status del primo elemento keyword, è possibile utilizzare la seguente istruzione: echo $keywords->keyword[0][‘status’]; // prints “live” Tuttavia, con i valori degli elementi, il discorso si complica quando si tenta di confrontare valori degli attributi con stringhe PHP, in quanto sono fondamentalmente oggetti. Ad esempio, si supponga di voler visualizzare solo parole chiave “live”. In tal caso, si potrebbe procedere come riportato di seguito: foreach ($keywords->keyword as $kw) { if ($kw[‘status’] == ‘live’) { // wrong! echo ‘<li>’ . htmlentities($kw) . ‘</li>’; } } Questo frammento di codice sembra accettabile. Tuttavia, dal momento che PHP non può eseguire il confronto di due entità diverse, in questo caso, oggetti e stringhe, nessuna delle parole chiave verrà visualizzata. Pertanto, è necessario eseguire il cast dell’oggetto attributo in una stringa: Esempio 7.23. sxmldemo3.php (estratto) foreach ($keywords->keyword as $kw) { if ((string)$kw[‘status’] == ‘live’) { echo ‘<li>’ . htmlentities($kw) . ‘</li>’; } } In questo caso si è scelto di imboccare la strada più difficile. Tuttavia, grazie a Xpath esiste un metodo più semplice per filtrare i valori che vengono richiamati da un documento XML. 178 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 179 Query XPath Query XPath Esiste un altro modo per filtrare elementi XML basati sul valore di un attributo, che implica l’uso del metodo xpath incontrato nel Capitolo 4, Visualizzazione di XML in un browser. Come già evidenziato, questo metodo consente di passare query XPath in SimpleXML e di ricevere i risultati sotto forma di array. Il modo più semplice per acquisire tutti gli elementi keyword il cui attributo status sia impostato su live consiste nell’eseguire la query XPath riportata di seguito dall’elemento keyword nel documento: keyword[@status=”live”] Questa query esaminerà tutti gli elementi keyword che sono elementi figlio dell’elemento corrente ed estrarrà tutti quelli il cui status sia live. Di seguito viene illustrato come effettuare questa operazione con SimpleXML: Esempio 7.24. sxmldemo4.php (estratto) foreach ($keywords->xpath(‘keyword[@status=”live”]’) as $kw) { echo ‘<li>’ . htmlentities($kw) . ‘</li>’; } Ma quali sono i vantaggi che offre una query XPath? In primo luogo, non è più necessario eseguire un confronto tra stringhe all’interno di PHP: questa operazione viene gestita da XPath. Se si ha familiarità con lo sviluppo di database in PHP, questo tipo di operazione equivale a consentire a una istruzione SQL di eseguire il filtraggio dei valori di database prima di restituirli nello script. In secondo luogo, il codice risulta ora meno complesso e, fortunatamente, anche più semplice da gestire. Analizzando il secondo costrutto, si può ritenere questo approccio molto più complicato che non eseguire il cast di un oggetto in una stringa per il confronto. Tuttavia, la difficoltà e la semplicità di un approccio sono relative. Utilizzo di SimpleXML per aggiornare codice XML Un altro dei grandi vantaggi offerti da SimpleXML è rappresentato dal fatto che consente di aggiornare i valori in maniera molto semplice. Si supponga di voler modificare la terza parola chiave dell’esempio da PHP a JSP. Di seguito viene mostrato come procedere: 179 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 180 Capitolo 7: Elaborazione di dati XML con PHP $keywords->keyword[2] = ‘JSP’; Per modificare il valore di un attributo, è sufficiente aggiungere il nome dell’attributo appropriato come chiave di array: $keywords->keyword[2][‘status’] = ‘live’; Per salvare oppure visualizzare il codice XML aggiornato, utilizzare il metodo asXML: echo $keywords->asXML(); Correzione di problemi SimpleXML con DOM SimpleXML, pur nella sua semplicità ed eleganza, presenta alcuni svantaggi. In primo luogo, non è possibile (al momento della scrittura) aggiungere nuovi nodi a un documento XML, ma solo modificare i valori di quelli esistenti. Non è possibile neppure eliminare nodi. Non è possibile associare fogli di stile al proprio XML per eseguire trasformazioni. Infine, come è stato possibile notare, il codice PHP dipende fortemente dai nomi dei tag XML: se questi vengono modificati, l’aggiornamento del codice può essere una vera seccatura. Ad ogni modo, SimpleXML dispone di una funzione davvero comoda che gli consente di interoperare con altre funzionalità XML PHP. Precisamente, questa funzionalità permette di importare documenti DOM e di convertirli in strutture di dati SimpleXML. Ma quali sono i vantaggi? Questa funzionalità consente di attuare la parte più impegnativa della funzionalità DOM nativa in PHP; successivamente è possibile convertirla in SimpleXML e completare il lavoro. Per comprendere quanto appena illustrato attraverso un esempio pratico, caricare un documento con DOM e apportarvi una modifica che non sarebbe possibile eseguire con SimpleXML: Esempio 7.25. sxmldemo5.php (estratto) $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; $doc->load(“keyword-data2.xml”); $root = $doc->documentElement; $newKW = $doc->createElement(‘keyword’, ‘JSP’); $newKW->setAttribute(‘status’, ‘live’); $root->appendChild($newKW); Con un nuovo elemento aggiunto alla struttura DOM, è possibile utilizzare la funzione simplexml_import_dom per importarlo in una struttura di dati SimpleXML. Successivamente, 180 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 181 Quando utilizzare i diversi metodi è possibile utilizzare l’API SimpleXML per eseguire tutte le operazioni richieste: ad esempio, ricercare e visualizzare nodi con un determinato valore di attributo. Esempio 7.26. sxmldemo5.php (estratto) $keywords = simplexml_import_dom($doc); echo ‘<ul>’; foreach ($keywords->xpath(‘keyword[@status=”live”]’) as $kw) { echo ‘<li>’ . htmlentities($kw) . ‘</li>’; } echo ‘</ul>’; Questo codice consente di visualizzare un elenco di parole chiave il cui status è live, compresa “JSP”, che è stata aggiunta utilizzando DOM. Quando utilizzare i diversi metodi A questo punto è lecito domandarsi quando è appropriato utilizzare SAX, DOM e/o SimpleXML. Di seguito vengono elencate alcune regole generali: ❑ Nel caso di un documento XML molto semplice che richiede unicamente una elaborazione lineare e che non deve essere modificato, SAX rappresenta la scelta appropriata. ❑ Se occorre elaborare un documento XML più complesso, ma al solo scopo di estrarre informazioni, SimpleXML rappresenta la scelta appropriata. ❑ Se occorre creare nuovi documenti XML oppure apportare modifiche sostanziali a documenti esistenti, utilizzare DOM. Progetto CMS A questo punto è possibile creare le funzionalità di amministrazione del progetto CMS. Gli strumenti di amministrazione di cui si dispone consentiranno agli utenti autorizzati di collegarsi a CMS e di amministrare le varie parti del sito Web. Più specificamente, si procederà alla realizzazione delle seguenti funzionalità: ❑ pagina di accesso/verifica ❑ indice amministrativo (da cui gli utenti di CMS possono accedere ad altre pagine) ❑ pagina per la creazione di nuovi articoli 181 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 182 Capitolo 7: Elaborazione di dati XML con PHP ❑ pagina per la modifica degli articoli ❑ pagina per l’eliminazione degli articoli Dopo aver creato queste pagine, il discorso sullo strumento di amministrazione verrà temporaneamente interrotto e ripreso poi nell’Appendice B, Strumento di amministrazione di CMS, in quanto gran parte delle informazioni è piuttosto ripetitiva e può essere trattata in una sezione a parte. Questa trattazione include: ❑ pagine per la creazione/modifica/eliminazione di notizie ❑ pagine per la creazione/modifica/eliminazione di file binari ❑ pagine per la creazione/modifica/eliminazione di categorie ❑ pagine per la creazione/modifica/eliminazione di amministratori Tutte le pagine dell’area di amministrazione si trovano nella directory admin del sito. Disabilitazione della funzione magic quotes Tutti gli script PHP presenti in questo libro presuppongono che la funzione magic quotes di PHP sia disabilitata. Per disabilitare questa funzione, impostare l’opzione magic_quotes_gpc contenuta nel file php.ini del proprio server su Off. Se questa funzione viene lasciata attiva, quando si manipola il contenuto utilizzando gli script di amministrazione che verranno sviluppati in questa sezione e nell’Appendice B, Strumento di amministrazione di CMS, il contenuto presenterà numerose barre rovesciate (\) indesiderate. Pagina di accesso La pagina di accesso è molto semplice. Si basa su un elementare form HTML che consente agli amministratori di specificare il proprio nome utente e la propria password. Questo form diventerà più complesso quando si strutturerà l’HTML in modo che possa essere efficacemente stilizzato grazie all’applicazione di un CSS. Esempio 7.27. login.php <?php session_start(); ?> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <title>Please Log In</title> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1”> 182 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 183 Pagina di accesso <link rel=”stylesheet” type=”text/css” href=”../xmlcms.css” /> <link rel=”stylesheet” type=”text/css” href=”login.css” /> </head> <body> <form class=”login” action=”verify.php” method=”post”> <h1>Please log in</h1> <div class=”fields”> <label for=”username”>User name</label> <input type=”text” id=”username” name=”username” class=”text” /> <label for=”password”>Password</label> <input type=”password” id=”password” name=”password” class=”password” /> </div> <div class=”actions”> <input type=”submit” value=”Submit” /> <input type=”reset” value=”Reset” /> </div> <p class=”error”><?php echo $_SESSION[‘error’]; ?></p> </form> </body> </html> Oltre al foglio di stile del sito di base (xmlcms.css), questa pagina utilizza un foglio di stile specifico per il layout del form di accesso. Come già evidenziato in precedenza, non essendo questo un manuale dedicato ai fogli di stile CSS, i dettagli relativi a questo file verranno tralasciati, ma per una questione di completezza di seguito ne viene fornito il codice: Esempio 7.28. login.css form.login { width: 290px; margin: 1em auto; padding: 4px; background: #ccc; } form.login h1 { text-align: center; font-size: medium; background: #fff; margin: 1px; } form.login .fields { text-align: right; } form.login label { float: left; width: 130px; text-align: right; 183 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 184 Capitolo 7: Elaborazione di dati XML con PHP } form.login .actions { text-align: center; } form.login input.text, form.login input.password { width: 150px; margin-bottom: 1px; } form.login .error { color: red; margin: 0; padding: 0; } Analizzando la pagina di accesso, è possibile notare che l’elemento action del form è impostato su una pagina denominata verify.php. La logica PHP contenuta in questa pagina deve verificare i valori immessi in un elenco di amministratori che si trova in admin.xml. Protezione dei file XML Ai fini degli esempi proposti in questo manuale e per l’archiviazione del relativo codice, i file XML in cui si trovano il contenuto, le categorie, gli autori e gli utenti amministrativi sono stati inseriti in una sottodirectory del sito principale. Tuttavia, per proteggere ulteriormente le informazioni riservate contenute in questi file come, ad esempio, la crittografia delle password degli amministratori, fare in modo che i visitatori occasionali del sito non abbiano accesso diretto a questi file. Per proteggere questi file, è possibile configurare il server Web in modo che impedisca ai browser di accedere alla directory oppure è possibile spostare la directory all’esterno della struttura di directory del sito accessibile dal Web. Nel secondo caso, sarà necessario modificare il file common.inc.php perché punti alla nuova ubicazione della directory, in modo che tutti gli script del sito possano trovare i file XML. Password crittografate Come è stato possibile notare, i valori delle password memorizzati in admin.xml vengono crittografati, così da garantire maggiore protezione. Per utilizzare il file di esempio admin.xml incluso nell’archivio del codice per questo libro, occorre sapere che la password iniziale di tutti e tre gli amministratori memorizzata in quel file (joe, bill e tom) è password. In caso di corrispondenza, il codice PHP imposterà una variabile di sessione e reindirizzerà l’utente verso la pagina dell’indice di amministrazione. In caso contrario, PHP riporterà l’utente di nuovo alla pagina login.php, con una variabile di sessione speciale ($_SESSION[‘error’]) contenente un messaggio di errore da visualizzare. 184 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 185 Pagina di accesso Il codice specifico viene riportato di seguito. Si noti che in questo caso si è scelto di utilizzare SimpleXML, in quanto è il metodo più veloce per caricare le informazioni da admin.xml. Esempio 7.29. verify.php <?php include_once ‘../common.inc.php’; $admins = simplexml_load_file($fileDir . ‘admin.xml’); foreach ($admins->admin as $admin) { if ($_POST[‘username’] == (string)$admin->username and crypt($_POST[‘password’], (string)$admin->password) == (string)$admin->password) { $_SESSION[‘login’] = true; header(‘location: index.php’); exit; } } $_SESSION[‘error’] = ‘Wrong user name or password. Try again.’; header(‘location: login.php’); ?> Inoltre, quando vengono crittografate le password in admin.xml, occorre accettare la password fornita dall’utente, crittografarle allo stesso modo, quindi confrontare l’input utente con la password memorizzata nel file XML. Se non si ha familiarità con la funzione crypt di PHP, è possibile consultare il manuale di PHP[4]. Dal momento che chiunque può immettere un URL per una delle pagine di amministrazione, è necessario garantire per il contenuto di queste pagine una maggiore protezione. Nella parte superiore di ciascuna pagina, occorre verificare se il valore della variabile di sessione $_SESSION[‘login’] è impostato su true. Se questo valore non è impostato su true, l’utente verrà indirizzato di nuovo verso la pagina login.php. Dal momento che questa funzionalità verrà utilizzata di nuovo, si consiglia di inserire questa verifica all’interno di un file delle inclusioni: Esempio 7.30. security.inc.php <?php include_once ‘../common.inc.php’; if (@$_SESSION[‘login’] !== true) { header(‘location: login.php’); $_SESSION[‘error’] = ‘You must log in before you can access administration pages.’; exit; } ?> [4] http://www.php.net/crypt 185 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 186 Capitolo 7: Elaborazione di dati XML con PHP Disabilitazione della protezione per la configurazione iniziale Quando si configura il sito per la prima volta, è possibile che non siano stati ancora creati degli amministratori. Ci si chiede dunque come fare ad aggiungere amministratori se non è possibile eseguire l’accesso. Per aggiungere i primi account amministratore, è necessario disabilitare la protezione: a tale scopo, annullare i commenti del contenuto del file security.inc.php, in modo che il file non possa eseguire alcuna azione. Successivamente, è necessario abilitare la visualizzazione libera delle pagine di amministrazione e aggiungere uno o due amministratori. Dopo di che, è possibile abilitare di nuovo la protezione e collegarsi utilizzando uno degli account appena creati. In ultimo, occorre creare una pagina di logout a cui collegarsi in modo che gli utenti possano scollegarsi e uscire dal sito prima di abbandonare la propria postazione: Esempio 7.31. logout.php <?php include_once ‘../common.inc.php’; $_SESSION[‘login’] = false; header(‘location: index.php’); ?> Pagina dell’indice amministrativo La prima pagina dello strumento di amministrazione fornisce accesso a tutte le funzionalità e all’intero contenuto del sito, compresi gli utenti amministrativi, gli articoli, i news item e altri tipi di contenuto. Il codice per questa pagina è molto conciso. È necessario fornire collegamenti a ciascuna delle principali parti dello strumento di amministrazione. Esempio 7.32. index.php <?php include ‘security.inc.php’; ?> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1” /> <title>Welcome to the Admin Index Page</title> <link rel=”stylesheet” type=”text/css” href=”../xmlcms.css” /> 186 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 187 Utilizzo degli articoli </head> <body> <h1>Welcome to the Admin Index Page</h1> <p> <a href=”articletool.php”>Manage Articles</a><br /> <a href=”newstool.php”>Manage News Items</a><br /> <a href=”admintool.php”>Manage Administrators</a> </p> <p><a href=”logout.php”>Log out</a></p> </body> </html> Ciascuna delle aree rappresentate da questi collegamenti avrà la stessa funzionalità. Ogni singola area consentirà agli utenti di CMS di creare, modificare ed eliminare item. Nelle sezioni successive verranno analizzate le singole attività per la prima area: gli articoli. Utilizzo degli articoli L’area di amministrazione degli articoli consente agli utenti di CMS di creare nuovi articoli, modificare quelli esistenti e di eliminare articoli quando occorre. Dal momento che gli articoli sono la parte più importante del sito, si partirà da essi per poi procedere con i news item e gli amministratori del sito nell’Appendice B, Strumento di amministrazione di CMS. Per la pagina principale dell’area di amministrazione degli articoli, sarà necessario un elenco di tutti gli articoli presenti sul sito. Poiché si suppone di aver acquisito sufficiente dimestichezza con il codice PHP, questa operazione non dovrebbe risultare troppo complicata: Esempio 7.33. articletool.php <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; ?> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1” /> <title>Article Index</title> <link rel=”stylesheet” type=”text/css” href=”../xmlcms.css” /> </head> <body> <h1>Article Index</h1> 187 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 188 Capitolo 7: Elaborazione di dati XML con PHP <p><a href=”articletool_create.php”>Create New Article</a></p> <p><a href=”index.php”>Cancel</a></p> <ul> <?php $handle = opendir($fileDir); while (($file = readdir($handle)) !== FALSE) { if (is_dir($fileDir . $file)) continue; if (!eregi(“^article.*\.xml$”, $file)) continue; $articleFile = simplexml_load_file($fileDir . $file); echo ‘<li>’ . htmlentities($articleFile->headline); echo ‘ <a href=”articletool_edit.php?id=’ . $articleFile[‘id’] . ‘”>edit</a>’; echo ‘ <a href=”doArticleDelete.php?id=’ . $articleFile[‘id’] . ‘”>delete</a></li>’; } ?> </ul> </body> </html> Creazione di nuovi articoli La pagina di creazione degli articoli è molto importante, in quanto consente agli amministratori del sito di creare nuovi articoli XML sul sito utilizzando un semplice form Web. Ciascuno dei campi del form è associato a un elemento nella struttura del documento XML pianificata nel Capitolo 1, Introduzione a XML, e modificata nel Capitolo 3, DTD per la consistenza. Gli articoli creati dovrebbero risultare come quello riportato di seguito: <?xml version=”1.0” encoding=”iso-8859-1”?> <article id=”article12499300388912”> <authorid>1</authorid> <categoryid>1</categoryid> <headline>Using XML with PHP</headline> <description>PHP offers many ways to work with XML</description> <pubdate>2004-06-26</pubdate> <status>live</status> <keywords>XML PHP SAX DOM SimpleXML</keywords> <body><![CDATA[ <h1>Using XML with PHP</h1> <p>PHP is very powerful. It offers many ways to work with XML.</p> ]]></body> </article> 188 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 189 Creazione di nuovi articoli In particolare, è bene ricordare che occorre generare un ID univoco e compilare tutte le varie parti dei metadati. Creare un form che contenga campi per tutte queste informazioni. Iniziare con la verifica che permette di assicurarsi che l’utente è collegato: Esempio 7.34. articletool_create.php (estratto) <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; ?> Iniziare ora la creazione del codice HTML, includendo un collegamento a un nuovo foglio di stile denominato forms.css, che conterrà le regole per il layout di form di amministrazione piuttosto complessi come quello riportato di seguito: Esempio 7.35. articletool_create.php (estratto) <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1” /> <title>Create a New Article</title> <link rel=”stylesheet” type=”text/css” href=”../xmlcms.css” /> <link rel=”stylesheet” type=”text/css” href=”forms.css” /> </head> <body> Infine, creare il form. Il form è costituito prevalentemente da semplici campi, ma per selezionare un autore e una categoria occorrono elenchi dinamici che verranno estratti dai file appropriati utilizzando SimpleXML. Esempio 7.36. articletool_create.php (estratto) <h1>Create a New Article</h1> <p><a href=”articletool.php”>Cancel</a></p> <form action=”doArticleCreate.php” method=”post”> <div class=”fields”> <p> <label for=”headline”>Headline</label> <input type=”text” id=”headline” name=”headline” class=”text” /> </p> <p> <label for=”author”>Author</label> <select id=”authorid” name=”authorid”> <?php 189 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 190 Capitolo 7: Elaborazione di dati XML con PHP $authors = simplexml_load_file($fileDir . ‘authors.xml’); foreach ($authors->author as $author) { echo ‘<option value=”’ . htmlentities($author[‘id’]) . ‘”>’ . htmlentities($author->name) . ‘</option>’; } ?> </select> </p> <p> <label for=”category”>Category</label> <select id=”categoryid” name=”categoryid”> <?php $cats = simplexml_load_file($fileDir . ‘categories.xml’); foreach ($cats->category as $cat) { echo ‘<option value=”’ . htmlentities($cat[‘id’]) . ‘”>’ . htmlentities($cat[‘label’]) . ‘</option>’; } ?> </select> </p> <p> <label for=”status”>Status</label> <select id=”status” name=”status”> <option value=”in progress”>In Progress</option> <option value=”live”>Live</option> </select> </p> <p> <label for=”keywords”>Keywords</label> <input type=”text” id=”keywords” name=”keywords” class=”text” /> </p> <p> <label for=”description”>Description</label> <textarea id=”description” name=”description”></textarea> </p> <p> <label for=”body”>Article Body (HTML)</label> <textarea id=”body” name=”body”></textarea> </p> </div> <div class=”actions”> <input type=”submit” value=”Add Article” /> <input type=”reset” value=”Reset” /> </div> </form> </body> </html> 190 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 191 Creazione di nuovi articoli Di seguito viene riportato il codice del foglio di stile che esegue il layout di questo complesso form: Esempio 7.37. forms.css form .actions { text-align: center; } form p { clear: left; margin: 1px 0; } form label { float: left; width: 15%; padding-right: 10px; text-align: right; } input.text, input.password, select { width: 300px; } textarea { width: 70%; height: 4em; } textarea#body { height: 30em; } L’action del form è impostata sulla pagina doArticleCreate.php, che utilizza funzioni DOM per creare un articolo XML dalle informazioni contenute nel form. Dal momento che si tratta di un passaggio molto complesso, il codice verrà riportato e analizzato a blocchi. La prima parte del file è l’inizio del nuovo documento XML, in cui viene impostata la versione e creato l’elemento root, article. Esempio 7.38. doArticleCreate.php (estratto) <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; $doc = new DOMDocument(); $root = $doc->createElement(‘article’); $root = $doc->appendChild($root); 191 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 192 Capitolo 7: Elaborazione di dati XML con PHP Successivamente, viene aggiunto un attributo id al nodo article. L’ID sarà composto dal termine article seguito da un timestamp. Nel caso in cui si creino contemporaneamente più di un articolo, ricercare un file esistente il cui nome contenga lo stesso ID e incrementare il timestamp di un’unità fino a individuare un ID che non crei conflitti. Esempio 7.39. doArticleCreate.php (estratto) $timestamp = date(‘YmdHis’); do { $id = ‘article’ . $timestamp++; } while (file_exists($fileDir . $id . ‘.xml’)); $root->setAttribute(‘id’, $id); Dopo aver creato l’elemento root, occorre creare, nell’ordine appropriato, i relativi elementi figlio. Il primo è authorid. Si noti che l’elemento authorid è un elemento figlio di article e che lo stesso ID è un elemento figlio di authorid. Esempio 7.40. doArticleCreate.php (estratto) $author = $doc->createElement(‘authorid’); $root->appendChild($author); $atext = $doc->createTextNode($_POST[‘authorid’]); $author->appendChild($atext); Viene utilizzata la stessa tecnica illustrata sopra per generare gli elementi categoryid, headline, description, status e keywords. Il valore di pubdate viene generato dallo script, ma per il resto è lo stesso. Esempio 7.41. doArticleCreate.php (estratto) $cat = $doc->createElement(‘categoryid’); $root->appendChild($cat); $ctext = $doc->createTextNode($_POST[‘categoryid’]); $cat->appendChild($ctext); $head = $doc->createElement(‘headline’); $root->appendChild($head); $htext = $doc->createTextNode($_POST[‘headline’]); $head->appendChild($htext); $desc = $doc->createElement(‘description’); $root->appendChild($desc); $dtext = $doc->createTextNode($_POST[‘description’]); $desc->appendChild($dtext); $pub = $doc->createElement(‘pubdate’); $root->appendChild($pub); $pubtext = $doc->createTextNode(date(‘Y-m-d’)); $pub->appendChild($pubtext); 192 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 193 Creazione di nuovi articoli $stat = $doc->createElement(‘status’); $root->appendChild($stat); $stext = $doc->createTextNode($_POST[‘status’]); $stat->appendChild($stext); $key = $doc->createElement(‘keywords’); $root->appendChild($key); $ktext = $doc->createTextNode($_POST[‘keywords’]); $key->appendChild($ktext); Successivamente, viene elaborato il testo del corpo. È bene ricordare che tali informazioni dovranno essere archiviate come HTML, il che significa che conterranno numerosi tag. Se si utilizza DOM per emettere questo valore come testo, il file XML risultante sarà difficile da leggere, con entità di caratteri quali < e > sparse in tutto il file. Come già menzionato nel Capitolo 1, Introduzione a XML, per garantire la leggibilità del codice, occorre memorizzare il testo del corpo come sezione CDATA all’interno del file XML, in modo che i caratteri speciali non debbano essere convertiti in entità di caratteri. Anziché utilizzare createTextNode, verrà utilizzato createCDATASection. Il testo HTML del form verrà inserito all’interno di questo comando. Esempio 7.42. doArticleCreate.php (estratto) $body = $doc->createElement(‘body’); $root->appendChild($body); $cdata = $doc->createCDATASection($_POST[‘body’]); $body->appendChild($cdata); Successivamente, scrivere l’intera struttura ad albero XML in un file, utilizzando l’ID generato precedentemente con un nome file: Esempio 7.43. doArticleCreate.php (estratto) $filename = $fileDir . $id . ‘.xml’; $doc->save($filename); Infine, occorre reindirizzare l’utente verso la pagina articletool.php, dove il file appena creato dovrebbe essere contenuto nell’elenco degli articoli principale. Esempio 7.44. doArticleCreate.php (estratto) header(‘location: articletool.php’); 193 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 194 Capitolo 7: Elaborazione di dati XML con PHP Modifica di un articolo XML In genere, la modifica di un articolo XML è pressoché uguale alla creazione di un articolo, ad eccezione del fatto che occorre caricare i valori di un articolo esistente nel form e scrivere le modifiche apportate all’esterno del file. Come prima cosa, creare il codice per la pagina del form. La prima parte dovrebbe risultare ora familiare: infatti, verifica che l’utente sia collegato. Esempio 7.45. articletool_edit.php (estratto) <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; Successivamente, occorre aprire il file per ricercare il documento che l’utente desidera modificare. Poiché per ora non verranno apportate modifiche al file, è possibile utilizzare SimpleXML per aprire il file ed estrarre le informazioni necessarie. Esempio 7.46. articletool_edit.php (estratto) if (!isset($_GET[‘id’]) || $_GET[‘id’] == ‘’ || !file_exists($fileDir . $_GET[‘id’] . ‘.xml’)) { header(‘location: articletool.php’); exit; } $file = simplexml_load_file($fileDir . $_GET[‘id’] . ‘.xml’); ?> Si dispone ora semplicemente della markup per un form molto simile a quello di articletool_create.php, ad eccezione del fatto che questa volta il valore esistente per ciascun file verrà richiamato dalla variabile $file. Esempio 7.47. articletool_edit.php (estratto) <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml”> <head> <meta http-equiv=”Content-Type” content=”text/html; charset=iso-8859-1” /> <title>Edit Article</title> <link rel=”stylesheet” type=”text/css” href=”../xmlcms.css” /> <link rel=”stylesheet” type=”text/css” href=”forms.css” /> </head> <body> <h1>Edit Article</h1> <p><a href=”articletool.php”>Cancel</a></p> <form action=”doArticleUpdate.php” method=”post”> <input type=”hidden” name=”id” value=”<?php echo htmlentities($_GET[‘id’]); ?>” /> 194 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 195 Modifica di un articolo XML <div class=”fields”> <p> <label for=”headline”>Headline</label> <input type=”text” id=”headline” name=”headline” class=”text” value=”<?php echo htmlentities($file->headline); ?>” /> </p> <p> <label for=”author”>Author</label> <select id=”authorid” name=”authorid”> <?php $authors = simplexml_load_file($fileDir . ‘authors.xml’); foreach ($authors->author as $author) { if ((string)$author[‘id’] == (string)$file->authorid) { echo ‘<option value=”’ . htmlentities($author[‘id’]) . ‘” selected=”selected”>’ . htmlentities($author->name) . ‘</option>’; } else { echo ‘<option value=”’ . htmlentities($author[‘id’]) . ‘”>’ . htmlentities($author->name) . ‘</option>’; } } ?> </select> </p> <p> <label for=”category”>Category</label> <select id=”categoryid” name=”categoryid”> <?php $cats = simplexml_load_file($fileDir . ‘categories.xml’); foreach ($cats->category as $cat) { if ((string)$cat[‘id’] == (string)$file->categoryid) { echo ‘<option value=”’ . htmlentities($cat[‘id’]) . ‘” selected=”selected”>’ . htmlentities($cat[‘label’]) . ‘</option>’; } else { echo ‘<option value=”’ . htmlentities($cat[‘id’]) . ‘”>’ . htmlentities($cat[‘label’]) . ‘</option>’; } } ?> </select> </p> <p> <label for=”status”>Status</label> <select id=”status” name=”status”> <option value=”in progress” <?php if ((string)$file->status == ‘in progress’) echo ‘selected=”selected”’?>>In Progress</option> 195 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 196 Capitolo 7: Elaborazione di dati XML con PHP <option value=”live” <?php if ((string)$file->status == ‘live’) echo ‘selected=”selected”’?>>Live</option> </select> </p> <p> <label for=”keywords”>Keywords</label> <input type=”text” id=”keywords” name=”keywords” class=”text” value=”<?php echo htmlentities($file->keywords); ?>” /> </p> <p> <label for=”description”>Description</label> <textarea id=”description” name=”description”> <?php echo htmlentities($file->description); ?></textarea> </p> <p> <label for=”body”>Article Body (HTML)</label> <textarea id=”body” name=”body”> <?php echo htmlentities($file->body); ?></textarea> </p> </div> <div class=”actions”> <input type=”submit” value=”Update Article” /> <input type=”reset” value=”Reset” /> </div> </form> </body> </html> Il file doArticleUpdate.php che elabora questo form è molto simile allo script doArticleCreate.php: Esempio 7.48. doArticleUpdate.php <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; $doc = new DOMDocument(); $root = $doc->createElement(‘article’); $root = $doc->appendChild($root); $id = $_POST[‘id’]; $root->setAttribute(‘id’, $id); … $filename = $fileDir . $id . ‘.xml’; unlink($filename); 196 PHP_CAP07.qxd 19/05/2006 11.14 Pagina 197 Eliminazione di un articolo XML $doc->save($filename); header(‘location: articletool.php’); ?> Eliminazione di un articolo XML Eliminare un file XML è un’operazione molto semplice. Quando si passa un ID alla pagina doArticleDelete.php, il file corrispondente viene eliminato e l’utente viene riportato alla pagina articletool.php: Esempio 7.49. doArticleDelete.php <?php include ‘security.inc.php’; include_once ‘../common.inc.php’; $filename = $fileDir . $_GET[‘id’] . ‘.xml’; unlink($filename); header(‘location: articletool.php’); ?> A questo punto, si dispone di una pagina di accesso, di un indice amministrativo, nonché di pagine in cui è possibile aggiungere, modificare ed eliminare gli articoli. Come già anticipato nel corso di questo capitolo, le altre pagine di amministrazione verranno descritte nell’Appendice B, Strumento di amministrazione di CMS. Come sarà possibile notare, il codice scritto per la creazione di queste prime pagine verrà utilizzato di nuovo per completare la realizzazione dello strumento di amministrazione. Riepilogo In questo capitolo è stato illustrato l’utilizzo delle funzionalità SAX, DOM e SimpleXML in PHP. A questo punto, le informazioni acquisite finora sono sufficienti per gestire la maggior parte delle attività di elaborazione XML. La seconda parte di questo capitolo è stata dedicata alla creazione di form amministrativi necessari per gestire gli articoli del sito Web. Nel capitolo successivo verranno descritti i due standard RSS e RDF, generalmente utilizzati per la creazione di intestazioni e altri feed di informazioni disponibili per terze parti nel formato XML. 197