Как структурировать систему шаблонов с использованием простого PHP?

15

Я читал этот вопрос на stackoverflow:

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

и принятый ответ говорит делать простые шаблоны PHP, как это:

template.php:

<HTML>
    <Голова>
        <Title> <? = $ Название?> </ Title>
    </ HEAD>
    <Тело>
        <? = GetContent ()?>
    </ Body>
</ Html>

index.php:

<? PHP

$ title = 'Demo Title';

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

включают в себя ( 'template.php');

?>

Для меня вышеизложенное не очень хорошо структурировано в том смысле, что template.php зависит от переменных, которые определены в других скриптах. И вы используете include () для выполнения кода, когда вы это делаете include('template.php')(в отличие от использования include () для включения класса или функции, которая выполняется не сразу).

Я чувствую, что лучший подход - обернуть ваш шаблон внутри функции:

template.php:

<? PHP

шаблон функции ($ title, $ content) {
    ob_start (); ?>

<HTML>
    <Голова>
        <Title> <? = $ Название?> </ Title>
    </ HEAD>
    <Тело>
        <? = $ Содержание?>
    </ Body>
</ Html>

    <? php return ob_get_clean ();
}

?>

index.php:

<? PHP

require_once ( 'template.php');

шаблон печати ('Demo Title', '<p> Hello World! </ p>');

?>

Второй подход лучше? Есть ли еще лучший способ сделать это?

Райан
источник

Ответы:

12

Я бы не стал использовать второй подход, так как параметры не названы.

Parr написал хорошо написанное эссе о том, как должна работать система шаблонов , его часто цитируют люди, пишущие системы шаблонов и / или фреймворки web-mvc.

Лично я обычно предпочитаю реализовывать класс ArrayObject с массивом свойств и ссылаться на шаблон $this->propertyName, который фактически будет объектом шаблона $this->property['name']. Этого также можно достичь, просто используя __setи __get, так:

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

и шаблон будет выглядеть так:

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

и вызов его будет выглядеть так:

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

Насколько я помню, так выглядят старые движки шаблонов Symfony 1.x и Zend_View, и для меня это нормально.

Aadaam
источник
TinyButStrong также делает нечто подобное
Изката
Как вы справляетесь с зацикливанием на некоторых счетах? Вы используете отдельный «суб» шаблон?
Райан
Интересно, есть ли способ избавиться от использования $ this в шаблоне, например, просто $ title или $ name.
Райан
1
@Ryan Вы можете использовать extract($this->properties);для извлечения свойств в переменные непосредственно перед оператором include.
Джойс Бабу
1

Шаблон всегда будет зависеть от внешних переменных, а с учетом того, что PHP является динамическим языком, во время компиляции невозможно принудительно установить, что они будут там. Таким образом, первый подход, вероятно, хорошо; Есть еще несколько проблем:

  • Пример не заботится о HTML-кодировании вывода, что означает, что у вас есть потенциальные уязвимости XSS.
  • Если одна из этих переменных не установлена, код выдаст предупреждение; Вы не увидите, имеет ли предупреждение смысл или нет (возможно, переменная является необязательной).

Очень простой подход - написать функцию, желательно с очень коротким именем, которая позаботится об этом; называть это, _()кажется, является общим соглашением. Простая версия может выглядеть так:

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

И тогда в вашем шаблоне вы сделаете:

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

Больше вещей, которые можно сделать с помощью _()функции:

  • Дайте ему второй аргумент, позволяющий переопределить htmlspecialcharsвызов, для случаев, когда вы хотите передать в него HTML, а не необработанные строки.
  • Добавьте проверку типов, чтобы массивы и объекты не выдавали Arrayи Object, а, скорее, что-то значимое (или пустую строку).
  • Добавить поддержку интернационализации (еще один аргумент).
  • В режиме отладки выведите имя переменной с некоторым специальным форматированием, если оно не определено. Таким образом, вы можете мельком увидеть, есть ли что-то не так с шаблоном.
tdammers
источник
1

Первый template.phpлегче загрузить как есть в dreamweaver / bluegriffon / что угодно и отредактированный неопытным веб-дизайнером на стороне сервера, второй ... ну, он все еще может, но с большей вероятностью сломается в процессе редактирования ,

Я бы пошел первым, чтобы он выглядел как HTML с преимуществами , как обычно предпочитают сторонние дизайнеры.

Zjr
источник
в каком смысле? Вы имеете в виду, что это не может быть изменено с помощью перетаскивания? второй текст - тот же, что и первый, но с несколькими строчками PHP. Таким образом, предполагая, что у него всегда будет такая структура, им не сложнее редактировать вручную.
Райан
1
Хороший шаблон имеет смысл для разработчиков, отличный шаблон имеет смысл для дизайнеров.
Citricguy
1

Поскольку Codeigniter - моя любимая среда, я предлагаю вам решение 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
        }
    }       
}

Ваш шаблон будет выглядеть так:

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

И вы бы отобразили его, создав экземпляр класса, передав данные в массиве в качестве параметра конструктора и вызвав метод render:

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

Таким образом, вы не выполняете напрямую включенный код, а также сохраняете свой шаблон читабельным, поэтому дизайнеры будут счастливы.

lortabac
источник
-3

Php не является языком программирования. Таким образом, написание строго типизированного невозможно, если ваш язык не имеет реальной системы типов. Но вы можете помочь вашей IDE, такой как PHP-Storm, сделать то, что вам нужно ...

Ваше мнение определение:

interface mySimpleView { 
  public function getTitle(); 
} 

Ваш файл шаблона:

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

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->
Роджер Кёлен
источник