8717 sujets

Développement web côté serveur, CMS

Bonjour à tous
J'ai le programme suivant

$dom = new DOMDocument();
$dom -> load(myFile.xml);

// plusieurs modifications de $dom

$dom -> save(myFile.xml);


Je constate que si j'appelle immédiatement après une fonction qui crée un autre DOMDocument et charge le même fichier, le contenu du DOMDocument ne tient pas compte des modifications effectuées précédemment.

J'en conclus que
$dom -> save(myFile.xml);

doit mettre le contenu dans un cache, et donc que le fichier en question n'a pas encore été modifié au moment où on effectue le nouveau chargement.

Question : comment fait-on pour forcer le vidage du cache avant de continuer ?
La documentation PHP sur la fonction flush() n'est pas très claire.

Merci de votre aide.
Modifié par PapyJP (21 Mar 2024 - 12:57)
Modérateur
Salut,

Je vais répondre à côté. Mais peut être que ce genre de choses t'intéresse. Comme je ne fais plus de php. Je ne maintiens plus cette lib que j'ai faite il y a un petit moment :
l'arborescence :

upload/1711036249-11496-arborescence.jpg

index.php

<?php
    $files = array_filter(scandir('.'), function($item){
        return strstr($item, 'example');
    });
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <ul>
        <?php foreach($files as $file): ?>
        <li><a href="./<?= $file ?>"><?= substr($file, 0, strrpos($file, '.')) ?></a></li>
        <?php endforeach ?>
    </ul>
</body>
</html>

data.xml

<?xml version="1.0" encoding="UTF-8"?>
<nodes>

</nodes>


exemple buildtree

<?php
    header('Content-Type: text/xml; charset=utf-8');
    require_once './libs/XMLWriterDom.php';
    $xml = new XMLWriterDom();
    $xml->preserveWhiteSpace = false;
    $xml->formatOutput = true;
    $xml->load('./data.xml');

    $root = $xml->documentElement;
    $dom = [
        "os" => [
            'gnu_linux' => [
                'arch?test=false' => '',
                'debian?paying=false' => [
                    'lenny' => '2009',
                    'Squeeze' => '2011',
                    'Wheezy' => '2013',
                    'Jessie' => '2015',
                    'Stretch' => '2017',
                    'fork' => [
                        'ubuntu' => [
                            'HardyHeron' => '2008',
                            'lucidlynx?best=true' => '2010',
                            'PrecisePangolin' => '2012',
                            'fork' => [
                                'elementary_os' => [
                                    'Luna' => '2013',
                                    'Loki' => '2015',
                                ]
                            ]
                        ]
                    ]
                ],
                'slakware' => [
                    'thirteen' => '2009',
                    'fourteen' => '2012',
                    'fork' => [
                        'suse?best=true&t=i+love+you' => [
                            'six' => '2000',
                            'eight' => '2001',
                        ]
                    ]
                ],
                'redhat?test=false' => '',
            ],
            'windows?state=bad&virus=true&t=i+hate+this+one' => [
                'version_9x' => [
                    'ninetyfive' => '1995',
                    'ninetyeight' => '1998',
                    'ninetyeightse' => '1998',
                ],
                'nt' => [
                    'nt97' => '1997',
                    'nt4' => '2000',
                    'xp' => '2001',
                    'vista' => '2007',
                    'seven' => '2009',
                    'eight' => '2009',
                    'ten' => '2014',
                ]
            ]
        ]
    ];
    $xml->buildTree($root, $dom);
    echo $xml->saveXML();
?>


exemple wrap build tree

<?php
    header('Content-Type: text/xml; charset=utf-8');
    require_once './libs/XMLWriterDom.php';
    $xml = new XMLWriterDom();
    $xml->preserveWhiteSpace = false;
    $xml->formatOutput = true;
    $xml->load('./data.xml');

    $root = $xml->documentElement;
    $dom = [
        [
            "title" => "Rear Window",
            "director" => "Alfred Hitchcock",
            "year" => '1954',
        ],
        [
            "title" => "Full Metal Jacket",
            "director" => "Stanley Kubrick",
            "year" => '1987',
            "actors" => [
                'joker' => [
                    'firstname' => 'Matthew',
                    'lastname' => 'Modine'
                ],
                'cowboy' => [
                    'firstname' => 'Arliss',
                    'lastname' => 'Howard'
                ],
                'baleine' => [
                    'firstname' => 'Vincent',
                    'lastname' => "D'Onofrio"
                ],
            ]
        ],
        [
            "title" => "Mean Streets",
            "director" => "Martin Scorsese",
            "year" => '1973'
        ],
    ];
    foreach($dom as $key => $value){
        $node = $xml->wrapBuildTree($value,'movie');
        $root->appendChild($node);
    }
    echo $xml->display();
?>


XMLWriterDOM

<?php

class XMLWriterDom extends \DomDocument{

    protected $XMLSource = null;
    protected $XMLDestination = null;
    protected $root = null;

    public function __construct()
    {
        parent::__construct();
    }

    public function createSimpleElement($el, $txt = "", $attributes = []){
        $node = $this->createElement($el);
        $this->writeAttributes($node, $attributes);

        if(!empty(trim($txt))){
            $txt = $this->createTextNode($txt);
            $node->appendChild($txt);
        }

        return $node;
    }


    public function wrapBuildTree($data, $txtElementWrapper, $attributes = []){
        $wrap = $this->createElement($txtElementWrapper);
        $this->writeAttributes($wrap, $attributes);

        foreach ($data as $key => $value) {
            $attributes = [];
            if(strstr($key, '?')){
                list($key, $queryString) = explode('?', $key);
                parse_str($queryString, $attributes);
            }
            $item = $this->typeNode($key, $value, $attributes);

            $wrap->appendChild($item);
        }

        return $wrap;
    }

    public function buildTree($root, $data, $attributes = []){
        foreach ($data as $key => $value) {
            $attributes = [];
            if(strstr($key, '?')){
                list($key, $queryString) = explode('?', $key);
                parse_str($queryString, $attributes);
            }
            $wrap = $this->createElement($key);
            $this->writeAttributes($wrap, $attributes);

            $item = $this->typeNode($key, $value, $attributes);

            $node = $wrap->appendChild($item);
            $root->appendChild($node);
        }
    }

    protected function writeAttributes($wrap, $attributes){
        if(!empty($attributes)){
            foreach ($attributes as $attrKey => $attrValue) {
                $wrap->setAttribute($attrKey, $attrValue);
            }
        }

        return $wrap;
    }

    protected function typeNode($key, $value, $attributes){
        if(is_array($value)){
            $node = $this->wrapBuildTree($value, $key, $attributes);
        }

        if(is_string($value)){
            $node = $this->createSimpleElement($key, $value, $attributes);
        }

        return $node;
    }

    /**
     * display xml
     * @return string [xml state]
     */
    public function display(){
        return $this->saveXML();
    }

    /**
     * write xml file
     * @param string $path
     * @return void
     */
    public function save($path = null, $option = 0){
        $path = !is_null($this->XMLDestination)? $this->XMLDestination : $path;
        $this->save($path, $options);
    }

    /**
     * where is xml file
     * @param string $value
     * @return $this
     */
    public function setXMLSource($value){
        if(!is_string($value)){
            throw new \Exception(sprintf("this args (%s) must be a string", $value));
        }
        $this->XMLSource = $value;
        $this->getXML();

        return $this;
    }

    /**
     * where does put xml file
     * @param string $value
     * @return $this
     */
    public function setXMLDestination($value){
        if(!is_string($value)){
            throw new \Exception(sprintf("this args (%s) must be a string", $value));
        }
        $this->XMLDestination = $value;

        return $this;
    }

    public function getRoot(){
        return $this->root;
    }

    /**
     * search xml file and set attribute root
     * @return $this
     */
    protected function getXML(){
        if(strstr($this->XMLSource, 'http') && !strstr(get_headers($this->XMLSource)[0], '200')){
            throw new \Exception(sprintf("%s isn't found", $this->XMLSource));
        }else if(!file_get_contents($this->XMLSource)){
            throw new \Exception(sprintf("%s doesn't exist", $this->XMLSource));
        }
        $this->load($this->XMLSource);
        $this->root = $this->documentElement;

        return $this;
    }

}
?>
Intéressant !
J'ai moi aussi créé une classe d'extension de DOMDocument, avec en gros les mêmes méthodes et que j'ai appelée DOM, avec le chargement du fichier .xml dans la méthode __construct
Mon problème est que la méthode "save", qui est très similaire à ce que tu as écrit, ne garantit pas que les données sont effectivement écrites dans le fichier .xml au sortir de la méthode, ce qui est normal à cause du système de cache de PHP.
Moralité si je fais quelque chose comme

$dom1 = new DOM('myFile.xml');
// manipulations dans $dom1
$dom1 -> save();
$dom2 = new DOM('myFile.xml');

$dom2 est chargé à partir du fichier tel qu'il est dans le système de fichiers, c'est à dire sans les modifications faites dans $dom1.

Je crois savoir qu'il existe une possibilité de forcer l'écriture physique du fichier à l'intérieur de la méthode save(() mais je ne sais pas comment on fait cela.
Modérateur
Bonjour,

De quel cache parle-t-on ? Et le problème apparait-il systèmatiquement ou seulement de temps en temps ?

Comme d'habitude, on n'a qu'une partie du code, et c'est en conséquence "compliqué" de reproduire ton problème et de faire le bon diagnostic.

Et il me semble peu clair que ce soit juste un problème de cache.

Soit le fichier a.xml suivant :
<?xml version="1.0"?>
<books></books>


Soit le code php suivant :
$dom=new DOMDocument();
$dom->load("a.xml");
$books=$dom->getElementsByTagName("books");
$books[0]->append($dom->createElement("book"));
$dom->save("a.xml");

$dom2=new DOMDocument();
$dom2->load("a.xml");
echo count($dom2->getElementsByTagName("book"));

À chaque fois qu'on exécute ce code, le nombre de <book> dans le xml augmente de 1, et la modification est répercutée immédiatement car on "voit" dans la page le compteur qui s'incrémente de 1.

Il se peut que j'ai du bol. Mais bon ... Smiley cligne

Amicalement,
Modérateur
PapyJP a écrit :
Intéressant !
J'ai moi aussi créé une classe d'extension de DOMDocument, avec en gros les mêmes méthodes et que j'ai appelée DOM, avec le chargement du fichier .xml dans la méthode __construct
Mon problème est que la méthode "save", qui est très similaire à ce que tu as écrit, ne garantit pas que les données sont effectivement écrites dans le fichier .xml au sortir de la méthode, ce qui est normal à cause du système de cache de PHP.
Moralité si je fais quelque chose comme

$dom1 = new DOM('myFile.xml');
// manipulations dans $dom1
$dom1 -&gt; save();
$dom2 = new DOM('myFile.xml');

$dom2 est chargé à partir du fichier tel qu'il est dans le système de fichiers, c'est à dire sans les modifications faites dans $dom1.

Je crois savoir qu'il existe une possibilité de forcer l'écriture physique du fichier à l'intérieur de la méthode save(() mais je ne sais pas comment on fait cela.


En fait , tu oublis d'indiquer dans quel fichier sauvegarder tes modification: https://www.php.net/manual/fr/domdocument.save.php


$dom1 = new DOM('myFile.xml');
// manipulations dans $dom1
$dom1 -> save('myFile.xml');
$dom2 = new DOM('myFile.xml');

a moins que ...

$dom1 = new DOM('myFile.xml');
// manipulations dans $dom1
$dom1 -> save();
$dom2 = new DOM($dom1);

... passes, mais j'ai jamais essayer de faire ça

Il serait plus logique d'en passer par un loadXML https://www.php.net/manual/fr/domdocument.loadxml.php


$dom1 = new DOM('myFile.xml');
// manipulations dans $dom1
$dom1 -> save();
$dom2 = new DOMDocument();
$dom2->loadXML($dom1);



Pas tester, du coup je ne sais pas vraiment quoi faire de $dom1 -> save(); (le laisser, l'enlever, le corriger)


Cdt
Modifié par gcyrillus (22 Mar 2024 - 15:14)
save() est une méthode de mon objet DOM qui (s'il n- a pas de paramètre) sauve le fichier xml dans le même fichier qui a été utilisé à la création.
J'utilise cette classe depuis plus de 10 ans, sans problème.
En fait j'ai deux scripts php
update.php
complete.php

Si je lance update.php puis complete.php tout fonctionne
Mais comme j'avais envie de ne pas lancer deux script, j'ai mis
include_once update.php dans complete.php
On n'a donc pas de rupture entre les deux, ce qui fait que le fichier n'est pas encore physiquement sauvé.
Modérateur
Bonjour,

PapyJP a écrit :
"Normalement" les fichiers sont mis en cache, ou bufferisés si tu préfères, ce qui est compréhensible pour ne pas ralentir le fonctionnement. Il existe une fonction flush voir https://www.php.net/manual/fr/function.fflush.php
Ce que je ne vois pas c’est comment utiliser cette fonction dans ce cas


Tu confonds un peu tout.

La fonction fflush() s'emploie avec fopen(). Et elle permet d'enregistrer quelque chose stocké dans un buffer alors qu'on n'a pas encore "fermer" le processus d'enregistrement avec fclose(). Tant que fclose() n'a pas eu lieu (ou qu'une fin de script n'a pas eu lieu), il n'y a pas l'assurance que l'enregistrement physique ait eu lieu. Ça permet donc de forcer des enregistrements intermédiaires physiques, sans devoir attendre le fclose() final, et éviter par exemple de tout perdre en cas d'erreurs ou de timeouts, ou encore dans le cas d'écritures répétées et espacées dans le temps (comme dans une log) de ne pas avoir à fermer et rouvrir le fichier de log à chaque fois.

Ce que tu essaies de faire (en utilisant la méthode DOMDocument::save()) est un mécanisme différent. Il se peut qu'en interne, cette méthode utilise fopen() et fclose(), mais tu n'as jamais la main pour "vider" le buffer, car le vidage de ce buffer sera effectué avant de te rendre la main justement. Et tu n'as de toute façon à aucun moment en main un "pointeur" de fichier qui te permettrait d'utiliser fflush().

En gros, DOMDocument::save(), c'est un peu du tout en un comme le fait file_put-contents(). En tout cas, c'est comme ça que je le vois.

Et a priori, je ne vois pas trop où serait mis "en cache" le contenu d'un fichier xml que tu viens d'enregistrer via la méthode DOMDocument::save().

Si cela ne marche pas chez toi actuellement, c'est que tu as probablement ailleurs une erreur qui traine ou un problème de conception, mais pas à cause d'une mise en cache ou en buffer (j'en serais très surpris en tout cas).

Bref, ton diagnostic qui pointe un problème de cache ou de bufférisation me semble erroné.

Amicalement,
parsimonhi a écrit :

Il se peut que j'aie du bol. Mais bon ... Smiley cligne ,

ce n'est pas du bol, c'est simplement que la taille de ton fichier xml est très petite par rapport à celle du mien (37Ko)
Modérateur
Bonjour,

PapyJP a écrit :

ce n'est pas du bol, c'est simplement que la taille de ton fichier xml est très petite par rapport à celle du mien (37Ko)

À ma connaissance, l'enregistrement n'est pas différé. Que ça fasse 1Ko ou 1Mo ne change rien à l'affaire (je viens de faire un test avec un fichier de 987Ko. Ça marche sans problème.

Amicalement,
Modérateur
PapyJP a écrit :
save() est une méthode de mon objet DOM qui (s'il n- a pas de paramètre) sauve le fichier xml dans le même fichier qui a été utilisé à la création.
J'utilise cette classe depuis plus de 10 ans, sans problème.
En fait j'ai deux scripts php
update.php
complete.php

Si je lance update.php puis complete.php tout fonctionne
Mais comme j'avais envie de ne pas lancer deux script, j'ai mis
include_once update.php dans complete.php
On n'a donc pas de rupture entre les deux, ce qui fait que le fichier n'est pas encore physiquement sauvé.


Essaie juste pour vérifier que cela peut avoir une incidence en ajoutant quel fichier doit être physiquement mis à jour avant de le recharger la ligne suivante (c'est vraiment la ligne suivante?). Nous n'avons pas ton code pour se faire une idée précise.
J'ai un peu jouer avec les fichiers XML et suis passer tout dans un fichier ou par des include pour séparer et réutilisée certaines fonctions ou parties de codes et quasiment à chaque fois j'ai du réadapter un peu le code parceque des valeurs n’étaient plus celle que j'attendais. Parfois je faisais un $valeur = $valeur->fonctionTruc() au lieu de juste $valeur->fonctionTruc() et parfois je modifiais simplement la logique en récupérant/passant la valeur autrement dans une autre fonction.

N'étant pas développeur, je n’évalue pas au mieux comment ces include() sont pris en compte. Suis je dans une class ou pas , faut-il un return , suis je juste dans une fonction : En gros à quel moment telle valeur est disponible et si elle est à jour .
Les versions de PHP et sa configuration ont aussi leur incidences . On peut ou ne pas avoir d'erreurs qui s'affichent ajoutant à la confusion. Çà marche alors pas de questions.

<hs>Je joue pas mal avec un vieux CMS Français: PluXml, où toutes les données sont stockées en XML Smiley smile , j'avoue donc être séduit par le format json et avoir appris à bien aimé Xpath par effet de bords ...</hs>
Modifié par gcyrillus (22 Mar 2024 - 22:05)
J’ai utilisé abondamment xml dans les années 2000 et je m’en suis beaucoup servi pour exporter des données depuis des fichiers Excel, puis transfert par ftp sur un serveur et lecture de ces données en php pour les mettre dans une base de données.
C’est exactement cela que fait mon programme :
1) mettre à jour un ficher xml à partir d’un autre ficher xml
2) mettre à jour la base de données à partir du fichier xml modifié
Mon problème se produit à la jonction entre ces deux phases.
Pour le reste j’utilise plutôt JSON pour mes échanges, mais je n’ai pas trouvé d’outil en Excel VBA qui écrive ou lise du JSON.

D’un autre côté je suis en train de changer de méthode de travail, ce n’est donc plus très important