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.