Version complète: sur le forum Webmaster Hub : SimpleXML -> supprimer un noeud XML
Webmaster Hub > Création et exploitation de Sites Internet > Les langages du Net > XML et ses dialectes
MS-DOS_1991
Bonjour smile.gif

Je viens de me mettre au XML avec la librairie SimpleXML intégrée à PHP 5. Tout marche parfaitement et j'arrive en 2 lignes à afficher tout ce que je veux de mon fichier a_thumbsup_20.gif

Le problème, c'est qu'il n'y a apparemment pas de fonction removeNode() ou removeChild() intégrée à cette classe, ce qui fait que je ne peux pas supprimer le noeud courant (utile dans mon cas car je fais un compteur de conenctés wink.gif )

Dans la doc PHP, quelqu'un a écrit une classe, extendedSimpleXML (class extendedSimpleXML extends SimpleXMLElement) qui contient une méthode removeNode...

Voici donc mon fichier XML (membres et invites sont séparés wink.gif ):
CODE
<?xml version="1.0" encoding="windows-1250"?>
<online_users>
<members>
</members>
<guests>
<user>
<ip>01.234.56.789</ip>
<last_activity_time>1157100123</last_activity_time>
<last_activity_url>/forums/index.php?show_forums</last_activity_url>
</user>
<user>
<ip>12.345.67.890</ip>
<last_activity_time>1157100456</last_activity_time>
<last_activity_url>/forums/index.php?show_forums</last_activity_url>
</user>
<user>
<ip>23.456.78.901</ip>
<last_activity_time>1157100789</last_activity_time>
<last_activity_url>/forums/index.php?show_forums</last_activity_url>
</user>
<user>
<ip>34.567.89.012</ip>
<last_activity_time>1157101234</last_activity_time>
<last_activity_url>/forums/index.php?show_forums</last_activity_url>
</user>
</guests>
</online_users>


et le code PHP :
CODE
$online_users = simplexml_load_file('././inc/online_users.xml');
$online_users = new extendedSimpleXML($online_users->asXML());
foreach ($online_users->guests->user AS $user)
if ($user->ip == $_SERVER['REMOTE_ADDR'])
{
$user->last_activity_time = time();
$user->last_activity_url = $_SERVER['REQUEST_URI'];
$found = true;
$online_users->asXML('././inc/online_users.xml');
}
elseif (time()-$user->last_activity_time > 180)
{
$user->removeNode();
$online_users->asXML('././inc/online_users.xml');
}

if (!$found)
{
$user = $online_users->guests->addChild('user');
$user->addChild('ip', $_SERVER['REMOTE_ADDR']);
$user->addChild('last_activity_time', time());
$user->addChild('last_activity_url', $_SERVER['REQUEST_URI']);
$online_users->asXML('././inc/online_users.xml');
}


Le problème est que la fonction removeNode() me fait apparemment sortir directement de ma boucle foreach, ce qui fait que je ne peux supprimer qu'un élément à la fois sad.gif

... Une idée ?

P.S : ah oui, SimpleXML me supprime l'indentation lorsque je rajoute des éléments... Est-il possible de la restaurer ?
rportal
Comme son nom l'indique simpleXML est une API simple de manipulation de contenu XML. Son objectif est de pouvoir manipuler rapidement l'XML pour l'affichage et la recherche. Si tu veux vraiment supprimer un noeud de ton arbre xml, utilise une API qui est complète pour la modification du xml: DOM.
http://fr2.php.net/manual/fr/function.dom-...removechild.php

il y a un pont entre dom et simpleXML si tu veux continuer à utiliser simpleXML pour l'affichage.

Bon courage
Spidetra
SimpleXML sert essentiellement à faire simplement des traitements simple sur des flux XML.
Si tu veux traiter un flux, pourquoi ne pas se tourner vers DOM ?
[edit]rportal a posté pendant que je tapais la causette [/edit]
MS-DOS_1991
CITATION
Si tu veux traiter un flux, pourquoi ne pas se tourner vers DOM ?
Pourquoi ? Parce que SimpleXML me convient parfaitement en ce qui concerne la lecture...

Si la seule solution est de passer par le DOM (qui me paraît beaucoup plus complexe d'utilisation) ... hé bien je passerai par le DOM sick.gif
rportal
Il n'y a jamais une seule solution, mais il faut utiliser les outils à ta disposition en fonction de leurs objectifs. Les détourner peut se révéler utile de temps en temps mais ici il s'agit de manipuler des objets qui utilisent pas mal de ressource donc autant utiliser ceux qui sont optimisés pour l'utilisation que tu désires. Comme tu le dis, simpleXML te convient pour la lecture d'un XML et cela est normal vu qu'il a été développé pour cela... mais pas pour manipuler en profondeur du xml...
MS-DOS_1991
Je me permet de re-poster car j'ai vraiment du mal avec le DOM unsure.gif

Voici mon code
CODE
foreach ($online_users->guests->user AS $user)
  {
    if ($user->ip == $_SERVER['REMOTE_ADDR'])
    {
      $user->last_activity_time = time();
      $user->last_activity_url  = $_SERVER['REQUEST_URI'];
      $found = true;
      $online_users->asXML('././inc/online_users.xml');
    }
    elseif (time()-$user->last_activity_time > 180)
    {
      $dom = new DOMDocument();
      $dom_sxe = dom_import_simplexml($online_users);
      // Que mettre ici ?
      $online_users = simplexml_import_dom($dom_sxe);
      $online_users->asXML('././inc/online_users.xml');
    }
  }


Je veux importer mon objet simplexml dans un objet DOM, pour ensuite supprimer le noeud <user> en cours d'analyse depuis le DOM, réimporter l'objet dans simplexml et sauvegarder le fichier...

Le problème est qu'il faut connaitre la position du noeud pour le détruire, l'idéal serait une méthode "current_item()" mais ... ça m'étonnerais que ce soit aussi simple wacko.gif

Quelqu'un sait-il comment faire ?
Spidetra
ça fait un moment que je n'ai pas maniuplé DOM en Php mais ça devrait ressembler à un truc de ce style.
Je n'utilise pas simpleXML, je trouve trop compliqué de mélanger simpleXML et DOM

Il faut que tu vérifie les appels de méthodes et l'algo, il y a surement des erreurs. C'est juste un brouillon pour te mettre sur la voie.

CODE
$doc = new DomDocument();
$doc->loadXML( $xml ); // méthode à adapter en fct de tes besoins : load(), loadXML(), etc...

// tu recherche tout les users. Tu peux les récupérer par leur nom
$users = $doc->getElementByTagName('user');

// tu boucle sur l'ensemble de tes users
foreach( $users as $user ) {

// Tu bloucle sur tout les fils de ton node
     foreach( $user->childNodes as $node ) {
        if ( $node->nodeType != XML_ELEMENT_NODE ) continue;
        if ( $node->tagName == '' ) {   // ici tu teste le tagName qui t'intéresse
             $ip = $node->nodeValue;
        }
        if ( $node->tagName == '' ) {   // ici tu teste le tagName qui t'intéresse
             $last_activity_time = $node->nodeValue;
        }
         ...... etc
     }

    // Tout les fils ont été traités tu as récupéres les valeurs qui t'intéresse
   // Ici tu fait tes controles
   if ( il faut eliminer ce noeud ) {
       $doc->removeChild( $node );  // J'ai un big doute sur cet appel !
  }


}
MS-DOS_1991
Merci beaucoup j'essaie de suite smile.gif
Spidetra
CITATION(MS-DOS_1991 @ vendredi 1 septembre 2006, 16h55) *
Merci beaucoup j'essaie de suite smile.gif



Essaye, avant de remercier smile.gif
J'suis pas du tout sur que ça va marcher du premier coup !
Spidetra
En relisant je viens de voir que tu avais des users sous deux arbo : <membre> et <guest>

Si tu ne veux traiter que les guests tu vas ajouter en début de code :

CODE
$doc = new DomDocument();
$doc->loadXML( $xml ); // méthode à adapter en fct de tes besoins : load(), loadXML(), etc...

// -> rajout ici : d'abord tu récupére le noeud guest ou memebre
$guest = $doc->getElementByTagName('guest');

// tu recherche tout les users. Tu peux les récupérer par leur nom
// J'ai remplacé $doc par $guest
$users = $guest->getElementByTagName('user');
MS-DOS_1991
Oui, c'est ce que j'avais fait wink.gif

Voilà où j'en suis pour le moment :
CODE
$document = new DomDocument();
$document->load('././inc/online_users.xml');

$guests = $document->getElementsByTagName('guests');
foreach ($guests AS $guests)
{
$users = $guests->getElementsByTagName('user');
foreach ($users AS $user)
{
foreach ($user->childNodes AS $node)
{
if ($node->nodeType != XML_ELEMENT_NODE)
continue;

// Si on sait que l'on se trouve dans le bloc <user> du visiteur courant
// Alors on met a jour les valeurs (lastactivity, etc)
if ($found)
{
if ($node->tagName == 'last_activity_time')
$node->nodeValue = time();
}
// Si on se trouve dans le bloc <user> du visiteur courant (on ne le savait pas encore)
// Alors on passe au <tag> suivant
elseif ($node->tagName == 'ip' && $node->nodeValue == $_SERVER['REMOTE_ADDR'])
{
$found = true;
continue;
}
// Si on n'est pas dans le bloc <tag> de l'utilisateur courant et que l'utilisateur de ce tag est inactif depuis trop longtemps
elseif ($node->tagName == 'last_activity_time' && time()-$node->nodeValue > 180)
$guests->removeChild($user);
}
}
}


Mais je suis un peu perdu et je pense que mon code est redondant :$
MS-DOS_1991
Ca y est ! ça supprime IMSTP2.gif

CODE
$document = new DomDocument();
$document->load('././inc/online_users.xml');

$guests = $document->getElementsByTagName('guests');
foreach ($guests AS $guests)
{
$users = $guests->getElementsByTagName('user');
foreach ($users AS $user)
{
$found = false;
foreach ($user->childNodes AS $node)
{
if ($node->nodeType != XML_ELEMENT_NODE)
continue;

if ($found)
{
echo 'current_user';
if ($node->tagName == 'last_activity_time')
{
$new_last_activity_time = $document->createElement('last_activity_time', 123);
$user->replaceChild($new_last_activity_time, $node);
continue;
}
}
// Si on se trouve dans le bloc <user> du visiteur courant (on ne le savait pas encore)
// Alors on passe au <tag> suivant
elseif ($node->tagName == 'ip' && $node->nodeValue == $_SERVER['REMOTE_ADDR'])
{
$found = true;
continue;
}
// Si on n'est pas dans le bloc <tag> de l'utilisateur courant et que l'utilisateur de ce tag est inactif depuis trop longtemps
elseif ($node->tagName == 'last_activity_time' && time()-$node->nodeValue > 180)
{
echo 'user_to_be_removed';
$guests->removeChild($user);
}
}
}
}
$document->save('././inc/online_users.xml');


Il me reste encore cependant à "updater" le noeud <last_activity_time></last_activity_time> en lui assignant la valeur du timestamp courant obtenu avec time().

J'ai épluché la doc et malheureusement, le seul moyen de modifier le contenu d'un noeud semble être... d'en créer un autre puis de remplacer l'ancien par le nouveau rolleyes.gif

Mon code affiche bien "current_user", reconnaît bien le tag <last_activity_time> et la fonction replaceChild ne renvoie pas d'erreur, pourtant le noeud n'est pas mis à jour mad2.gif

edit (le dernier ^_^) : C'est bon, tout fonctionne .
Ce n'est pas (si) compliqué le DOM XML (mais alors c'est lourd rolleyes.gif )
Spidetra
tu as +sieurs noeud guest ou un seul ?
si tu n'as qu'un le foreach sur $guests ne sert à rien.
Pour le replace pas d'idées.
MS-DOS_1991
J'ai un seul noeud : <guests><user>...</user><user>...</user></guests>

Mais sans le foreach ça plante et comme je ne sais pas (encore) comment dire à PHP comment aller à l'indice 0, je fais une itération de foreach ...

En tout cas, merci beaucoup de ton aide a_thumbsup_20.gif
Spidetra
CITATION
edit (le dernier ^_^) : C'est bon, tout fonctionne .
Ce n'est pas (si) compliqué le DOM XML (mais alors c'est lourd rolleyes.gif )





si tu doit juste parcourir un flux XML, sans le modifier, essaye de remplacer simpleXML par SAX.
C'est plus dur à utiliser, mais c'est bc plus performant.
edouardo70
Bonjour,
Je souhaiterais utiliser DOMDocument pour supprimer des noeuds. Un peu comme MS-DOS_1991, j'ai utilisé simpleXML jusqu'ici, mais me voilà bloqué.
Si je poste ici, c'est que j'ai un problème pour activer dom dans apache et qu'il est dit de partout "Sous PHP 5, il n'y a rien à installer, le package DOM fait partie du langage de base."...

Je suis sous fedora, avec un PHP Version 5.1.6.

Si par chance, vous pouviez m'expliquer la procédure...

Par avance , merci !
Ceci est une version "bas débit" de notre forum. Pour voir la version complète avec plus d'information, la mise en page et les images, veuillez cliquer ici.