web-dev-qa-db-fra.com

Comment structurer un système de template en PHP simple?

Je lisais cette question sur stackoverflow:

https://stackoverflow.com/questions/104516/calling-php-functions-within-heredoc-strings

et la réponse acceptée dit de faire des modèles simples PHP comme ceci:

template.php:

<html> 
 <head> 
 <title><?=$title?> </title> 
 </head> 
 <body> 
 <? = getContent ()?> 
 </body> 
 </html>

index.php:

<? php 
 
 $ title = 'Titre de démonstration'; 
 
 fonction getContent () {
 return '<p> Bonjour tout le monde! </p> '; 
} 
 
 comprennent (' template.php '); 
 
?>

Pour moi, ce qui précède n'est pas bien structuré dans le sens où template.php dépend de variables définies dans d'autres scripts. Et vous utilisez un include () pour exécuter du code lorsque vous faites include('template.php') (par opposition à utiliser include () pour inclure une classe ou une fonction qui n'est pas immédiatement exécutée).

Je pense qu'une meilleure approche consiste à envelopper votre modèle dans une fonction:

template.php:

<? php 
 
 modèle de fonction ($ title, $ content) {
 ob_start (); ?> 
 
 <html> 
 <head> 
 <title><?=$title?> </title> 
 </ head > 
 <body> 
 <? = $ content?> 
 </body> 
 </html> 
 
 < ? php return ob_get_clean (); 
} 
 
?>

index.php:

<? php 
 
 require_once ('template.php'); 
 
 modèle d'impression ('Demo Title', '<p> Hello World! </ p > '); 
 
?>

La deuxième approche est-elle meilleure? Existe-t-il une meilleure façon de procéder?

15
Ryan

Je ne ferais pas la deuxième approche car les paramètres ne sont pas nommés.

Un bien écrit essai sur la description du fonctionnement d'un système de modèles devrait fonctionner provient de Parr, il est souvent cité par des personnes écrivant des systèmes de modèles et/ou des frameworks web-mvc.

Personnellement, je préfère généralement implémenter une classe ArrayObject avec un tableau de propriétés, et le modèle ferait référence à $this->propertyName, Qui serait en fait le $this->property['name'] De l'objet modèle. Cela pourrait également être réalisé simplement en utilisant __set Et __get, Donc:

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

et un modèle ressemblerait à:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

et l'invoquer ressemblerait à:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

Pour autant que je m'en souvienne, c'est à quoi ressemblent les anciens Symfony 1.x et les moteurs de modèles Zend_View, et pour moi ça va.

12
Aadaam

Parce que Codeigniter est mon framework préféré, je vous propose une solution Codeigniter-ish:

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

Votre modèle ressemblerait à:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

Et vous le rendriez en instanciant la classe, en passant des données dans un tableau en tant que paramètre constructeur et en appelant la méthode 'render':

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

De cette façon, vous n'exécutez pas directement le code inclus et vous gardez également votre modèle lisible, donc les concepteurs seront heureux.

2
lortabac

La première template.php est plus facile à charger tel quel dans dreamweaver/bluegriffon/que ce soit et édité par un concepteur Web non averti côté serveur, le second ... eh bien, il pourrait toujours, mais c'est beaucoup plus susceptible de se casser pendant le processus d'édition.

J'irais avec le premier pour qu'il ressemble à HTML avec des avantages, la façon dont les concepteurs tiers préfèrent généralement.

1
ZJR

Un modèle toujours dépendra de variables externes, et avec PHP étant un langage dynamique, il n'y a aucun moyen de faire respecter lors de la compilation qu'elles seront là. la première approche est probablement très bien, mais il y a encore quelques problèmes:

  • L'exemple ne prend pas en charge le codage HTML de la sortie, ce qui signifie que vous avez des vulnérabilités XSS potentielles.
  • Si l'une de ces variables n'est pas définie, le code déclenchera un avertissement; vous ne verrez pas si l'avertissement est logique ou non (peut-être que la variable est facultative).

Une approche très simple consiste à écrire une fonction, de préférence avec un nom très court, qui prend en charge les deux; l'appeler _() semble être une convention courante. Une version simple pourrait ressembler à ceci:

function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}

Et puis dans votre modèle, vous feriez:

<title><?= _($title) ?></title>

Plus de choses à faire avec la fonction _():

  • Donnez-lui un deuxième argument pour autoriser la substitution de l'appel htmlspecialchars, dans les cas où vous souhaitez lui passer du code HTML plutôt que des chaînes brutes.
  • Ajoutez une vérification de type afin que les tableaux et les objets ne produisent pas Array et Object, mais plutôt quelque chose de significatif (ou chaîne vide).
  • Ajoutez le support de l'internationalisation (encore un autre argument).
  • En mode débogage, imprimez le nom de la variable avec une mise en forme spéciale si elle n'est pas définie. De cette façon, vous pouvez voir en un coup d'œil s'il y a un problème avec le modèle.
1
tdammers