IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

DOMDocument.save() casse mon fichier d'origine


Sujet :

Langage PHP

  1. #1
    Membre confirmé Avatar de Aizen64
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    561
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 561
    Points : 462
    Points
    462
    Par défaut DOMDocument.save() casse mon fichier d'origine
    Bonjour,

    j'écris un script qui doit modifier des fichiers de templates Twig à la volée pour mettre à jour une donnée un peu partout.

    Mon script s'exécute correctement, le hic c'est qu'au moment de l'appel de saveHTMLFile(), mon fichier de destination a non seulement des balises HTML/body en plus mais toutes les URL dans les attributs src par ex sont désormais encodés, tout comme certains opérateurs == de twig qui deviennent &gt.


    En fait, j'aimerais faire du setAttribute() tel que je fais actuellement, mais sans qu'aune autre chose ne soit altérée dans le document.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    <?php
    $document = new DOMDocument();
                $document->loadHTMLFile
                (
                    $twigFile, LIBXML_HTML_NODEFDTD /* pas d'ajout de DTD dans le doc a la sauvegarde */
                );
     
                $scriptTagsElements = $document->getElementsByTagName("script" );
     
                /** @var DOMElement $scriptTagElement */
                foreach ( $scriptTagsElements as $scriptTagElement )
                {
                    $regex = "/\-\d+?\.\d+\.js$/";
     
                    if ( preg_match( $regex , $scriptTagElement->getAttribute("src" ) ) )
                    {
                        $newVersion = self::getVersion();
     
                        $assetsPathNoVersion = preg_split( "/-/", $scriptTagElement->getAttribute( "src" ) )[0];
     
                        $scriptTagElement->setAttribute( "src", $assetsPathNoVersion . "-" . $newVersion . ".js" );
                    }
                }
     
                $document->saveHTMLFile( $twigFile );
    Des idées pour contourner le problème ?

  2. #2
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 896
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 896
    Points : 6 655
    Points
    6 655
    Par défaut
    Citation Envoyé par Aizen64 Voir le message
    j'écris un script qui doit modifier des fichiers de templates Twig
    Un template Twig n'est pas à proprement parler un fichier html dans la mesure où il utilise des caractères comme > et < pour autre chose que d'ouvrir ou fermer une balise.

    Mon script s'exécute correctement, le hic c'est qu'au moment de l'appel de saveHTMLFile() ...
    C'est là que tu t'en rends compte, mais en réalité le mal est fait (=les corrections sont appliquées) au moment du parsing du fichier et de la construction de l'arbre DOM (donc au niveau de DOMDocument::loadHTMLFile()).

    ... mon fichier de destination a non seulement des balises HTML/body ...
    C'est normal, un document html est composé à minima d'un élément racine (ce qui est indispensable pour obtenir un arbre), et en cas d'absence, DOMDocument ajoute les balises html et body automatiquement. Ce comportement peut être désactivé avec l'option LIBXML_HTML_NO_IMPLIED, mais je te déconseille de le faire car dans ce cas là, la première balise rencontrée deviendra alors l'élément racine. Concrètement si ton document est <div><span></span></div> pas de problème car il y a déjà un élément racine, par contre si ton document est <div></div><span></span> il te le transformera en <div><span></span></div> (il voit la balise fermante div comme ne pouvant pas appartenir à l'élément racine puisqu'il y a encore du contenu après, et donc il l'enlève, puis arrivé à la fin, comme la racine n'est pas fermée, il rajoute </div>).
    Pour solutionner le problème de l'ajout de html et body, il suffit de reconstituer la chaîne html par concaténation des nœuds enfant de body. (Encore faut-il être sûr que body a bien été rajouté par DOMDocument et qu'il n'était pas présent au départ.)

    ... toutes les URL dans les attributs src par ex sont désormais encodés ...
    Là il faudrait que tu nous montres, poste un fichier template qui reproduit le problème.

    , tout comme certains opérateurs == de twig qui deviennent &gt.
    En l'occurrence ce qui produit &gt; c'est le remplacement de > par son entité html, le = n'est pas vraiment problématique.

  3. #3
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 896
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 896
    Points : 6 655
    Points
    6 655
    Par défaut
    J'avoue que je suis assez mitigé quant à l'emploi de libxml (DOMDocument et consortes) dans ce cas de figure précis. Car une template twig qui a vocation à produire un document html (ou même une partie de ce document) parfaitement correct, peut elle-même être un document html (ou une partie de document html) totalement incorrect dés lors qu'on l'observe uniquement du point de vue html.

    Ce qu'il aurait fallu faire, c'est de rendre paramétrable le numéro de version du script au préalable. Maintenant que le mal est fait, je vois difficilement comment on peut éditer ça proprement.

  4. #4
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 896
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 896
    Points : 6 655
    Points
    6 655
    Par défaut
    À ta place et vu qu'il s'agit d'une modification ponctuelle, je traiterai ça en ligne de commande à coup de regex avec utilisation des yeux et du cerveau (pas facile):
    1. Je fais un grep pour être sûr que ma pattern cible bien ce que je veux et pas autre chose.
    2. J'applique un sed (avec copie en .bak des anciens fichiers) pour mettre un placeholder en lieu et place du numéro de version.
    3. Je copie les fichiers obtenus dans un coin pour la prochaine fois.
    4. J'effectue de nouveau avec sed le remplacement du placeholder par le numéro de version voulu.


    Peut-être qu'il est possible de faire mieux avec xidel (qui fait tout même le café), mais ça fait un moment que je ne l'ai pas utilisé...

  5. #5
    Membre confirmé Avatar de Aizen64
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    561
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 561
    Points : 462
    Points
    462
    Par défaut
    Ben justement, j'ai essayé une alternative avec le shell et sed.

    J'ai pas la regex sous la main (j'updaterai plus tard ce poste mais l'idée est de :
    - lire le fichier de version, ça c'est OK,
    - remplacer dans tous les fichiers .twig le numéro de version à la volée via l'utilisation de find avec option -exec pour remplacer la partie suivante

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <script src="unScript-3.4.js"></script>
    Donc de tête j'ai du écrire une regex comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    's/\-[0-9]+?\..*\.js/g'
    Ou en français : commence par un tiret, puis un ou plusieurs chiffres suivi optionnellement d'un point et d'une suite de chiffre puis de l'extension JS.

    Qui ne marche pas.

    Ça m'a un peu beaucoup cassé la tête en fin d'après midi.

    Rappel s/ avec sed signifie substituer.

    Des idées ?

    Sur ce bonne nuit.

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 896
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 896
    Points : 6 655
    Points
    6 655
    Par défaut
    Pour vérifier que les cibles atteintes sont correctes:
    Code shell : Sélectionner tout - Visualiser dans une fenêtre à part
    grep -E '<script\>[^>]*\<src="nomscript-[0-9][0-9.]*\.js"' *.twig
    Il est important de faire un description assez large pour éviter par la suite de remplacer n'importe quoi n'importe où.

    Pour procéder au remplacement:
    Code shell : Sélectionner tout - Visualiser dans une fenêtre à part
    sed -E 's/(<script\>[^>]*\<src="nomscript-)[0-9][0-9.]*(\.js")/\1remplacement\2/' *.twig

    Quand on est sûr du résultat, il suffit d'ajouter le switch -i.bak à la commande sed pour modifier les fichiers et obtenir une copie des originaux en .bak (voir man sed).

  7. #7
    Membre confirmé Avatar de Aizen64
    Profil pro
    Inscrit en
    Mai 2007
    Messages
    561
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2007
    Messages : 561
    Points : 462
    Points
    462
    Par défaut
    Rah, ma regex passe toujours pas, ai je loupé quelque chose d'évident ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #!/usr/bin/env bash
    TEMPLATES_DIRECTORY=../templates
    REGEX=[0-9+]?\.[0-9]+
    NEW_VERSION="$(cat ../templates/shared/version.twig | grep -E ^$REGEX)"
     
    find $TEMPLATES_DIRECTORY -type f -iname '*.twig' -exec sed -i {} -e "s/\-[0-9]+?\.[0-9]\.js/-$NEW_VERSION.js/g" \;
    Je continue d'expérimenter, si quelqu'un a une idée je suis preneur.

  8. #8
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 896
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 896
    Points : 6 655
    Points
    6 655
    Par défaut
    Citation Envoyé par Aizen64 Voir le message
    Rah, ma regex passe toujours pas, ai je loupé quelque chose d'évident ?
    Visiblement mon précédent message et notamment la phrase:"Il est important de faire un description assez large pour éviter par la suite de remplacer n'importe quoi n'importe où."


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #!/usr/bin/env bash
    TEMPLATES_DIRECTORY=../templates
    REGEX=[0-9+]?\.[0-9]+
    NEW_VERSION="$(cat ../templates/shared/version.twig | grep -E ^$REGEX)"
     
    find $TEMPLATES_DIRECTORY -type f -iname '*.twig' -exec sed -i {} -e "s/\-[0-9]+?\.[0-9]\.js/-$NEW_VERSION.js/g" \;
    On ne pipe pas cat avec grep qui prend naturellement un nom de fichier en paramètre:
    Code man grep : Sélectionner tout - Visualiser dans une fenêtre à part
    grep [OPTIONS] PATTERN [FILE...]

    À propos de sed (et de grep d'ailleurs): par défaut sa syntaxe regex est BRE (Basic Regular Expression), syntaxe dans laquelle le quantificateur + n'existe pas et encore moins la notion de quantificateur non-gourmand (donc exit les +?, *?, et ??). Avec le switch -E, on peut changer cette syntaxe pour l'ERE (Extended Regular Expression) qui est un peu plus évoluée mais où les quantificateurs non-gourmands n'existe toujours pas.
    Le caractère - n'est pas un caractère spécial, inutile de l'échapper.
    Code man sed : Sélectionner tout - Visualiser dans une fenêtre à part
    sed [OPTION]... {script-only-if-no-other-script} [input-file]...
    Comme pour grep le nom du ou des fichiers cible sont à la fin et pas après le switch -i qui lui éventuellement attend une extension pour le renommage des fichiers originaux (voir post précédent). Donc:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    find blahblahblah -exec sed -i.bak scriptsed {} \;
    find n'est utile que si il est nécessaire d'explorer des sous-répertoires, si tous tes fichiers sont dans le même répertoire, sed est capable de traiter plusieurs fichiers à la suite: donc un simple glob suffit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sed SCRIPT ../templates/*.twig

Discussions similaires

  1. Réponses: 3
    Dernier message: 09/06/2016, 16h36
  2. [Servlet] Comment référencer mon fichier CSS
    Par fytheone dans le forum Servlets/JSP
    Réponses: 3
    Dernier message: 07/01/2005, 09h58
  3. Réponses: 2
    Dernier message: 28/09/2004, 09h41
  4. [debutant][Fichier] Comment obtenir le path de mon fichier ?
    Par Soulsurfer dans le forum Entrée/Sortie
    Réponses: 2
    Dernier message: 22/06/2004, 17h09
  5. __declspec(dllexport) dans mon fichier header mais...?
    Par Jasmine dans le forum Autres éditeurs
    Réponses: 1
    Dernier message: 03/03/2004, 18h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo