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


15

Je lisais cette question sur stackoverflow:

/programming/104516/calling-php-functions-within-heredoc-strings

et la réponse acceptée dit de faire des modèles PHP simples 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> Hello World! </p>';
}

include ('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 le faites include('template.php')(par opposition à l'utilisation de 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 ('Titre de démonstration', '<p> Bonjour tout le monde! </p>');

?>

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

Réponses:


12

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

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

Personnellement, ce que je préfère généralement, c'est d'implémenter une classe ArrayObject avec un tableau de propriétés, et le modèle s'y réfère $this->propertyName, ce qui en fait serait celui de l'objet modèle $this->property['name']. Cela pourrait également être réalisé simplement en utilisant __setet __getdonc:

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.


TinyButStrong fait aussi quelque chose de similaire
Izkata

comment gérez-vous le bouclage sur certains scores? utilisez-vous un modèle de «sous» distinct?
Ryan

Je me demande s'il existe un moyen de se débarrasser de l'utilisation de $ this dans le modèle, par exemple juste $ title ou $ name.
Ryan

1
@Ryan Vous pouvez utiliser extract($this->properties);pour extraire les propriétés en variables, juste avant l'instruction include.
Joyce Babu

1

Un modèle dépendra toujours de variables externes, et avec PHP étant un langage dynamique, il n'y a aucun moyen d'imposer au moment de la compilation qu'elles seront là. La première approche est donc probablement très bien; il reste cependant 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; appeler cela _()semble être une convention commune. 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 permettre de remplacer l' htmlspecialcharsappel, dans les cas où vous souhaitez lui passer du HTML plutôt que des chaînes brutes.
  • Ajoutez une vérification de type pour que les tableaux et les objets ne produisent pas Arrayet 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

Le premier template.phpest 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 le faire, mais il est beaucoup plus susceptible de se briser pendant le processus d'édition .

J'irais avec le premier pour qu'il ressemble à du HTML avec des avantages , comme le préfèrent généralement les concepteurs tiers.


dans quel sens? voulez-vous dire qu'il ne peut pas être modifié par glisser-déposer? textuellement, le second est le même que le premier mais avec quelques lignes de PHP enroulées autour. Donc, en supposant qu'il aura toujours cette structure, il n'est pas plus difficile pour eux de modifier à la main.
Ryan

1
Un bon modèle est logique pour les développeurs, un excellent modèle a du sens pour les concepteurs.
Citricguy

1

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.


-3

Php n'est pas un langage de programmation. L'écriture fortement typée n'est donc pas possible si votre langue n'a pas de véritable système de typage. Mais vous pouvez aider votre IDE comme PHP-Storm à faire le tour que vous voulez ...

Votre définition de vue:

interface mySimpleView { 
  public function getTitle(); 
} 

Votre fichier modèle:

<?php
/**
* @var $this mySimpleView  <== Your IDE will try to use this type.
*/
?>

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.