Jump to content
Sign in to follow this  
MS-DOS_1991

SimpleXML -> supprimer un noeud XML

Rate this topic

Recommended Posts

Bonjour :)

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 :thumbsup:

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 ;) )

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 ;) ):

<?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 :

$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 :(

... Une idée ?

P.S : ah oui, SimpleXML me supprime l'indentation lorsque je rajoute des éléments... Est-il possible de la restaurer ?

Edited by MS-DOS_1991

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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]

Edited by Spidetra

Share this post


Link to post
Share on other sites
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:

Share this post


Link to post
Share on other sites

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...

Share this post


Link to post
Share on other sites

Je me permet de re-poster car j'ai vraiment du mal avec le DOM :unsure:

Voici mon 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:

Quelqu'un sait-il comment faire ?

Share this post


Link to post
Share on other sites

ç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.

$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 !
}


}

Share this post


Link to post
Share on other sites
Merci beaucoup j'essaie de suite :)

Essaye, avant de remercier :)

J'suis pas du tout sur que ça va marcher du premier coup !

Share this post


Link to post
Share on other sites

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 :

$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');

Share this post


Link to post
Share on other sites

Oui, c'est ce que j'avais fait ;)

Voilà où j'en suis pour le moment :

$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 :$

Edited by MS-DOS_1991

Share this post


Link to post
Share on other sites

Ca y est ! ça supprime :fete:

$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:

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:

edit (le dernier ^_^) : C'est bon, tout fonctionne .

Ce n'est pas (si) compliqué le DOM XML (mais alors c'est lourd :rolleyes: )

Edited by MS-DOS_1991

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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 :thumbsup:

Share this post


Link to post
Share on other sites
edit (le dernier ^_^) : C'est bon, tout fonctionne .

Ce n'est pas (si) compliqué le DOM XML (mais alors c'est lourd :rolleyes: )

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.

Share this post


Link to post
Share on other sites

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 !

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...