Хорошие статьи по шаблонам в PHP

флоппик

promotor fidei
Команда форума
Партнер клуба
Ну, во-первых, верстальщик знает, что такое эскейпинг, потому что он знает HTML, (ведь эскейпинг —сюрприз! именно особенность языка разметки) а самое главное, эскейпинг — это проблема отображения, а не логики. Поэтому, «программист долж ен сам решить что будет заэскейпено» — неправильный подход. Программист вообще может не знать, куда эти данные попадут. Верстальщик — знает.
Во-вторых, $this->var писать не надо.
 

Фанат

oncle terrible
Команда форума
А, да, согласен. Затупил с $var

Но вот насчёт верстальщика ты неправ.
Во-первых, программист не может не знать, куда эти данные попадут.
Во-вторых, не путайте понятия. Здесь ситуация зеркальная: речь идёт не о искейпинге, а наоборот - о не-искейпинге.
При чем здесь "знает, что такое эскейпинг"? Речь не о том, что это такое, а о том, какую переменную искейпить не надо. Речь не о том, куда эти данные попадут, а о том, откуда они взялись. Никакая это не логика отображения.
То, что данная переменная содержит хтмл, введённый доверенным юзером, или профильтрованный через пурифаер юзер инпут - знает как раз только программист
 

Вурдалак

Продвинутый новичок
НЕ НАДО это делать. Ни в твиге, ни в native-php.
Это тебе не надо. Ты нарушаешь инкапсуляцию своим экранированием вне шаблона. Данные могут экранироваться по-разному в зависимости от контекста в шаблоне: обычное HTML-экранирование и JS-экранирование, например.
 

Фанат

oncle terrible
Команда форума
ОПЯТЬ вы путаете экранирование с не-экранированием, е-моё!
Забудьте уже об экранировании! Оно делается АВТОМАТИЧЕСКИ!
Если вы его не делаете автоматически, если у вас верстальщик решает, что экранировать, а что нет - вы сидите по уши в XSS

Само по себе замечание про контекст - хорошее. Надо подумать, как это делать для JS и других вариантов искейпинга.
Но сначала перестаньте путать экранирование с не-экранированием.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Совершенно не важно, что она содержит. Важно, где она будет отображаться. В шаблоне может внезапно оказаться что угодно — xsl, html, xml, xml+rss, csv. И не важно, были ли в этой переменной теги, или нет. Важно — допустимы ли они в этом месте вывода, или нет (по техническим причинам, доверие к данным на самом деле, тоже к этому сводится). Вот это и есть отделение данных и логики от отображения. А если «программист знает» — это смешение их.

Наличие хтмл в переменной имеет к эскейпингу такое же отношение, как и addslashes к обработке параметров запроса в базе.

И разговор банально сводится к тому, что запретительная политика вывода тегов в каком то месте с белым списком надежнее, чем разрешительная — с черным. Вот и все.
 

Фанат

oncle terrible
Команда форума
флоппик!
Заканчивай уже тупить, а? :)
Пойми - у нас зеркальная ситуация. Всё с ног на голову :)
И не важно, были ли в этой переменной теги, или нет.
Всё верно, для искейпинга - неважно.
Теперь инвертируем ситуацию:
В заранее отформатированном тексте HTML есть!
И, следовательно, его наличие становится нам важно. Как раз в ситуации с белым списком нам это становится важно.
 

Sufir

Я не волшебник, я только учусь
Чёрт, вы меня только запутали больше...
PHP:
Good {
  protected $id;
  protected $title;
  protected $price;
  protected $description;

  public function getId() {}
  public function getTitle() {}
  public function getPrice() {}
  public function getDescription() {}

  public static find() {}
}
PHP:
GoodController {
  $id = $request->getId;
  $view->good = Good::find($id);
}
PHP:
<title><?= $this->good->getTitle() ?></title>
<body>
  <div id="good-<?= $this->good->getId() ?>">
    <span><?= $this->good->getTitle() ?></span>
    <span><?= $this->good->getPrice() ?></span>
    <p>
        <?= $this->good->getDescription() ?>
    </p>
  </div>
</body>
Вот конкретный маленьки простой пример. Покажите как это должно выглядеть? Экранирование должно быть в геттерах модели?

Например в ZF предлагается это делать вот так:
PHP:
<p>
    <?= $this->escape($this->good->getDescription()) ?>
</p>
Что, на сколько я понял неправильно... Тогда геттерам модели (это её обязанность?) можно добавить параметр (bool $unescaped = false)?
 

Фанат

oncle terrible
Команда форума
Не, ну в виде одолжения не надо. Хочется же, чтобы понятно было.

Вообще, вопрос "кто должен отменять искейпинг" - не принципиальный. По уму, все эти переменные должны быть перечислены в ТЗ, а кто их маркирует - не так уж и важно.
Но я не согласен с тем, что к не-искейпингу применяются те же подходы, что и к искейпингу. К примеру - "нам не важно, откуда данные взялись." Как раз это-то нам важно. Из белого списка. Мы должны это знать по определению
 

Фанат

oncle terrible
Команда форума
Sufir
Весь мир подходит, наконец, к тому, чтобы $this->escape() руками в шаблоне вообще никогда не делалось.
Точно так же, как не делается сейчас руками addslashes + кавычки для SQL.
Экранирование должно быть в геттерах модели?
Это ты загнул, малость, по-моему.
Экранировать должен вью. Всё подряд.
С этим, вроде бы, все согласны.

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

Sufir

Я не волшебник, я только учусь
Так, вот мне и интересно каким именно образом вью должен экранировать - мне это не понятно. А может тогда и станет ясно, как отменить это при необходимости? И это не я предлагаю учить модель эскейпингу:
Объект, переданный в шаблон почти всегда — модель. Ее надо будет тоже научить автоэскейпингу.
Вот на конкретном приведённом мной примере покажите, как это должно работать.
 

Sufir

Я не волшебник, я только учусь
Неэкранировать, кстати говоря, вполне можно предложенным флоппиком способом:
PHP:
View->row()
Поскольку, как было упомянуто таких данных/мест мало, вполне можно передать дополнительно переменную с не экранированными данными.
PHP:
GoodController {
  $id = $request->getId;
  $view->good = Good::find($id);
  $view->assignRow('unescapedDescription', $good->getDescription());
}
Но как экранировать всегда и все остальные данные? Объекты?
 

Фанат

oncle terrible
Команда форума
Не очень понял, что возвращает find(). Объект класса Good? Но это разве не модель? А почему тогда присваивается переменной $view? Откуда в $view->good берется метод getDescription()?
 

Фанат

oncle terrible
Команда форума
Лично я считаю, что модели делать во вью нечего.
поэтому

PHP:
Good {
  public static find() {}
}
PHP:
GoodController {
  $id = $request->getId;
  $data = Good::find($id);
  $data['link'] = $this->makeUrl($id);
  $data['pic'] = $this->makePic($id);
  $view->setTemplate('good.tpl.php');
  $view->setRaw('description');
  return $data;
}
PHP:
<title><?= $title ?></title>
<body>
  <div id="good-<?= $id ?>">
    <a href=<?=$link?>><img src=<?=$pic?>></a>
    <span><?= $title ?></span>
    <span><?= $price ?></span>
    <p>
        <?=$description ?>
    </p>
  </div>
</body>
 

Absinthe

жожо
Верстальщик — знает.
И может забыть.

Лично я считаю, что модели делать во вью нечего.
Большинство так не считает. Включая главного авторитета(рельсы), с которого все берут пример.

Вот задача. Код вида:
<?= $foo ?>
<?= noescape($bar) ?>

Как ее удобнее решить?
У меня пока нет нормальных идей.
 

Фанат

oncle terrible
Команда форума
Absinthe
Ну, было предложено два варианта
Либо в контроллере
PHP:
$view->setRaw('bar');
либо во вью
PHP:
<?= $bar->raw() ?>
 

Mols

Новичок
Хз.
Как по мне в представление должны идти не экранированные данные.
И это уже дело представления нормльно отображать эти данные.
И не надо думать, что это может быть только ХТМЛ.
Есть туча форматов, которые могут быть востребованы на выходе.
Тот же JSON или YAML да и вобще что угодно (на то он и существует, этот слой отображения).

Ниже пример того как я бы это делал для хтмл.
Все переменные входящие в представление - помещаются во враппер.
Расширить эту штуку для работы с массивами и скалярами, думаю, никому труда не составит.

Ну и вместо текстового сообщения об ошибке, конечно лучше бы пробросить эксепш приличный.

PHP:
class Boom
{

    public $tim = '<tim>';

    public function tom()
    {
        return '<tom> ';
    }

}

class Wrapper
{

    private $_obj;

    public function __construct($obj)
    {
        $this->_obj = $obj;
    }

    public function raw()
    {
        return $this->_obj;
    }

    public function __get($name)
    {
        return isset($this->_obj->$name) ? $this->e($this->_obj->$name) : "Error: unknown property '$name'";
    }

    public function __call($name, $arguments)
    {
        if (method_exists($this->_obj, $name))
            return $this->e($this->_obj->$name());
        return "Error: unknown method '$name'";
    }

    protected function e($v)
    {
        return htmlspecialchars($v);
    }

}

$varWrapper = new Wrapper(new Boom());

echo $varWrapper->tom(), "\t", $varWrapper->tim
 , PHP_EOL
 ,$varWrapper->tomo(), "\t", $varWrapper->timo

, PHP_EOL
, $varWrapper->raw()->tom(), "\t", $varWrapper->raw()->tim;
 

Absinthe

жожо
Фанат определять, нужно ли экранировать, нужно в виде. Мое мнение, т.к. именно вид отвечает за отображение, и в нем мы должны определять, как выводятся данные.
Не указывать же нам, что $book[13]->title должен быть экранированным, а $book[13]->description - нет?
Иначе перед каждой передачей данных мы должны будем сильно обрабатывать структуры данных. С учетом разных выводных форматов(html, json, pdf) это будет еще тот геморрой.

Что касается второго метода, то строка не имеет ->raw(). Что снова нас ведет к зависимости контроллера, в котором нам придется выдавать не просто данные, а специально подготовленные данные для вида. Либо оборачивать строки в какой нибудь String класс, что еще хуже.
 
Сверху