Come strutturare il sistema template usando PHP semplice?

12

Stavo leggendo questa domanda su StackOverflow:

link

e la risposta accettata dice di fare semplici modelli PHP come questo:

template.php:

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

index.php:

<?php

$title = 'Demo Title';

function getContent() {
    return '<p>Hello World!</p>';
}

include('template.php');

?>

Per quanto sopra, non è ben strutturato nel senso che template.php dipende da variabili definite in altri script. E stai usando un include () per eseguire codice quando fai include('template.php') (invece di usare include () per includere una classe o una funzione che non viene immediatamente eseguita).

Credo che un approccio migliore sia quello di avvolgere il tuo modello all'interno di una funzione:

template.php:

<?php

function template($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');

print template('Demo Title', '<p>Hello World!</p>');

?>

Il secondo approccio è migliore? C'è un modo ancora migliore per farlo?

    
posta Ryan 04.08.2012 - 22:21
fonte

5 risposte

10

Non farei il secondo approccio in quanto i parametri non sono nominati.

Un ben scritto saggio sulla descrizione di come un sistema di template dovrebbe funzionare è provenendo da Parr, viene spesso citato da persone che scrivono sistemi di template e / o framework web-mvc.

Personalmente, quello che di solito preferisco è implementare una classe ArrayObject con una matrice di proprietà e il modello farebbe riferimento a $ this- > propertyName, che in effetti sarebbe il nome $ this- > proprietà dell'oggetto ['nome dell'oggetto ']. Questo potrebbe anche essere ottenuto semplicemente usando __set e __get, quindi:

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];
  }
}

e un modello assomiglia a:

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

e invocandolo sembrerebbe:

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

Per quanto mi ricordi, questo è il modo in cui appaiono i vecchi template engine Symfony 1.x e Zend_View, e per me va bene.

    
risposta data 04.08.2012 - 23:30
fonte
1

Un modello sempre dipende da variabili esterne e, essendo PHP un linguaggio dinamico, non c'è modo di far rispettare in fase di compilazione che saranno lì. Quindi il primo approccio è probabilmente corretto; ci sono ancora alcuni problemi però:

  • L'esempio non si occupa della codifica HTML dell'output, il che significa che hai potenziali vulnerabilità XSS.
  • Se una di queste variabili non è impostata, il codice genera un avviso; non vedrai se l'avviso ha senso o no (forse la variabile è facoltativa).

Un approccio molto semplice è quello di scrivere una funzione, preferibilmente con un nome molto breve, che si prende cura di entrambi; chiamarlo _() sembra essere una convenzione comune. Una versione semplice potrebbe essere simile a questa:

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

E poi nel tuo modello, dovresti fare:

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

Altre cose da fare con la funzione _() :

  • Assegna un secondo argomento per consentire l'override della chiamata htmlspecialchars , per i casi in cui desideri passare l'HTML anziché le stringhe non elaborate.
  • Aggiungi controllo tipo in modo che gli array e gli oggetti non generino Array e Object , ma piuttosto qualcosa di significativo (o stringa vuota).
  • Aggiungi supporto per l'internazionalizzazione (ancora un altro argomento).
  • In modalità di debug, stampa il nome della variabile con una formattazione speciale se non è definita. In questo modo, puoi vedere a colpo d'occhio se c'è qualcosa di sbagliato nel modello.
risposta data 05.08.2012 - 11:49
fonte
1

Il primo template.php è più facile da caricare così com'è in dreamweaver / bluegriffon / qualunque e modificato da un web designer non-savvy sul lato server, il secondo ... beh, è ancora un po ' potrebbe, ma è molto più probabile che si interrompa durante il processo di modifica.

Vorrei andare con il primo a mantenere l'aspetto HTML con vantaggi , come di solito preferiscono i progettisti di terze parti.

    
risposta data 05.08.2012 - 01:44
fonte
1

Dato che Codeigniter è il mio framework preferito, ti propongo una soluzione 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
        }
    }       
}

Il tuo modello dovrebbe essere:

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

E lo renderesti istanziando la classe, passando i dati in una matrice come parametro costruttore e chiamando il metodo 'render':

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

In questo modo non stai eseguendo direttamente il codice incluso, e stai anche mantenendo il tuo modello leggibile, così i designer saranno felici.

    
risposta data 26.08.2012 - 15:43
fonte
-4

Php non è un linguaggio di programmazione. Quindi scrivere strongmente non è possibile se la tua lingua non ha un vero sistema di tipo. Ma puoi aiutare il tuo IDE come PHP-Storm a fare il trucco che vuoi ...

La tua definizione di visualizzazione:

interface mySimpleView { 
  public function getTitle(); 
} 

Il tuo file modello:

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

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->
    
risposta data 21.05.2016 - 13:31
fonte

Leggi altre domande sui tag