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


15

Я читав це запитання в режимі stackoverflow:

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

і прийнята відповідь говорить робити звичайні шаблони PHP на зразок цього:

template.php:

<html>
    <head>
        <title><?=$title?> </title>
    </head>
    <ніхто>
        <? = getContent ()?>
    </body>
</html>

index.php:

<? php

$ title = 'Демонстраційний заголовок';

функція getContent () {
    повернути '<p> Привіт, світ! </p>';
}

include ('template.php');

?>

Для мене вищезазначене недостатньо структуровано в тому сенсі, що template.php залежить від змінних, визначених в інших сценаріях. І ви використовуєте include () для виконання коду, коли ви це робите include('template.php')(на відміну від використання include () для включення класу або функції, яка не виконується негайно).

Я вважаю, що кращим підходом є загортання шаблону всередині функції:

template.php:

<? php

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

<html>
    <head>
        <title><?=$title?> </title>
    </head>
    <ніхто>
        <? = $ вміст?>
    </body>
</html>

    <? php повернути ob_get_clean ();
}

?>

index.php:

<? php

need_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, і для мене це добре.


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, а щось значуще (або порожній рядок).
  • Додайте підтримку інтернаціоналізації (ще один аргумент).
  • У режимі налагодження друкуйте назву змінної з деяким спеціальним форматуванням, якщо воно не визначене. Таким чином, ви можете побачити, чи є щось із шаблоном.

1

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

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


в якому сенсі? ти маєш на увазі, що це неможливо змінити за допомогою перетягування? Текстуально другий такий же, як перший, але з декількома лініями PHP, обгорнутими навколо. Тож якщо припустити, що ця структура завжди матиме, не важче їх редагувати вручну.
Райан

1
Хороший шаблон має сенс для розробників, великий шаблон має сенс для дизайнерів.
Citricguy

1

Оскільки Codeigniter - це моя улюблена основа, я пропоную вам рішення Codeigniter:

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();

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


-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 !  -->
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.