Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Un Forum con PHP ed XML

Come costruire passo passo un Forum usando i linguaggi PHP e XML: dalla progettazione alla definizione dei dati
Come costruire passo passo un Forum usando i linguaggi PHP e XML: dalla progettazione alla definizione dei dati
Link copiato negli appunti

Benvenuti a tutti. Oggi cominciamo una breve serie di articoli che introdurrà le tecniche che si trovano alla base della realizzazione di un forum. Il nostro forum includerà solamente le funzionalità base, mentre verrà lasciato a voi il compito di estenderlo al fine di renderlo completo; svilupperemo una semplice amministrazione parallela, che ci permetta di agire agilmente sulla struttura del forum.

L'applicativo che mi propongo di mostrare, è stato sviluppato in modo da sfruttare una semplice interfaccia per prelevare e depositare i dati, permettendo al forum di essere sviluppato utilizzando qualsiasi tipo di supporto per il salvataggio dei dati (a patto che vengano estese le interfacce in modo corretto).

Noi svilupperemo solamente il supporto per XML per vari motivi:

  • Esistono pochissimi forum che possono lavorare senza database; anche se l'utilizzo dei database incrementa le prestazioni, non è comunque da sottovalutare il fatto che molti spazi web non supportano ancora i database
  • L'XML è un linguaggio molto potente e facilmente estendibile, il che ci permetterà, in futuro, di agire sulla struttura della nostra applicazione anche attraverso programmi scritti da terzi
  • PHP sta migliorando di molto il supporto all'XML
  • La gestione di XML tramite PHP è un argomento poco trattato, ed in questo articolo mi pongo l'obiettivo di introdurne il funzionamento

Il forum sarà strutturato in modo da avere una struttura ad albero virtualmente infinita: sarà possibile creare delle categorie principali alle quali associare infinite sottocategorie.

I messaggi potranno essere postati all'interno di ogni categoria, anche se questa avrà a disposizione delle sottocategorie. Viene volontariamente tralasciato il processo di autenticazione, la ricerca e la moderazione, che potranno essere aggiunte in futuro in caso ne abbiate bisogno. Prima di continuare la lettura scaricate i file utili a seguire il tutorial. Il codice è ampiamente commentato per favorirne la comprensione.

La struttura del filesystem

Il nostro applicativo salverà i dati direttamente all'interno del filesystem, in modo da permettere un controllo agile dei dati.

All'interno della cartella bacheche (possibilmente protetta con i giusti permessi) avremo:

  • un numero di cartelle corrispondenti alle bacheche principali, aventi come nome un numero crescente da 1 ad N
  • Un file XML (chiamato struttura.xml) che conterrà informazioni relative alla struttura delle bacheche: ad ogni cartella assocerà un nome ed una descrizione in modo da permetterci di visualizzare un elenco auto-descrittivo dei forum disponibili

All'interno di ogni singola cartella, sarà presente una struttura simile a quella della cartella bacheche con alcune informazioni aggiuntive:

  • un numero variabile di file XML aventi come nome un numero crescente da 1 ad N, che conterranno i post e le risposte scritte dagli utenti
  • un file post.xml che conterrà informazioni relative ad ogni singolo file di post: ad ogni file verrà associato un titolo ed eventuali informazioni utili

Seguendo le nostre direttive, il nostro filesystem dovrebbe avere un aspetto simile al seguente

Figura 1
la figura del filesystem

I file XML avranno una struttura molto semplice, che ci permetterà di gestire le informazioni contenute senza troppe difficoltà.

Il file struttura.xml avrà un nodo principale di nome struttura contente un numero di figli di nome forum pari al numero di cartelle contenute nella directory in cui si trova il nostro file. Il tag forum avrà un attributo, id che rappresenterà il nome della cartella a cui le informazioni fanno riferimento, e due figli, nome e descrizione, che conterranno rispettivamente il nome della bacheca e la descrizione testuale di questa ultima.

Il file post.xml avrà un nodo principale di nome post avente un numero di figli di nome td pari al numero di file XML presenti nella bacheca corrente. Ogni nodo td, avrà un attributo id contente il nome del file a cui le informazioni fanno riferimento, e due figli, nome e riassunto, che rappresenteranno rispettivamente il titolo ed un breve e opzionale riassunto della discussione.

Infine i file XML numerati avranno un nodo principale di nome discussione avente un numero di nodi figli di nome post pari al numero di post contenuti in una singola discussione. Ognuno di questi nodi conterrà i figli data, rappresentante la data di creazione, autore, rappresentate l'autore ed infine contenuto, che rappresenta il testo visibile del post.

L'accesso ai dati XML tramite PHP

Per accedere ai dati che rappresentano la struttura del nostro forum, utilizzeremo il parser SAX fornito con PHP; sebbene più lento di DOM, richiede meno memoria ed è implementato in tutte le distribuzioni di PHP senza necessità di librerie aggiuntive.

Il parser SAX si comporta come una Statate Machine: quando, navigando all'interno del documento, il parser rileva una struttura conosciuta, modifica il suo stato e richiama un'azione definita dall'utente associata a quel determinato stato, passandole varie informazioni utili. Quello di cui si dovrà preoccupare il programmatore è semplicemente associare operazioni corrette ai vari stati, utilizzando delle strutture dati per immagazzinare le informazioni adeguate.

Prima di presentare il parser SAX implementato per la lettura dei nostri file XML, volevo mostrare l'interfaccia (che in futuro estenderemo) che verrà utilizzata per recuperare le informazioni dal filesystem in modo indipendente dall'implementazione. Questa classe "astratta" dovrà essere estesa da ogni classe che propone un metodo capace di restituire informazioni al nostro applicativo.

Il file library/DataTaker.php contiene la nostra classe astratta commentata. Come potete notare la classe è molto semplice, e richiede l'implementazione di pochi metodi per effettuare le operazioni necessarie. Ogni metodo stampa un messaggio di errore e blocca l'esecuzione dello script: in questo modo, in caso venga richiamato dall'applicativo un metodo non implementato dalla classe figlio, l'errore verrà visualizzato. Dato che in questo articolo tratteremo solamente il metodo tramite cui prelevare i dati, mi sono limitato a specificare le funzioni che svolgono queste operazioni.

Nei file library/Forum.class.php, library/Post.class.php e library/Thread.class.php sono contenute le classi che utilizzerò per il salvataggio dei dati relativi alle informazioni prelevate. Ognuno di questi file contiene due classi: la classe padre implementa un tipo di informazione inizialmente vuota, mentre la classe figlio implementa l'informazione con dei valori iniziali già settati. In questo modo risulterà più semplice la gestione dei dati.

Il file library/DataXML.php contiene la nostra implementazione per l'accesso ai dati: la classe DataXML è quella che utilizzeremo direttamente per prelevare ed in futuro salvare e modificare le informazioni. BaseXMLparser definisce un'interfaccia da utilizzare per implementare un parser SAX:

class BaseXMLparser {

   var $p = NULL;
   var $file = '';
   
   function BaseXMLparser(){
      $p = xml_parser_create();
      xml_parser_set_option($p, XML_OPTION_CASE_FOLDING, false);
      xml_set_object($p, $this);
      xml_set_element_handler($p, "OnTagOpen", "OnTagClose");
      xml_set_character_data_handler($p, "OnData");
      $this->p = $p;
   }
   
   function Parse($file){
      $this->file = $file;
      xml_parse($this->p, stripslashes(file_get_contents($file)));
   }
   
   function Free(){
      $this->file = '';
   }
   
   function Close(){
      xml_parser_free($this->p);
      $this->p = NULL;
   }
   
   function Error($msg, $type){
      $func = ($type == ER_FATAL) ? 'die' : 'print';
      $func($msg);
   }
   
   function OnTagOpen($parser, $tagname, $attributes){}
   
   function OnTagClose($parser, $tagname){}
   
   function OnData($parser, $data){}
}

Procedo con la descrizione della classe:

  • Le variabili $p e $file conterranno rispettivamente un riferimento al parser generato ed il nome del file su cui si sta operando
  • Il costruttore inizializza un nuovo parser SAX utilizzando la funzione xml_parser_create; dopo aver fatto questo, assegna al parser delle opzioni e assegna l'oggetto corrente come riferimento per le callback che verranno successivamente settate tramite xml_set_element_handler ed xml_set_character_data_handler
  • La funzione Parse esegue il parsing del file XML
  • Il metodo Free libera i riferimenti al parser corrente permettendoci di crearne uno nuovo successivamente
  • Il metodo Close chiude definitivamente l'oggetto, liberando la memoria occupata
  • La funzione Error stampa un errore
  • Infine i metodi On* non svolgono alcuna operazione, ma sono da intendersi come metodi virtuali da implementare nelle classi figlie. Questi metodi sono richiamati da SAX nel momento in cui verrà trovato un nuovo tag, verrà trovato il contenuto di un tag o verrà trovato un tag chiuso

Le classi ForumParser, PostParser e ThreadParser estendono la classe BaseXMLparser al fine di implementare dei parser capaci di comprendere determinate strutture XML. Ognuna di queste classi estende i metodi On* della classe padre, e definisce funzioni aggiuntive per salvare i dati prelevati all'interno di uno o più oggetti. Le tre classi sono molto simili, e per questo motivo è stata commentata, all'interno dei sorgenti, solamente la classe ForumParser.

Prelevare i dati da XML

Prima di concludere, descrivo l'implementazione della classe DataXML, che utilizzeremo nei prossimi articoli per prelevare i dati necessari:

class DataXML extends DataTaker {

   var $thread_p = NULL;
   var $post_p = NULL;
   var $forum_p = NULL;
   
   function DataXML(){
      $this->DataTaker();
      $this->thread_p = new ThreadParser;
      $this->forum_p = new ForumParser;
      $this->post_p = new PostParser;
   }
   
   function GetMainForums(){
      $this->forum_p->Parse('bacheche/struttura.xml');
      return $this->forum_p->forums;
   }
   
   function GetSubForums($parent, $parents){
      $path = array('bacheche');
      foreach($parents as $p)
         array_push($path, $p);
      array_push($path, $parent->id);
      array_push($path, 'struttura.xml');
      $this->forum_p->Parse(implode('/', $path));
      return $this->forum_p->forums;
   }
   
   function GetPosts($forum, $parents){
      $path = array('bacheche');
      foreach($parents as $p)
         array_push($path, $p);
      array_push($path, $forum->id);
      array_push($path, 'post.xml');
      $this->post_p->Parse(implode('/', $path));
      return $this->post_p->posts;
   }
   
   function GetThread($post, $parents){
      $path = array('bacheche');
      foreach($parents as $parent)
         array_push($path, $parent);
      array_push($path, $post->id.'.xml');
      $this->thread_p->Parse(implode('/', $path));
      return $this->thread_p->threads;
   }
   
   function Close(){
      $this->thread_p->Close();
      $this->forum_p->Close();
      $this->post_p->Close();
      $this->thread_p = NULL;
      $this->forum_p = NULL;
      $this->post_p = NULL;
   }
}

Come vedete la classe estende DataTaker, implementando tutti i metodi "virtuali" definiti in quest'ultimo:

  • Il costruttore si occupa di inizializzare i parser;
  • La funzione GetMainForums restituisce i forum principali, eseguendo il parsing della struttura base;
  • Il metodo GetSubForums restituisce un elenco di forum figli di un padre passato come primo argomento. Il secondo argomento è un array di interi che rappresenta gli indici (e quindi i nomi di cartella) necessari a raggiungere la cartella nella quale dovremo operare;
  • I metodi GetPosts e GetThread restituiscono rispettivamente un elenco di Post o Thread relativi ai parametri passati come argomento;
  • Infine il metodo Close liberare le risorse.

Come potete notare, una volta definite le classi necessarie per operare sulla nostra base di dati virtuale, l'implementazione dell'interfaccia per gestire i dati è molto semplice. Finchè ci preoccuperemo di implementare tutti i metodi e di restituire il corretto risultato, il nostro applicativo funzionerà correttamente, sia che i dati siano contenuti in MySQL, sia che questi siano contenuti in file di testo.


Articolo da eliminare

Articolo da eliminare

Sviluppiamo ora la seconda parte del nostro forum: imposteremo le pagine di visualizzazione e creeremo il sistema di plug-in. Il sistema di plug-in che intendo sviluppare ed utilizzare è molto semplice, e permetterà un controllo sui dati PRIMA che vengano scritti sulla base di dati e DOPO che sono stati prelevati; altre informazioni in seguito. Il nostro forum utilizzerà anche un semplicissimo sistema di template, che potrà essere modificato utilizzando template engine più famosi e prestanti.

Prima di continuare la lettura scaricate i file utili a seguire il tutorial.

Le impostazione di base

Il processo di visualizzazione delle informazioni richiede alcuni passi molto semplici da seguire ed alcune informazioni da cui partire:

  • deve essere recuperabile in qualsiasi momento la posizione, nel nostro caso all'interno dell'albero delle cartelle, da cui dobbiamo prelevare tutte le informazioni necessarie;
  • dobbiamo prelevare informazioni riguardanti i Forum contenuti in una certa cartella (se presenti) e visualizzarle dopo aver filtrato l'output;
  • dobbiamo prelevare le informazioni riguardanti i Messaggi contenuti nel forum attuale (in quello principale non ve ne saranno) e visualizzarle dopo aver filtrato l'output;
  • in caso ci trovassimo all'interno di una discussione, visualizzare un riassunto della posizione e la lista dei messaggi relativi, ovviamente dopo aver filtrato l'output.

Questi passaggi, data la struttura delle classi che abbiamo deciso di utilizzare, sono molto semplici da eseguire; ho deciso di inglobare tutte le operazioni di visualizzazione dei dati all'interno di una classe, chiamata View, in modo da cercare di rendere il più indipendente possibile la struttura ad oggetti dalle informazioni di visualizzazione.

Prima di mostrare e commentare il codice della classe di visualizzazione, occorre introdurre brevemente al sistema di template: una template è un blocco di codice HTML dinamico, che viene caricato ed interpretato da PHP. Il vantaggio di utilizzare le template consiste nel fatto che è possibile ottenere un ottimo grado di indipendenza tra aspetti strettamente legati alla programmazione ed aspetti strettamente legati alla grafica. Tra qualche settimana pubblicheremo un articolo in proposito.

Il nostro template engine sarà molto semplice: preleverà la struttura da un file tpl, all'interno del quale sostituirà i simbolo %s con il testo necessario. La breve classe che utilizzeremo è la seguente:

class Template {

var $data = "";
var $vars = array();

function Template($name){
if(file_exists('templates/'.$name.'.tpl')){
$data = stripslashes(file_get_contents('templates/'.$name.'.tpl'));
}else
die("Template '".$name."' not found!");
$this->data = $data;
}

function SetVar($pos, $value){
$this->vars[$pos] = $value;
}

function Apply(){
$args = array($this->data);
$count = substr_count('%s', $this->data);
for($i = 0; $i < $count; $i++)
$args[] = $this->vars[$i];
$result = call_user_func_array('sprintf', $args)
return str_replace('?','%', $result);
}

function Reset(){
$this->vars = array();
}
}

  • la variabile $data conterrà le informazioni prelevate dal file di testo contenente il template;
  • la variabile $vars conterrà le variabili da sostituire;
  • nel costruttore, che accetta come argomento una stringa rappresentante il nome della template che vogliamo caricare, controlliamo l'esistenza del file, ed in caso affermativo ne salviamo il contenuto all'interno di una variabile temporanea.
  • Il metodo SetVar assegna il valore ad una variabile in una data posizione, passata come primo argomento;
  • Il metodo Apply applica la template, restituendoci una stringa rappresentante i dati: la varibile $args conterrà i parametri per la funzione built-in sprintf, che richiameremo utilizzando call_user_func_array. L'ultima sostituzione server per sostituire ? con il valore di percentuale;
  • Il metodo Reset cancella tutte le variabili settate precedentemente;
  • Ricordo che se il simbolo di % NON rappresenta una variabile (per esempio nel caso che rappresenti la percentuale di dimensioni di una tabella) deve essere sostituito con ? all'interno del sorgente template;

Un semplice utilizzo delle template sarà mostrato nel listato della classe View.

Il sistema di plug-in

Il sistema di plug-in che intendo utilizzare dovrà svolgere le seguenti operazione:

  • permettere agli utenti di definire un numero di filtri da applicare ai dati in ingresso ed in uscita;
  • fornire agli utenti una semplice interfaccia da cui astrarre tutti i plug-in;
  • premettere l'installazione dei plug-in semplicemente inserendo il file relativo all'interno di una cartella;
  • fornire un semplice set di funzioni per gestire alcune opzioni utili;

Ogni plug-in creato (vi sono alcuni esempi utilizzati all'interno della cartella ./filters/) dovrà estendere dalla classe Filter, ridefinendo alcuni metodi che verranno richiamati in caso in cui il filtro debba essere applicato. La classe FilterManager invece (presente nel file Filter.class.php) contiene delle funzioni statiche per la gestione dei plug-in. Di seguito viene mostrato il listato del file Filter.class.php:

class FilterManager {

function SetFilterVar($name, $value){
global $GLOBALS;

$GLOBALS['filter_vars'][$name] = $value;
}

function GetFilterVar($name){
global $GLOBALS;

if(array_key_exists($GLOBALS['filter_vars'][$name]))
return $GLOBALS['filter_vars'][$name];
else
return NulL;
}

function Init($classname){
if(class_exists($classname))
return new $classname();
else
return NulL;
}

function Load($plugin){
if(file_exists($plugin)){
include($plugin);
$tmp = explode(".", basename($plugin));
$name = $tmp[0];
return FilterManager::Init($name);
}else
return NulL;
}
}

class Filter {

function Filter(){}
function InputFilter($data){
switch(get_class($data)){
case 'Post': $function = 'GetDiscussion'; break;
case 'Thread': $function = 'GetMessage'; break;
case 'Forum': $function = 'GetForum'; break;
}

return $this->{$function}($data);
}

function OutputFilter($data){
switch(get_class($data)){
case 'Post': $function = 'PostDiscussion'; break;
case 'Thread': $function = 'PostMessage'; break;
case 'Forum': $function = 'PostForum'; break;
}

return $this->{$function}($data);
}

function PostForum($forum){
return $forum;
}

function GetForum($forum){
return $forum;
}

function PostMessage($message){
return $message;
}

function GetMessage($message){
return $message;
}

function PostDiscussion($post){
return $post;
}

function GetDiscussion($post){
return $post;
}

}

  • Le funzione FilterManager::SetFilterVar e FilterManager::GetFilterVar servono rispettivamente per settare e prelevare delle variabili globali accessibili a tutti i filtri;
  • Le funzioni FilterManager::Init e FilterManager::Load servono per inizializzare e caricare un plug-in;
  • Nella classe Filter, le funzioni InputFilter ed OutputFilter sono "private", e non dovrebbero essere estese. Le altre sei funzioni servono per filtrare i dati, e devono essere estese. Ognuna di queste funzioni DEVE restituire lo stesso oggetto ricevuto come parametro ma eventualmente modificato.

Il processo di visualizzazione

Il processo di visualizzazione viene effettuato tramite la classe View, che si occupa di prelevare i dati dalla base di dati, applicare i filtri necessari, impostare le template, ed infine restituire i risultati ottenuti. Tra le varie pagine verranno passate due variabili: la viabile $path conterrà la lista di indici che fanno riferimento alla discussione corrente, mentre la variabili $disc conterrà l'indice della discussione da visualizzare. Per ora ci limiteremo all'implementazione dei metodi relativi alla visualizzazione del forum principale; il resto della classe sarà implementato nel prossimo articolo.

Il file contente la classe View è library/View.class.php; segue la descrizione:

  • il costruttore della classe si occupa di inizializzare tutti gli oggetti di cui avremo bisogno durante il processo di visualizzazione:
    • inizializza le variabili di classi;
    • carica le template necessarie a visualizzare le informazioni relative ad un forum e ad un post;
    • carica il driver per l'accesso ai dati;
    • carica tutti i plug-in presenti all'interno della cartella ./filters/.
  • Il metodo Filter applica i filtri ad un dato prima di restituirlo alle funzioni per la gestione della visualizzazione; accetta come parametri il dato da filtrare e il tipo di transizione effettuata: INPUT in caso di dato che proviene dall'esterno, ed OUTPUT in caso i dati debbano essere visualizzati;
  • GetData è il metodo che, in base al tipo di visualizzazione richiesta, richiama la funzione necessaria per prelevare i dati;
  • Il metodo GetHomeForums, che è l'unico dei metodi di visualizzazione implementato, serve per recuperare una stringa da restituire all'utente: questa stringa viene costruita prelevando tutti i forum principali attraverso i metodi implementati nel nostro DataTaker, e applicando il template forumrow ad ogni forum.

Dopo aver sviluppato i driver (pagg. 1-3) per la connessione al filesystem virtuale rappresentante il nostro database in XML, e dopo aver impostato le classi per l'accesso e la visualizzazione dei dati (pagg. 4-5), in questo articolo tratteremo di come amministrare il nostro forum, come permettere agli utenti di inserire messaggi e come rendere possibile la navigazione.

Il file compresso allegato all'articolo (che vi invito a scaricare e decomprimere) contiene tutto il codice sorgente necessario a far funzionare il nostro forum, con una bacheca di test che potrete utilizzare fin da subito; purtroppo la grafica è molto spartana, ma applicando semplici modifiche alle template ed ai file PHP, sarà possibile migliorarla a dovere.

Completare la visualizzazione

L'ultima articolo terminava mentre trattavamo la classe View, che permetteva di visualizzare dei contenuti differenti in base al contesto creato durante l'inizializzazione della classe. Avevamo trattato di come visualizzare i forum principali della home, mentre non avevamo ancora preso in considerazione come visualizzare i sotto-forum, le discussioni aperte ed i messaggi.

All'interno del file View.class.php ho apportato le seguenti modifiche:

//?
function Reset($type, $path, $disc){
$this->type = $type;
$this->path = explode(",", $path);
$this->disc = $disc;
}
//?
function GetForums(){
$template = $this->templates['forumrow'];
$forums = $this->datataker->GetSubForums($this->path);
$data = "";
foreach($forums as $forum){
$forum = $this->Filter($forum, OUTPUT);
$count = -1;
$template->SetVar(++$count, implode(",", array_merge($this->path,array($forum->id))));
$template->SetVar(++$count, '');
$template->SetVar(++$count, $forum->name);
$template->SetVar(++$count, $forum->desc);
$data .= $template->Apply();
$template->Reset();
}

return $data;
}

function GetThreads(){
$template = $this->templates['threadrow'];
$posts = $this->datataker->GetPosts($this->path);
$data = "";
foreach($posts as $post){
$post = $this->Filter($post, OUTPUT);
$count = -1;
$template->SetVar(++$count, implode(",", $this->path));
$template->SetVar(++$count, $post->id);
$template->SetVar(++$count, $post->name);
$template->SetVar(++$count, $post->riassunto);
$data .= $template->Apply();
$template->Reset();
}

return $data;
}

function GetPosts(){
$template = $this->templates['postrow'];
$posts = $this->datataker->GetThread($this->disc, $this->path);
$data = "";
foreach($posts as $post){
$post = $this->Filter($post, OUTPUT);
$count = -1;
$template->SetVar(++$count, $post->autore);
$template->SetVar(++$count, $post->data);
$template->SetVar(++$count, $post->contenuto);
$data .= $template->Apply();
$template->Reset();
}

return $data;
}
//?

  • La funzione Reset permette di reimpostare i criteri di base tramite cui effettuare la visualizzazione; questa funzione ci sarà utile quando vorremo visualizzare più di un blocco di contenuti distinto ed avremo bisogno di non inizializzare nuovamente le risorse necessarie alla visualizzazione;
  • Il metodo GetForums restituisce una stringa rappresentate un insieme di righe di una tabella rappresentante i forum contenuti all'interno di un determinato path; il path non è altro che il percorso (formato dagli indici dei forum padri) nel quale sono contenute le informazioni relative al forum;
  • Il metodo GetThreads restituisce, nello stesso formato di GetForums, la lista delle discussioni iniziate in un determinato forum;
  • Infine il metodo GetPosts visualizza la lista dei messaggi di una determinata discussione, basandosi sul path del forum in cui si trova e sull'indice della discussione.

Dopo aver effettuato queste modifiche, ho semplicemente generato le template necessarie all'interno della cartella ./templates/ ed ho creato i file guida per la visualizzazione delle informazioni (forum.php, viewpost.php), inizializzando in ognuno di questi la classe View utilizzando parametri differenti, in modo da ricevere contenuti diversi.

Amministrare il forum

Ora che le impostazioni per la visualizzazione sono pronte, è necessario studiare un metodo che permetta di aggiungere, togliere o modificare la struttura del nostro forum. Per fare questo ci serviremo di una sezione specializzata (che andrebbe protetta), che ci permetterà di svolgere le operazioni necessarie in modo semplice ed ordinato.

Quello di cui abbiamo bisogno è un insieme di funzioni che permettano di modificare il contenuto di file XML: volendo potremmo appoggiarci a DOM, che, grazie alla sua struttura ad oggetti annidata, permette di controllare agilmente la struttura di un documento XML; purtroppo DOM non è presente su tutti i server, e per questo motivo implementeremo noi una semplice libreria che permetta la modifica di documenti XML.

La nostra libreria creerà una struttura ad oggetti molto semplice di un documento XML, ci permetterà di apportare modifiche ai nodi, ai tag ed agli attributi, e successivamente eseguirà il salvataggio dei dati. Dato che non esiste un modo semplice per generare un albero XML completo utilizzando il parser SAX, creeremo delle classi specifiche per ogni tipo di documento XML modificabile.

Le classi necessarie si trovano nel file XMLlib.php:

  • La classe XMLnode rappresenta un singolo node XML; può contenere attributi e nodi figlio, ed avere un nome;
  • La classe BaseXMLEdit, che estende la classe BaseXMLparser creata nel file DataXML.php, serve come base per tutte le altre classi che avranno il compito di caricare in memoria una struttura di oggetti;
  • La classe XMLStrutturaEdit carica un file rappresentante la struttura di un forum all'interno di un albero di nodi;
  • La classe XMLDiscussioniEdit carica un file rappresentante una discussione all'interno di un albero di nodi;
  • La classe XMLMessaggiEdit carica un file rappresentante dei messaggi all'interno di un albero di nodi;

Leggendo il codice sorgente delle classi, vi accorgerete di come sia semplice generare l'albero, e di come sia altrettanto semplice modificare la struttura del file XML caricato.

Nel file admin.php è stata creata una semplicissima interfaccia per aggiungere nuovi forum. Basta passare una lista sparata da virgole rappresentante la posizione all'interno della cartella bacheche in cui vogliamo aggiungere il forum, il nome e la descrizione di quest'ultimo.

L'interfaccia è molto semplice e poco "usabile"; visto che, per motivi di tempo, non ho potuto implementarne una migliore, lascio a voi il compito di aggiungere opzioni per l'editing ed il delete di forum.

Utilizzare il forum

All'interno di ogni forum che non sia quello principale, è possibile per un utente creare una nuova discussione cliccando sul link apposito; questo link porta ad una pagina in cui l'utente dovrà specificare un titolo per la nuova discussione, un riassunto, il suo nome ed il testo del post principale. Lo script per la creazione di discussioni si trova all'interno del file aggiungi.php; basandosi sul path del forum corrente, carica in memoria il file XML contenete le discussioni già esistenti, e vi aggiunge i nodi relativi alla nuova discussione creata. Dopo aver effettuato il salvataggio di questi dati, si occupa di generare un nuovo file XML in cui saranno contenuti i messaggi relativi alla discussione, e si occupa di riempire questo file con i dati appena inseriti dall'utente.

Anche in questo caso abbiamo sfruttato la libreria XMLlib creata in precedenza, che ci ha semplificato di molto il lavoro.

Ad ogni discussione generata sarà possibile rispondere seguendo una procedura simile a quella della generazione di una discussione. Lo script per le risposte, chiamato rispondi.php, si comporta in modo molto simile allo script di aggiunta: dopo aver prelevato i dati dal form e dopo aver caricato in memoria il file XML relativo alla discussione corrente, aggiunge un nuovo nodo all'albero e lo salva nella vecchia locazione.

Concludendo

Siamo giunti alla fine di questa ricca guida; purtroppo non ho potuto affrontare molti argomenti interessanti, visto che la generazione di un forum è un argomento complesso, che richiede molto lavoro e studio.

Ho preferito tralasciare le feature che avreste potuto implementare da soli (autenticazione, alcune opzioni amministrative, barra di navigazione) per approfondire alcuni discorsi legati alla struttura delle classi ed all'implementazione di argomenti leggermente più complessi, come i plugin e i driver di connessione.

Spero che questa guida non sia stata inutile, e vi esorto nuovamente a scrivermi per ogni problema riscontrato. In futuro, forse, amplieremo la guida parlando degli argomenti tralasciati. Buon lavoro.

Ti consigliamo anche