PHP - création de modèles avec des balises personnalisées - est-ce une utilisation légitime d'eval ?

StackOverflow https://stackoverflow.com/questions/3326446

  •  29-09-2020
  •  | 
  •  

Question

Aperçu

Vers la fin de 2009, j'ai écrit un système de modèles simple pour PHP/HTML destiné à être utilisé en interne par nos concepteurs pour les sites Web de type brochure.L'objectif du système est de permettre la création de modèles en HTML par ailleurs pur via des balises personnalisées traitées par PHP.Par exemple, une page modèle pourrait ressembler à ceci :

<tt:Page template="templates/main.html">
  <tt:Content name="leftColumn">
    <p> blah blah </p>
    ...
  </tt:Content>
  <tt:Content name="rightColumn">
    <p> blah blah </p>
    ...
  </tt:Content>
</tt:Page>

Le modèle lui-même pourrait ressembler à ceci :

<html>
  <head>...</head>
  <body>
    <div style="float:left; width:45%">
      <tt:Container name="leftColumn" />
    </div>
    <div style="width:45%">
      <tt:Container name="rightColumn" />
    </div>
  </body>
</html>

Outre les balises Page et Contenu/Conteneur, quelques autres balises sont incluses dans le noyau pour des tâches telles que le contrôle de flux, l'itération sur une collection, la sortie de valeurs dynamiques, etc.Le framework est conçu de manière à ce qu'il soit très facile d'ajouter votre propre ensemble de balises enregistrées sous un autre préfixe et espace de noms.

Balises personnalisées vers PHP

Comment analysons-nous ces balises personnalisées ?Puisqu'il n'y a aucune garantie que le fichier HTML soit un XML bien formé, des solutions comme XSLT/XPATH ne seront pas fiables.Au lieu de cela, nous utilisons une expression régulière pour rechercher des balises avec des préfixes enregistrés et les remplaçons par du code PHP.Le code PHP est une conception basée sur une pile...lors de la rencontre d'une balise d'ouverture, un objet représentant la balise est créé et poussé sur la pile, et sa "fonction d'initialisation" (le cas échéant) s'exécute.Chaque fois qu'une balise de fermeture enregistrée est rencontrée, l'objet le plus récent est retiré de la pile et sa « fonction de rendu » s'exécute.

Ainsi, une fois que le framework a remplacé les balises de modèle par PHP, notre page d'exemple pourrait ressembler à ceci (en réalité, c'est un peu plus moche) :

<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?>
  <?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?>
    <p> blah blah </p>
    ...
  <?php $tags->pop(); ?>
  <?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?>
    <p> blah blah </p>
    ...
  <?php $tags->pop(); ?>
<?php $tags->pop(); ?>

Le bon, le mauvais et eval

Maintenant, comment exécuter notre code PHP nouvellement généré ?Je peux penser à quelques options ici.Le plus simple est simplement de eval la chaîne, et cela fonctionne assez bien.Cependant, n'importe quel programmeur vous dira "eval est mauvais, ne l'utilisez pas..." donc la question est : existe-t-il quelque chose de plus approprié que eval que nous pouvons utiliser ici ?

J'ai envisagé d'utiliser un fichier temporaire ou mis en cache, en utilisant php:// flux de sortie, etc., mais pour autant que je sache, ceux-ci n'offrent aucun réel avantage par rapport à eval.La mise en cache pourrait accélérer les choses, mais dans la pratique, tous les sites que nous avons sur ce sujet sont déjà incroyablement rapides, donc je ne vois pas la nécessité de procéder à des optimisations de vitesse à ce stade.

Des questions

Pour chacune des choses de cette liste :Est-ce que c'est une bonne idée?Pouvez-vous penser à une meilleure alternative ?

  • toute l'idée en général (balises personnalisées pour html/php)
  • convertir les balises en code php au lieu de les traiter directement
  • l'approche basée sur la pile
  • l'utilisation de eval (ou similaire)

Merci d'avoir lu et TIA pour tout conseil.:)

Était-ce utile?

La solution

Permettez-moi de préconiser une approche différente.Au lieu de générer du code PHP de manière dynamique, puis d'essayer de comprendre comment l'exécuter en toute sécurité, exécutez-le directement lorsque vous rencontrez les balises.Tu peux traiter l'intégralité du bloc HTML en un seul passage et gérer chaque balise au fur et à mesure que vous la rencontrez immédiatement.

Écrivez une boucle qui recherche des balises.Sa structure de base ressemblera à ceci :

  1. Recherchez une balise personnalisée, que vous trouvez à la position n.
  2. Tout avant la position n doit être du HTML simple, donc soit enregistrez-le pour le traitement, soit affichez-le immédiatement (si vous n'avez pas de balises sur votre $tags pile, vous n'avez probablement pas besoin de la sauvegarder nulle part).
  3. Exécutez le code approprié pour la balise.Au lieu de générer du code qui appelle $tags->push, il suffit d'appeler $tags->push directement.
  4. Revenez à l'étape 1.

Avec cette approche, vous appelez uniquement les fonctions PHP directement, vous ne créez jamais de code PHP à la volée pour l'exécuter ensuite.Le besoin de eval est parti.

Vous aurez essentiellement deux cas pour l'étape 3.Lorsque vous rencontrez une balise d'ouverture, vous effectuez une action immédiate push.Puis plus tard, lorsque vous appuyez sur la balise de fermeture, vous pouvez faire un pop puis gérez la balise de la manière appropriée, maintenant que vous avez traité l'intégralité du contenu de l'élément personnalisé.

Il est également plus efficace de traiter le HTML de cette façon.Effectuer plusieurs recherches et remplacements sur une longue chaîne HTML est inefficace car chaque recherche et chaque remplacement est O(n) sur la longueur de la chaîne.Cela signifie que vous analysez la chaîne à plusieurs reprises, et chaque fois que vous effectuez un remplacement, vous devez générer de nouvelles chaînes entières de longueur similaire.Si vous disposez de 20 Ko de HTML, chaque remplacement implique une recherche dans ces 20 Ko, puis la création d'une nouvelle chaîne de 20 Ko.

Autres conseils

J'ai posté une autre réponse car elle est radicalement différente de la première, ce qui pourrait également être utile.

Essentiellement, cette question demande comment exécuter du code PHP avec regex.Cela ne semble peut-être pas si évident, mais c’est ce que l’évaluation vise à accomplir.

Cela dit, au lieu de faire une passe de preg_replace puis de faire une évaluation, vous pouvez simplement utiliser la fonction preg_replace_callback de PHP pour exécuter un morceau de code lorsqu'il correspond.

Voyez ici le fonctionnement de la fonction : http://us.php.net/manual/en/function.preg-replace-callback.php

Oui, au lieu d'eval, vous pouvez faire ce que font Zend et d'autres frameworks majeurs, utiliser la mise en mémoire tampon de sortie :

ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top