[PHP][XML] Parser di un documento.

Sto impazzendo con un semplice file.

In realtà sto costruendo un sistema di FAQ abbastanza complicato, per ogni gruppo di Domande esiste unacategoria, ad esempio: generico, login, email, eccetera.

Questo fa sì che le stesse domande possano venire raggruppate in categorie in modo che sia facile sia per l'utente trovare la giusta risposta al suo problema, sia a chi le scrive raggrupparle in un ambito di risposte precise per ogni argomento trattato.

Bene direte voi, dov'è il problema? Eccolo:

Ho creato un semplicissimo file xml che raggruppa le categorie.

<?xml version="1.0" encoding="iso-8859-1"?>
<categorie>
   <categoria id='generico'>
      <genere>generica</genere>
      <genere>sistema operativo</genere>
      <genere>commerciale</genere>
      <genere>email</genere>
      <genere>ftp</genere>
      <genere>attivazione account</genere>
      <genere>configurazione</genere>
      <genere>sicurezza</genere>
   </categoria>
   <categoria id='cpanel'>
      <genere>generale</genere>
      <genere>login</genere>
      <genere>posta</genere>
   </categoria>
</categorie>

e fino a qui nulla di più semplice.

Il problema nasce nel modo di leggere tale file, ci sto sbattendo la testa da qualche giorno senza risultato, ovviamente togliendo i bernoccoli :D

Per raggruppare visivamente le categorie, le inserisco in una casella a discesa dentro un form, in tale casella ogni categoria entra in un gruppo definito dalla categoria medesima, ad esempio:

[*]generico

[*]sistema operativo

[*]email

[*]commerciale

[/list]

[*]cpanel

[*]login

[*]posta

[/list]

[/list]

e sembra facile dopotutto, il problema nasce dal leggere il file xml, se lo facessi semplicemente ricorsivamente sarebbe facile, ma le categorie sarebbero lette così come inserite, venenendosi a creare una struttura assolutamente senza senso.

Quindi devo necessariamente leggerlo in modo da separare ogni categoria attraverso l'attributo ID che ho inserito a bella posta.

Ecco a cosa sono arrivato mio malgrado:

                <label for='categoria'>Categorie per le FAQ</label>
      <select name='categoria' id='categoria' onchange='document.inserimento.cat.value=this'>
         <option label='scegli la categoria' value=''>scegli la categoria</option>
      <?php
      // Creazione dell'elenco delle categorie
      $fnCat = 'FAQ/categorie/categorie.xml';
      if (file_exists($fnCat) && trustedFile($fnCat)) {
      
         $doc = new DomDocument;
         // Occorre validare il documento prima di puntare all'id
         $doc->validateOnParse = true;
         $doc->preserveWhiteSpace = false;
         $doc->Load($fnCat);

         $categoria = $doc->getElementsByTagName('categoria');
         foreach ($categoria as $attributo) {
            switch ($attributo->getAttribute('id')) {
            case "generico":
               echo "   <optgroup label='generico'>\n";
               foreach ($doc->getElementsByTagName('genere') as $elemento) 
                  echo '<option>'. $elemento->nodeValue . "</option>\n";
               echo "   </optgroup>\n";
            break;
            case "cpanel":
               echo "   <optgroup label='cpanel'>\n";
               foreach ($doc->getElementsByTagName('genere') as $elemento) 
                  echo '<option>'. $elemento->nodeValue . "</option>\n";
               echo "   </optgroup>\n";
            break;
            }
         }
      }
      ?>
      </select>

Il sistema sembra funzionare se non che, lo legge ricorsivamente due volte, una volta mi mette tutte le categorie presenti nel file xml sotto il nome generico e una volta mette tutte le categorie presenti nel file xml sotto il gruppo cpanel.

Insomma legge tutto senza fermarsi mai se non alla fine del file, mentre dovrebbe leggere ricorsivamente gruppo per gruppo.

Chi mi aiuta?

inviato 9 anni fa
Marco Grazia
X 0 X

Non ho mai usato l'oggetto DOM, cmq ho provato a farti un esempio di come l'avrei fatto, basandomi sull'xml ceh hai postato sopra:

$p = xml_parser_create();
xml_parse_into_struct($p, $xml, $vals, $index);
xml_parser_free($p);


//nella variabile $xml ho messo l'xml che hai messo sopra



$j=0;

for($i=0; $i<count($vals); $i++)
{
   
   if(isset($vals[$i]['tag']) && $vals[$i]['tag'] == 'CATEGORIA' && isset($vals[$i]['attributes']['ID']))
   {      
      $categoria = $vals[$i]['attributes']['ID'];            
   }   
      
   if(isset($vals[$i]['tag']) && $vals[$i]['tag'] == 'GENERE')
      $return[$j][$categoria][] = $vals[$i]['value'];
    
   if(isset($vals[$i]['tag']) && $vals[$i]['tag'] == 'CATEGORIA' && $vals[$i]['type'] == 'close')
   {      
      $categoria = "";
      $j++;
   }
}

echo "<pre>";
print_r($return);
echo "<pre>";

Spero che sia quello che intendevi fare tu, o per lo meno che ti possa servire per qualcosa...

ciao

Lore

risposto 9 anni fa
Lore
Lore
1
modificato 9 anni fa
X 0 X

Grazie lore, interessante, l'ho un po' modificato dato che prima non funzionava bene.

                $xml = file_get_contents($fnCat);
      $parseHandle = xml_parser_create();
      xml_parser_set_option($parseHandle, XML_OPTION_SKIP_WHITE, 0);
      xml_parse_into_struct($parseHandle, $xml, $valore, $indice);
      xml_parser_free($parseHandle);
      $indiceArray = 0;

      for($index = 0; $index < count($valore); $index++){
         if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'CATEGORIA' && isset($valore[$index]['attributes']['ID']))
            $categoria = $valore[$index]['attributes']['ID'];
         if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'GENERE')
            $arraySet[$indiceArray][$categoria][] = $valore[$index]['value'];
         if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'CATEGORIA' && $valore[$index]['type'] == 'close') {
            $categoria = "";
            $indiceArray++;
         }
      }

         echo "<pre>";
         print_r($arraySet);
         echo "</pre>";

Ok, ora abbiamo il parsing del file xml dentro un arra, devo solo gestire l'array ed è fatta a quanto pare.

In realtà sono da capo a 12, ma almeno questo codice esegue meno cicli dell'altro, il che è un bel vantaggio in termini di velocità.

Procedo e poi faccio sapere.

risposto 9 anni fa
Marco Grazia
X 0 X

Bene ha funzionato.

questo l'output xhtml:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang='it-IT' lang='it-IT' dir='ltr'>
<head>
   <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type" />
   <title>Frequent Ask Question :: inserimento e modifica</title>
  
   <meta name="description" content="" />
   <meta name="keywords" content="" />
  
   <meta name="author" content="StilisticaMente Team" />
   <meta name="copyright" content="Copyright 2005-2006 StilisticaMente Team - www.stilisticamente.com" />
  
   <link rel='Home' type='text/html' href='./' title='Home page del sito' />

   <link rel='Top' type='text/html' href='#alto' title='Vai in alto' />
   
   <script type='text/javascript' src='script/library.js'></script>
   <link rel="stylesheet" type="text/css" href="script/faq.css" title="main" media="screen" />
</head>

<body>

   <form name='inserimento' method='post' action='/FAQ/index.php'>
   <fieldset><legend>&nbsp;Gestione FAQ&nbsp;</legend>
      <label for='faq_esistenti'>FAQ Esistenti: click per modificare.&nbsp;</label><br />

      <select name='faq' id='faq_esistenti'>
         <option value='nuova FAQ' onclick='nuovo(this);'>Nuova FAQ</option>
      </select>
      <input type='hidden' name='numero' value='0' />
      <br />
      <label for='categoria'>Categorie per le FAQ</label>
      <select name='categoria' id='categoria' onchange='document.inserimento.cat.value=this'>
         <option label='scegli la categoria' value=''>scegli la categoria</option>

      <optgroup label='generico'>
         <option value='generica'>generica</option>
         <option value='sistema operativo'>sistema operativo</option>
         <option value='commerciale'>commerciale</option>
         <option value='email'>email</option>
         <option value='ftp'>ftp</option>

         <option value='attivazione account'>attivazione account</option>
         <option value='configurazione'>configurazione</option>
         <option value='sicurezza'>sicurezza</option>
      </optgroup>
      <optgroup label='cpanel'>
         <option value='generale'>generale</option>
         <option value='login'>login</option>

         <option value='posta'>posta</option>
      </optgroup>
      </select>
      <input type='hidden' name='cat' value='' />
      </fieldset>
      <br />
      <fieldset><legend>&nbsp;Inserimento testo&nbsp;</legend>
      <label for='domanda'>Domanda:&nbsp;</label><br />

      <input type='text' id='domanda' name='domanda' value='' size='60' /><br />
      <label for='testo'>Testo di risposta&nbsp;</label><br />
      <textarea id='testo' name='risposta' rows='20' cols='50'></textarea>
   </fieldset>
   <fieldset>
      <input type='submit' name='invia' value='invia dati' />
   </fieldset>
   </form>

</body>
</html>

dove ci sono gli optiongroup è la parte creata dinamicamente dalla funzione di lore.

E ora il codice così come l'ho implementato:

                <label for='categoria'>Categorie per le FAQ</label>
      <select name='categoria' id='categoria' onchange='document.inserimento.cat.value=this'>
         <option label='scegli la categoria' value=''>scegli la categoria</option>
      <?php
      // Creazione dell'elenco delle categorie
      $fnCat = 'FAQ/categorie/categorie.xml';
      if (file_exists($fnCat) && trustedFile($fnCat)) {
         $xml = file_get_contents($fnCat);
         $parseHandle = xml_parser_create();
         xml_parser_set_option($parseHandle, XML_OPTION_SKIP_WHITE, 0);
         xml_parse_into_struct($parseHandle, $xml, $valore, $indice);
         xml_parser_free($parseHandle);
         $indiceArray = 0;

         // Parsing del database delle categorie
         for($index = 0; $index < count($valore); $index++){
            if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'CATEGORIA' && isset($valore[$index]['attributes']['ID']))
               $categoria = $valore[$index]['attributes']['ID'];
            if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'GENERE')
               $arraySet[$indiceArray][$categoria][] = $valore[$index]['value'];
            if(isset($valore[$index]['tag']) && $valore[$index]['tag'] == 'CATEGORIA' && $valore[$index]['type'] == 'close') {
               $categoria = "";
               $indiceArray++;
            }
         }

         // Costruisce il gruppo di selezione a discesa
         // Categoria generica
         echo "<optgroup label='generico'>\n";
         for ($index = 0; $index < count($arraySet[0][generico]); $index++)
            echo "\t\t\t<option value='{$arraySet[0][generico][$index]}'>{$arraySet[0][generico][$index]}</option>\n";
         echo "\t\t</optgroup>\n";
         
         // Categoria cpanel
         echo "\t\t<optgroup label='cpanel'>\n";
         for ($index = 0; $index < count($arraySet[1][cpanel]); $index++)
            echo "\t\t\t<option value='{$arraySet[1][cpanel][$index]}'>{$arraySet[1][cpanel][$index]}</option>\n";
         echo "\t\t</optgroup>\n";
      }
      ?>
      </select>
      <input type='hidden' name='cat' value='' />

Ora una piccola funzioncina che aiuta a capire se un file incluso è presente sul server o si trova altrove, la dove magari osano i lamer :D

        function trustedFile($file) {
   // Si considera affidabili soltanto i file locali del proprio utente. Controlla che il file arrivi proprio dal server e on dall'esterno.
      if (!eregi("^([a-z]+)://", $file) && fileowner($file) == getmyuid()) return true;
   return false;
   }

In pratica controlla semplicemente se il file in questione ha settato lo stesso uid del proprietario dell'account sul server dove sta girando.

Ma torniamo al nostro problema: così com'è funge alla grande, ma c'è un ma. Se volessi implementare un sistema mediante il quale aggiungere nuove categorie al file xml come faccio?

La funzione creata ha di suo il fatto che è implementata in modo specifico per quel file xml e solo per quello; devo riuscire a renderla astratta.

risposto 9 anni fa
Marco Grazia
X 0 X

cioè, se ad esempio al posto di avere 2 categorie ne avessi N?

<categoria id='generico'>

      <genere>generica</genere>

      <genere>sistema operativo</genere>

      <genere>commerciale</genere>

      <genere>email</genere>

      <genere>ftp</genere>

      <genere>attivazione account</genere>

      <genere>configurazione</genere>

      <genere>sicurezza</genere>

   </categoria>

   <categoria id='cpanel'>

      <genere>generale</genere>

      <genere>login</genere>

      <genere>posta</genere>

   </categoria>

        <categoria id='altra_caetgoria1'>

      <genere>generale</genere>

      <genere>login</genere>

      <genere>posta</genere>

   </categoria>

       <categoria id='altra_caetgoria2'>

      <genere>generale</genere>

      <genere>login</genere>

      <genere>posta</genere>

   </categoria>

 ecc ecc.....

Se intendi così, dovrebbe funzionare cmq il codice che ti ho dato.

Se no, scusa, ma nn ho capito cosa intendi,se puoi, fai un esempio, così è più chiaro...

ciao

Lore

risposto 9 anni fa
Lore
Lore
1
X 0 X

Il mio contributo, solo per PHP5!

<?php
$XML = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>
<categorie>
   <categoria id='generico'>
      <genere>generica</genere>
      <genere>sistema operativo</genere>
      <genere>commerciale</genere>
      <genere>email</genere>
      <genere>ftp</genere>
      <genere>attivazione account</genere>
      <genere>configurazione</genere>
      <genere>sicurezza</genere>
   </categoria>
   <categoria id='cpanel'>
      <genere>generale</genere>
      <genere>login</genere>
      <genere>posta</genere>
   </categoria>
        <categoria id='altra_caetgoria1'>
      <genere>generale</genere>
      <genere>login</genere>
      <genere>posta</genere>
   </categoria>
       <categoria id='altra_caetgoria2'>
      <genere>generale</genere>
      <genere>login</genere>
      <genere>posta</genere>
   </categoria>
</categorie>";

$doc = simplexml_load_string($XML);

for($i = 0, $c = count($doc->categoria); $i<$c ; $i++)
{
   echo "Categoria ID: " . $doc->categoria[$i]['id'] . "\n";
   
   for($j = 0, $cc = count($doc->categoria[$i]->genere); $j<$cc ; $j++)
   {
      echo " - " . $doc->categoria[$i]->genere[$j] . "\n";
   }
}
?>

 :bye:

risposto 9 anni fa
Gianni Tomasicchio
X 0 X

Ottimo Gianni,  ora ho anche la funzione astratta per la lettura delle categorie.

 O0

risposto 9 anni fa
Marco Grazia
X 0 X
Effettua l'accesso o registrati per rispondere a questa domanda