Come strutturare il sistema di template usando PHP semplice?


15

Stavo leggendo questa domanda su StackOverflow:

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

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

template.php:

<Html>
    <Head>
        <Title> <? = $ Title?> </ Title>
    </ Head>
    <Body>
        <? = GetContent ()?>
    </ Body>
</ Html>

index.php:

<? Php

$ title = 'Titolo demo';

funzione getContent () {
    return '<p> Ciao mondo! </p>';
}

include ( 'template.php');

?>

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

Sento che un approccio migliore è avvolgere il modello all'interno di una funzione:

template.php:

<? Php

modello di funzione ($ title, $ content) {
    ob_start (); ?>

<Html>
    <Head>
        <Title> <? = $ Title?> </ Title>
    </ Head>
    <Body>
        <? = $ Contenuti?>
    </ Body>
</ Html>

    <? php return ob_get_clean ();
}

?>

index.php:

<? Php

require_once ( 'template.php');

modello di stampa ('Demo Title', '<p> Hello World! </p>');

?>

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

Risposte:


12

Non farei il secondo approccio poiché i parametri non sono nominati.

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

Personalmente, ciò che di solito preferisco è implementare una classe ArrayObject con un array di proprietà, e il modello farebbe riferimento $this->propertyName, che in realtà sarebbe l'oggetto del modello $this->property['name']. Questo potrebbe essere ottenuto anche semplicemente usando __sete __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 sarebbe simile a:

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

e invocandolo sarebbe simile a:

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

Per quanto ricordo, ecco come appaiono i vecchi motori di Symfony 1.xe i template Zend_View, e per me va bene.


Anche TinyButStrong fa qualcosa di simile
Izkata il

come gestisci il looping su alcuni spartiti? usi un modello "sub" separato?
Ryan,

mi chiedo se c'è un modo per sbarazzarsi di usare $ questo nel modello, ad esempio solo $ title o $ name.
Ryan,

1
@Ryan È possibile utilizzare extract($this->properties);per estrarre le proprietà in variabili, proprio prima dell'istruzione include.
Joyce Babu,

1

Un modello dipenderà sempre da variabili esterne e con PHP un linguaggio dinamico, non c'è modo di imporre al momento della compilazione che saranno presenti. Quindi il primo approccio probabilmente va bene; ci sono ancora alcuni problemi:

  • 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 genererà un avviso; non vedrai se l'avviso ha un senso o meno (forse la variabile è facoltativa).

Un approccio molto semplice è quello di scrivere una funzione, preferibilmente con un nome molto breve, che si occupi di entrambi; chiamarlo _()sembra essere una convenzione comune. Una versione semplice potrebbe apparire così:

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:

  • Dagli un secondo argomento per consentire l'override della htmlspecialcharschiamata, per i casi in cui desideri passarci l'HTML anziché le stringhe non elaborate.
  • Aggiungi la verifica del tipo in modo che array e oggetti non cedano Arraye Object, piuttosto, qualcosa di significativo (o stringa vuota).
  • Aggiungi il supporto per l'internazionalizzazione (ancora un altro argomento).
  • In modalità 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.

1

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

Vorrei andare con il primo per mantenerlo simile all'HTML con vantaggi , come preferiscono i designer di terze parti.


in che senso? vuoi dire che non può essere modificato usando il trascinamento della selezione? testualmente il secondo è lo stesso del primo ma con un paio di righe di PHP avvolte. Quindi supponendo che avrà sempre quella struttura, non è più difficile per loro modificarli a mano.
Ryan,

1
Un buon modello ha senso per gli sviluppatori, un grande modello ha senso per i progettisti.
Citricguy,

1

Poiché 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 sarebbe simile a:

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

E lo renderizzeresti istanziando la classe, passando i dati in un array come parametro del 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 esegui direttamente il codice incluso e mantieni anche il tuo modello leggibile, quindi i progettisti saranno felici.


-3

Php non è un linguaggio di programmazione. Quindi scrivere in modo fortemente tipizzato non è possibile se la tua lingua non ha un sistema di tipi reale. Ma puoi aiutare il tuo IDE come PHP-Storm a fare il trucco che desideri ...

La definizione della vista:

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 !  -->

Mi piace questa risposta (anche se chiaramente "Php non è un linguaggio di programmazione" sarà sempre controverso.
Ronnie,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.