Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в ответ несколько комментов про шаблонизаторы и задумался. Я знаю про шаблонизаторы, но есть много проектов, в которых они не используются, которые написаны на самописных движках, на CMS, либо на фреймворках, в которых нет шаблонизатора по умолчанию. Эти проекты продолжают развиваться и требуют писать код.
В этой статье я хочу изложить некоторые аргументы за то, что такой оператор будет полезен. И, возможно, получить обоснованные аргументы против.


Я предлагаю оператор <?~ $value ?> как короткую запись для <?= htmlspecialchars($value, ENT_QUOTES) ?>. Это позволит уменьшить копи-пасту вызовов и улучшить безопасность приложений без движков шаблонизации, так как в таких приложениях это частая операция. Обычно можно перевести приложение на новую версию языка программирования, но практически невозможно переписать все имеющиеся PHP-шаблоны на специальный шаблонизатор. Вариант сделать функцию <?= h($value) ?> это не выход, так как копи-паста все равно остается, и можно когда-нибудь забыть ее вызвать.

Поискав на bugs.php.net, я нашел один активный feature request с 2012 года. Там в комментариях была предложена форма записи <?~ ?>, которая показалась мне удобной. Написав там примерно то же самое, что и в статье про PHP7, получил ответ, что это потребует процесса RFC.

Я пообщался с разработчиками в рассылке PHP Internals list и получил ответ, что это уже обсуждалось много раз.

Ссылки:
marc.info/?t=145851323800001
marc.info/?t=135082660600002
marc.info/?t=144225546000001
wiki.php.net/rfc/escaper
wiki.php.net/rfc/taint
bugs.php.net/bug.php?id=62574
bugs.php.net/bug.php?id=20310
bugs.php.net/bug.php?id=16007
bugs.php.net/bug.php?id=3284

Основной аргумент в том, что экранирование контекстно-зависимо. У нас есть разные контексты — HTML/URL/JS/CSS — и делать какой-то оператор только для одного контекста неправильно, а для всех сразу сложно.

На самом деле это немного не так. Это не взаимоисключающие контексты, HTML может присутствовать или нет независимо от остальных. Кроме того, для JS и CSS контекста нельзя применить понятие «экранирование» (escaping), потому что это другие языки со своим синтаксисом. Правильная запись для них не сводится к добавлению слэшей и замене спец-символов.

Рассмотрим пример.
<a href="/things/<?= $thing['name'] ?>" onclick="alert('<?= $thing['name'] ?>');">
    <?= $thing['name'] ?>
</a>

На первый взгляд может показаться, что тут везде нужно разное экранирование. Но это не так. Вызов htmlspecialchars() нужен во всех трех случаях.

<?php $thing = ['name' => 'Say "Hello")']; ?>

<a
    href="/things/<?= htmlspecialchars(urlencode($thing['name'])) ?>"
    onclick="alert(<?= htmlspecialchars(json_encode($thing['name']), ENT_QUOTES) ?>);"
>
    <?= htmlspecialchars($thing['name']) ?>
</a>


Несмотря на то, что urlencode() возвращает безопасную для HTML строку, и можно не использовать htmlspecialchars(), сочетание htmlspecialchars() + urlencode() встречается в обработке различных фильтров.

Пример:
<?php
    $postData = ['contains_text' => 'Say "Hello")'];

    $filterUrl = '/my_route/?state=active';
    if ($postData['contains_text']) $filterUrl .= '&' . 'contains_text=' . urlencode($postData['contains_text']);
    $pageNumber = 1;
?>

<a
    href="<?= htmlspecialchars($filterUrl) ?>"
    onclick="alert(<?= htmlspecialchars(json_encode($postData['contains_text']), ENT_QUOTES) ?>);"
>
    <?= $pageNumber ?>
</a>


При обработке веб-страниц htmlspecialchars() не нужен только внутри тегов style и script.

1  HTML        Содержимое тега, любые атрибуты тега (включает следующие 3 варианта)
2  HTML + URL  Атрибуты href, action, различные варианты типа data-url
3  HTML + JS   Атрибуты on-event - onclick, onkeypress и т.д.
4  HTML + CSS  Атрибут style
5  URL         -
6  JS          Тег <script></script>
7  CSS         Тег <style></style>
8  non-HTML    Экранирование может зависеть не только от формата, но и от задачи.


Пункт 5 — отдельно в HTML-документе не встречается, только в составе других контекстов.
Пункт 6 — встречается довольно часто, это единственный практический случай, когда нам не надо применять html-экранирование. Но такая связка PHP+JS считается не очень хорошим стилем, лучше использовать data-атрибуты, особенно в мультиязычных приложениях. А для них нужен htmlspecialchars().
Пункт 7 — очень редкий случай, обычно PHP там не используется.
Пункт 8 — для этого случая безопасное экранирование принципиально невозможно придумать заранее.

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

Оператор <?~ ?> не требует настроек в «php.ini» и не влияет на остальные теги и операторы. Его удобно набирать, все символы набираются с Shift, и меньше вероятность написать <?= ?> вместо него, так как тильда находится с другой стороны клавиатуры. Если вы по каким-то причинам не используете htmlspecialchars(), то для вас ничего не поменяется. Он не является универсальным оператором для экранирования любых данных, это просто короткая запись для вызова <?php echo htmlspecialchars($str, ENT_QUOTES) ?>, так же как <?= ?> для <?php echo $str; ?>

Внимание. Если вы пишете приложения, к примеру, только на Twig и Symfony, или последние N лет делаете только API, то большая просьба не голосовать, а то получится нерепрезентативная выборка. Для вас сделан третий опрос. В первых двух интересует мнение людей, которые реально с этим работают.

Аргументированные комментарии за и против приветствуются.
Насколько часто вы работаете с проектами с рендерингом шаблонов на PHP, в которых не используются шаблонизаторы?

Проголосовало 505 человек. Воздержался 121 человек.

Как вы считаете, такой оператор был бы полезен?

Проголосовало 502 человека. Воздержалось 132 человека.

Я не использую рендеринг шаблонов на PHP…

Проголосовало 304 человека. Воздержалось 264 человека.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться с друзьями
-->

Комментарии (125)


  1. ertaquo
    27.06.2016 08:52
    -3

    А как вам вариант <?== $q ?>? Более привычно выглядит, и где-то уже видел подобное использование в шаблонах.


    1. affka
      27.06.2016 09:11
      +1

      Я бы предложил тогда <?- $q ?> по аналогии с underscore (<%-… %> для экранирования и <%=… %> без экранирования)


    1. michael_vostrikov
      27.06.2016 09:19
      +5

      В feature request человек, который его предложил, объясняет это так:

      the reason is that ~ is often located near the 'ESC' on the keyboard, so it feels more like escape

      Ну и набирать удобнее, и перепутать сложнее.


      1. LionAlex
        27.06.2016 09:29
        -1

        А что будет, когда он клаву поменяет? :)


        1. XXXXPro
          27.06.2016 22:58

          А что, есть клавиатуры, где ~ не рядом с Esc?


          1. symbix
            27.06.2016 23:16
            +2

            Конечно, стандартная international qwerty — между левым шифтом и Z.


          1. LionAlex
            28.06.2016 09:50
            +1

            Конечно, любая маковская, например


    1. denis_g
      27.06.2016 23:15

      <?==8 $q ?>. Простите ;)


  1. impwx
    27.06.2016 10:01
    +6

    Вариант сделать функцию <?= h($value) ?> это не выход, так как копи-паста все равно остается, и можно когда-нибудь забыть ее вызвать.
    А что мешает забыть поставить тильду?


    1. michael_vostrikov
      27.06.2016 10:07
      +2

      Оператор без тильды сейчас не используется. Написать случайно <?= ?> сложнее, потому что надо символы по-другому набирать. Скопировать <?= ?> из другого места можно, но если этот оператор будет применяться всегда при выводе в HTML, то и эта проблема решится. Проблема с функцией в том, что можно вывести без функции, а без оператора вывести ничего не получится.


  1. XaveScor
    27.06.2016 10:03
    +2

    А далее напишите свою функцию __($val).
    Зачем засорять язык?


    1. michael_vostrikov
      27.06.2016 10:03
      +8

      Написание функции с коротким именем не решает проблему копи-пасты или невнимательности. Кроме того, дело не в копи-пасте как таковой, а в том, что это очень частая операция. 90% выводимых данных — это данные из БД, и только иногда нам надо вывести готовый HTML-контент. Ну и просто это логично, что в языке для веб-программирования должен быть оператор безопасного вывода данных в HTML-окружение.


      1. Evengard
        27.06.2016 14:44

        XaveScor предлагает андерскор, чтобы получилось <?_($val);?> что уже очень близко к вашему варианту, только с другим знаком.


        1. Evengard
          27.06.2016 14:54

          https://eval.in/596367 собственно вот вам и пример — вместо тильды — андерскор, а итог тот же что вы и хотели. Новая синтаксическая конструкция, не правда ли?


          1. michael_vostrikov
            27.06.2016 15:30
            +1

            Ага, только еще скобки, и короткий оператор <? ?> надо включать.


  1. Methos
    27.06.2016 10:07
    -5

    MVC


    1. lifestyle
      27.06.2016 18:35
      +3

      FBI


  1. dim_s
    27.06.2016 10:18
    +2

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


    1. michael_vostrikov
      27.06.2016 10:48
      +5

      Аргументируйте пожалуйста. Я считаю, что это не бесполезный хлам, это нужная и часто используемая операция. Один оператор не превратит PHP в хороший шаблонизатор и не заставит исчезнуть другие шаблонизаторы, зато поможет в разработке тех проектов, где шаблонизатора нет. Кроме того, такой аргумент можно было бы рассмотреть, если бы в PHP не добавлялись новые операторы для уже существующих действий — ?? или <=>. Много ли людей пишут свою сортировку на PHP? А данные из БД выводят практически все.


      1. dim_s
        27.06.2016 10:59
        -2

        Внедрение ?? и <=> тоже было сомнительным, но в меньшей степени. Как уже подсказали выше, достаточно завести короткую функцию вместо htmlspecialchars(). Выходит так, у нас есть проблема — длинная и неудобная функция htmlspecialchars(), ага, значит надо решать ее путем внедрения в язык новой синтаксической конструкции, а сделать синоним из одной буквы той же функции для нас проблема, но это же на несколько символом длиннее…

        P.S. Все это выглядит как из пушки по воробьям. Желание многих разработчиков решить свои проблемы путем «исправления» языка вызывает у меня лишь негодование.


        1. michael_vostrikov
          27.06.2016 11:47
          +3

          Проблема не в длине названия функции, а в том, что есть возможность написать без нее, специально или по невнимательности, что бывает довольно часто. И приходится постоянно держать в голове, что я не могу просто воспользоваться оператором <?= ?>, а надо туда еще что-то дописывать, причем дописывать всегда одно и то же.


          1. dim_s
            27.06.2016 13:05

            В PHP по невнимательности можно сделать много ошибок, а еще в голове надо постоянно держать порядок аргументов для функций. А то что надо постоянно держать в голове, что надо писать <?~ вместо <?= и можно случайно ошибиться… Это не аргументы, многие разработчики могут быть со мной несогласны, но если каждый будет решать свои проблемы таким радикальным способом, в скором времени php превратится в франкенштейна с кучей возможностей, как perl например. Но зачем нам php тогда, если есть уже perl.


            1. michael_vostrikov
              27.06.2016 13:27
              +2

              если каждый будет решать свои проблемы

              Это не какая-то моя локальная проблема, это операция, которая часто встречается во многих проектах. И чтобы каждый не решал таким способом свои проблемы, существует процесс RFC. Данная статья нужна для того, чтобы решить, стоит ли его начинать, узнать мнение сообщества, аргументы за и против. Потому что могут быть причины, с которыми я не сталкивался и о которых не подумал. Пока что все аргументы против сводятся к вариантам «мне не нравится странный синтаксис» и «используйте шаблонизаторы, PHP уже не шаблонизатор», либо к контекстам экранирования. По поводу шаблонизаторов и контекста я и написал в статье, и пока что нет аргументов в пользу того, что я не прав.


              1. dim_s
                27.06.2016 15:11
                +1

                Если такая фича вызывает бурные обсуждения, как сторонников, так и противников, это означает что фича далеко не однозначна и маловероятно будет принята сообществом.


                1. michael_vostrikov
                  27.06.2016 15:16

                  Если есть неоднозначность, значит должны быть обоснованные аргументы. «Мне не нравится новый синтаксис» не аргумент в технической дискуссии.


                  1. dim_s
                    27.06.2016 16:06

                    Это ваше субъективное мнение, что они необоснованные. Они вполне обоснованные.


                    1. michael_vostrikov
                      27.06.2016 18:07

                      Ваше обоснование в том, что можно сделать специальную функцию. Но проблема не в том, что у нас нет функции, а в том, что часто повторяется одно и то же действие, а если случайно его не повторить, то это приводит к проблемам в безопасности.

                      Но проблема с функцией немного шире. Как обеспечить глобальную доступность экранирующей функции в каждом файле представления? Делать дополнительный include_once / require_once при каждом рендеринге шаблона? Подключать один раз при старте приложения, независимо от того, будем мы рендерить HTML или нет? Автозагрузки функций в PHP еще нет, это пока только на стадии RFC.


    1. DjOnline
      27.06.2016 11:49
      +1

      Многие считают ваши шаблонизаторы, построенные на базе языка шаблонизатора, хламом.


      1. Fedcomp
        27.06.2016 18:45
        +3

        Фанаты битрикса наверное?


  1. bentall
    27.06.2016 10:41
    +2

    Не всё из наследия Расмуса хорошо для PHP, многое, наоборот, очень-очень плохо, но, так или иначе, он начинал писать язык как шаблонизатор. Приделывать к шаблонизатору шаюлонизатор можно, и такое повышение уровня косвенномти даже решит какие-то проблемы, но руки сами тянутся к бритве Оккама.

    Логичное и простое развитие языка в стиле «чтобы стоять на ногах, мне надо держаться корней» с неплохим потенциалом в плане повышения безопасности при написании простых приложений.


  1. KlimovDm
    27.06.2016 11:04
    +3

    Тильда (~) у меня вызывает стойкую ассоциацию с обработкой регулярок (vcl, perl, nginx conf etc.) Безотносительно полезности такой конструкции не думаю, что сам выбор знака тильды удачен.


    1. VolCh
      28.06.2016 11:18
      +1

      Ну, в какой-то мере предложенный <?~ можно рассматривать как замену в аргументе по регулярному выражению.


    1. JTG
      28.06.2016 11:30

      А underscor'овая конструкция у меня ассоциируется с вырезанием пробелов слева от блока {%-… %} в jinja2/twig.


  1. ilyaplot
    27.06.2016 11:07
    -3

    Постоянно пишу шаблоны на чистом php, но никогда не сталкивался с проблемой копипасты. Что мешает готовить экранированные данные со стороны бэкэнда? Если этот оператор действительно нужен, можете написать extension для PHP и выложить в паблик.


    1. zelenin
      27.06.2016 11:23
      +5

      бэкэндом может выступать все что угодно, в частности чужой апи. У бэкэнда может быть много фронтэндов с разными задачами — бэкэнд должен отдавать raw-данные, фронтэнд должен эти данные представить в том виде, в котором ему нужно. Бэкэнд ничего о фронтэнде не должен знать.


      1. ilyaplot
        28.06.2016 12:02

        Да, согласен, бэкэнд не должен экранировать данные. Просто меня ни разу не напрягало писать что то вроде Html::encode($var), потому предложил, совершенно не думая, такое решение.


  1. OnYourLips
    27.06.2016 12:39

    Оператор специально для того, чтобы плодить XSS-уязвимости?

    Я так и не понял, какую проблему он решает. Почему он чем-то отличается от использования хелпер-функции?
    При этом такой оператор не решит проблему случайного отсутствия экранирования. В результате выйдет XSS.
    Я категорически против введения такого уродства в мой любимый язык: он вводит лишь новые проблемы.


    1. michael_vostrikov
      27.06.2016 12:50
      +2

      Приведите пример, пожалуйста


      1. OnYourLips
        27.06.2016 13:43
        +2

        Прмер хелпер-функции?
        <?= e($anyString) ?>
        Отличий от предложенного варианта с <?~ ?> нет, но сущности не плодятся.

        Пример ошибки с XSS?
        <?= $anyString ?>
        Тут вместо <?~ ?> использовали <?= ?> из-за невнимательности, вызванной привычным использованием <?= ?>, шаблонизаторы же такой проблемы лишены, и в них нужно задумываться не для экранирования, а для отмены экранирования.
        Хочу заметить, что с шаблонами работают фронтендеры, и их навыки в PHP минимальны. Поэтому вероятность ошибки у них будет выше.

        И самое главное: откуда такой тег будет узнавать контекст для экранирования?
        Или мы заведём <?~ ?> для HTML вне тегов, <?# ?> для строк в json, <?@#$*&^% ?> для LIKE выражений в SQL и т.д.?


        1. michael_vostrikov
          27.06.2016 14:36
          -2

          <?= e($anyString) ?>
          Отличий от предложенного варианта нет, но сущности не плодятся.

          Плодится копи-паста. И если случайно убрать символ «e», то выведутся небезопасные данные. А если убрать специальный оператор, то не выведется ничего.

          Пример ошибки с XSS?
          <?= $anyString ?>
          Тут вместо <?~ ?> использовали <?= ?> из-за невнимательности, вызванной привычным использованием <?= ?>

          Ну так сейчас только так и выводится. Небезопасный вывод не должен быть привычным использованием. В 90% случаев данные нужно экранировать для HTML, поэтому с новым оператором привычным будет его использование.

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

          То есть надо каждому новому фронтендеру объяснять, что у нас есть специальная функция e(), которую надо вызывать всегда и везде, и что им надо забыть про функциию h(), которая была у них на предыдущем проекте? Кроме того, я имею в виду в первую очередь уже существующие проекты, в которых уже не используются шаблонизаторы. С тем, что шаблонизаторы это хорошо, никто и не спорит.

          Или мы заведём <?~ ?> для HTML вне тегов, <?# ?> для строк в json, <?@#$*&^% ?> для LIKE выражений в SQL и т.д.

          Нет. Это оператор специально для HTML-контекста. Потому что это самая частая операция, и она применяется совместно с другими контекстами. И вывод данных из PHP напрямую в JS это не настолько частая операция, как вывод данных в HTML-контекст. С LIKE-выражениями вполне справляются движки для работы с БД, они используются везде, в отличие от шаблонизаторов.


          1. OnYourLips
            27.06.2016 18:37
            +1

            > И если случайно убрать символ «e», то выведутся небезопасные данные
            И если заменить "~" на "=", то тоже выведутся небезопасные данные. Ваше предложение ничего не меняет, но плодит сущности.

            > Ну так сейчас только так и выводится.
            Это неправда. Вы ведь слышали про twig? Чтобы отключить автоэкранирование, нужно добавить "|raw".
            Этот способ в бесконечное количество раз лучше того, который предложили вы.

            > То есть надо каждому новому фронтендеру объяснять, что у нас есть специальная функция e()
            А как без этого?
            В каждом проекте свой подход к работе с шаблонами, и объяснять его придётся в 100% случаев.
            И оба эти способа без автоматического экранирования плохие. А twig хороший. Возлюби twig!

            > Потому что это самая частая операция
            Последний раз я <?= ?> встречал три года назад, работая с легаси-проектом.

            > Это оператор специально для HTML-контекста
            А давайте введём register_globals обратно, объявим неймспейсы и классы deprecated?
            Ведь эти старонововведения позволят упростить работу с неструктурированным спагетти-кодом.


            1. michael_vostrikov
              27.06.2016 18:59

              И если заменить "~" на "="

              Дело не в том, что и где можно заменить. а в том, что часто повторяется одно и то же действие, а если случайно его не повторить, то это приводит к проблемам в безопасности.

              Этот способ в бесконечное количество раз лучше того, который предложили вы.

              Предложите пожалуйста такой же бесконечно простой способ перевести 100500 работающих шаблонов в большом внутреннем проекте компании с PHP на Twig.

              И оба эти способа без автоматического экранирования плохие

              <?~ ?> и есть способ с автоматическим экранированием.

              Последний раз я <?= ?> встречал три года назад, работая с легаси-проектом.

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

              А давайте введём register_globals обратно, объявим неймспейсы и классы deprecated

              Не вижу связи. Для введения неймспейсов и других нововведений были свои причины. Причины для появления такого оператора я изложил в статье. Он не уменьшает безопасность и не увеличивает длину названий в коде, как раз наоборот.


              1. OnYourLips
                27.06.2016 20:48

                > а если случайно его не повторить, то это приводит к проблемам в безопасности.
                Именно поэтому я против <?~ ?>, потому что его легко перепутать с <?= ?>
                И именно поэтому нужно использовать систему, где автоэкранирование будет по уполчению. Например, twig.

                > Предложите пожалуйста такой же бесконечно простой способ перевести 100500 работающих шаблонов в большом внутреннем проекте компании с PHP на Twig.
                Элементарно. Ставится директива: все новые шаблоны и исправление старых — толькона twig.
                Подкрепляете директиву хуком на гит, чтобы не нарушали.

                > и есть способ с автоматическим экранированием.
                Это не будет работать: если кто-то использует "=" вместо "~" по ошибке, то автоэкранирования не будет.

                > которые реально работают с проектами без шаблонизаторов.
                Почему тогда просто не использовать нормальный качественный шаблонизатор? Производительность будет на уровне, если не выше (ob_* функции не быстрые), а компиляции шаблонов (основной тормоз шаблонизаторов) в продакшене все равно не будет.

                > Не вижу связи.
                А я вижу. Вы предлагаете ввести средство, которое пригодится лишь в коде плохого качества.

                > Он не уменьшает безопасность
                Уменьшает. Я ДВАЖДЫ обосновал, почему.

                > и не увеличивает длину названий в коде
                Это не важно. Однако и twig не увеличивает.


                1. michael_vostrikov
                  27.06.2016 21:27

                  а если случайно его не повторить, то это приводит к проблемам в безопасности.

                  Именно поэтому я против <?~ ?>

                  Это взаимоисключающие операторы, можно написать либо <?~ ?>, либо <?= ?>, иначе не будет работать.
                  А с отдельной функцией это совместное использование, можно написать начало <?=, а потом либо продолжить, либо нет.
                  Я вообще не очень понимаю вашу логику. Введение специального оператора для экранирования в каком-либо контексте как минимум не уменьшит безопасность, потому что это не влияет на старый код и старые операторы.

                  Это не будет работать: если кто-то использует "=" вместо "~" по ошибке, то автоэкранирования не будет.

                  А если кто-то в Twig по ошибке скопипастит длинный оператор с | raw в конце, то тоже экранирования не будет.
                  От ошибок никто не застрахован, это не аргумент. Я упомянул про это не в качестве аргумента за или против, а чтобы показать отличие. Если кто-то использует =, то будет точно так же, как сейчас. А новый оператор позволит уменьшить количество вариантов, где можно совершить ошибку.

                  Элементарно. Ставится директива: все новые шаблоны и исправление старых — только на twig.

                  Это абсолютно никак не уменьшает сложность перевода. Как нарисовать сову? Элементарно — ставится директива: нарисовать сову.

                  Почему тогда просто не использовать нормальный качественный шаблонизатор?

                  Есть такие проекты, это факт. Для них нужно писать код, это тоже факт. Не думайте только о себе.
                  Речь не о том, нужны шаблонизаторы или нет. Шаблонизаторы вещь полезная, это тоже факт.

                  Вы предлагаете ввести средство, которое пригодится лишь в коде плохого качества.

                  Отсутствие шаблонизатора не означает код плохого качества.


  1. Reuniko
    27.06.2016 12:47
    -5

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


    Тем временем MVC в Битриксе решает эту проблему следующим образом:


    Компонент формирует данные для шаблона:


    array(
        'TITLE'   => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
        'CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
    );

    Движок перед подключением шаблона экранирует данные:


    array(
        'TITLE'    => 'Опрос. Новый тег <?~ $value ?> для HTML-экранирования данных в PHP',
        'CONTENT'  => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
        '~TITLE'   => 'Опрос. Новый тег &lt;?~ $value ?&gt; для HTML-экранирования данных в PHP',
        '~CONTENT' => 'Некоторое время назад была статья про нововведения в PHP7. Я написал в комментариях, что раз уж добавлены разные новые операторы для упрощения кода в стандартных конструкциях, то неплохо было бы добавить еще и оператор для вывода HTML-экранированных данных. Получил в от...',
    );

    Теперь в шаблоне вы можете использовать:


    <?=$arResult['TITLE']?>
    <?=$arResult['~TITLE']?>

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


    1. SamDark
      27.06.2016 13:12
      +3

      Такое экранирование опасно. Можно же умудриться и вывести данные в немного другом контексте, где стандартное экранирование не сработает.


  1. gpaw
    27.06.2016 12:49
    +3

    Тильда — побитовое отрицание, одно неловкое движение — — и мы получаем не то, что хотим.

    По самому же вопросу — да, с одной стороны это удобно. Но, на мой взгляд, введение оператора прозвучит как призыв опять размыть границы между логикой и шаблоном, яркий пример чего — печально известный код CMS-Которую-Нельзя-Называть.


    1. gpaw
      27.06.2016 12:59
      +2

      между тире должно стоять <?=~$foo;?> — прошу прощения, не разобрался с допустимыми тегами.


  1. atoshin
    27.06.2016 12:55
    -1

    > Я знаю про шаблонизаторы

    Похоже, что нет, так как PHP суть просто шаблонизатор с «расширенными» возможностями


  1. SamDark
    27.06.2016 13:16
    +1

    С одной стороны желание понятно, с другой — надо будет очень сильно постараться, чтобы втолковать всем, что <~$content ?> — всего лишь замена <?= htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?>, а не универсальное экранирование под любой возможный контекст...


    Так я считаю, что подумать над RFC стоит.


    1. michael_vostrikov
      27.06.2016 13:32
      +1

      Да, по этому поводу была пара замечаний от разработчиков. Это то, что стоит подробно описать в документации (если она будет).


  1. ollisso
    27.06.2016 14:57
    +1

    Проблема с <?~ и тп в том что их будут часто забывать использовать.

    Можно сделать обратное предложение.
    1. сделать настройку (или любым другим способом), чтобы все подобные записи были автоматически экранированы: <?= $abc ?> — автоматически экранировано.
    2. сделать новый оператор: <?== $abc ?> который выводит не экранировано.

    Проблемы:
    1. Сейчас пхп комьюнити движется в сторону уменьшение настроек.
    2. Плохо то, что один и тот же оператор будет значить разные вещи, в зависимости от настроек.

    Решение 2:
    1. не менять работу оператора <?=
    2. добавить оператор <?~ или <?== (синтаксис — наименьшая проблема) который автоматически экранирует
    3. Добавить настройку, которая включает режим варнингов в случае использования <?= оператора. Нужно это чтобы люди не использовали <?= где не надо.
    4. Добавить оператор <?== который выводит данные без экранирования.

    Проблемы:
    1. вместо 1 оператора — становиться 3, и 1 настройка.


    1. michael_vostrikov
      27.06.2016 15:22
      +1

      Оба варианта слишком ломают обратную совместимость. С тем, что будут забывать использовать, никакой проблемы нет, потому что нет никакого отличия от текущей ситуации. К тому же, забывать можно, если используется редко, а экранированный вывод используется часто.


  1. brain2xml
    27.06.2016 15:15
    -2

    Вообще тут не верный подход, например в yii проще и лучше при получении данных в модели сделать все преобразования afterFind, а потом отправлять безопасные данные на вывод в шаблон. Так получается место откуда все расходиться и не надо думать где ты написал <?= или <?~


    1. zelenin
      27.06.2016 15:20
      +1

      https://habrahabr.ru/post/304162/#comment_9676184


      1. brain2xml
        27.06.2016 15:23

        Ну да, так по научному


        1. zelenin
          27.06.2016 15:26
          +1

          вы поняли или не поняли? разделение ответственностей — модель ничего не должна знать о представлении, соответсвенно и не должна подготавливать данные для представления. Поэтому никаких afterFind.


          1. brain2xml
            27.06.2016 15:40

            Мне это не кажется логичным. По мимо экранирования вывода есть ещё уйма всего чего надо сделать с данными перед отправкой в шаблон. Начиная от преобразования даты до замены boolean. Перекладывая ответственность на шаблон вы порождаете кучу спагетти кода. А ещё не будем забывать про рефакторинг. Если заголовок новости используется в шаблоне 3 раза (сам заголовок, alt и title для фото то зачем 3 раза вызывать эскейп, пусть даже и в короткой форме чем один раз в модели. Тем более что если сложно представить что где-то может понадобиться не экранированный заголовок.

            В самой схеме классов заложен механизм преобразования и не очень понятно зачем снимать с него ответственность и перекладывая на шаблоны которых может быть гораздо больше чем один


            1. zelenin
              27.06.2016 15:51
              +1

              В представлении у меня дата в формате «2015, 26 марта», а в апи в формате 2015-03-26T15:41:12Z. Дублировать преобразование? Для 10 точек выхода — десятерить?

              >>> Перекладывая ответственность на шаблон вы порождаете кучу спагетти кода

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

              >>> А ещё не будем забывать про рефакторинг

              вот-вот. Что-то вы забыли совсем о нем, раздувая модель засчет каких-то хелперов.

              >>> Если заголовок новости используется в шаблоне 3 раза (сам заголовок, alt и title для фото то зачем 3 раза вызывать эскейп, пусть даже и в короткой форме чем один раз в модели. Тем более что если сложно представить что где-то может понадобиться не экранированный заголовок.

              ну как где? В апи например. В выхлопе консольной команды. В логах. Везде кроме html-вьюшки на самом деле.

              >>> В самой схеме классов заложен механизм преобразования

              в какой схеме классов? какой механизм? Вы же сейчас про yii? Там есть некая событийная модель, позволяющая подписываться на события до и после сохранения например. Как вы этим будете пользоваться, дело ваше. Но возможность подписаться !== заложенный механизм преобразования.


            1. VolCh
              27.06.2016 19:14

              Экранирование данных — ответственность или контроллера, или представления, скорее представления, но никак не модели.


  1. symbix
    27.06.2016 15:35
    +2

    В некоторых европейских раскладках тильды на клавиатуре в явном виде нет, и вводить ее надо с AltGr/Shift-Alt/прочими ухищрениями.


    1. michael_vostrikov
      27.06.2016 16:06

      Хороший аргумент, спасибо. Надо будет подумать над этим.


  1. shoomyst
    27.06.2016 15:57
    +1

    Иметь что-то подобное было бы неплохо, но копать надо глубже, чем кажется на первый взгляд.

    Ну и что касается контекстно-зависимого экранирования:
    https://github.com/symfony/symfony/blob/303f05baafc2267b72812c44670493433b7acb0f/src/Symfony/Component/Templating/PhpEngine.php#L419


  1. Fesor
    28.06.2016 02:17
    +2

    • новая синтаксическая конструкция для тривиальной задачи. Лучше вообще убрать эти <?php к чертям или сделать необязательными (RFC для этого вроде бы была). Вот тогда заживем и вариантов кроме как использовать адекватные шаблонизаторы не останется.
    • "забыть" ее применить можно с той же долей вероятности что и хелпер функцию
    • целая новая синтаксическая конструкция которая обрабатывает только ОДИН частный случай экранирования
    • лишний пробел — мы проиграли.

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


    p.s. Аргумент мол "php и так шаблонизатор" можно рассматривать конечно, но это очень плохой шаблонизатор.


    1. michael_vostrikov
      28.06.2016 07:18

      • Проекты с его использованием никуда не денутся, и для них также надо будет писать код.
      • Можно написать либо одну конструкцию, либо другую. А хелпер-функцию можно написать либо нет независимо от начала конструкции. И вероятность забыть применить все-таки немного меньше, потому что эта конструкция должна применяться при любом выводе в HTML, и если у вас везде в файле встречается <?~ ?>, то надо задуматься, зачем ставить другой оператор. Также обычно просто копи-пастят, и копи-паста в данном случае будет помогать.
      • Это один частный случай, но и самый частый. Он встречается даже чаще, чем конструкции для ?? и <=>.
        Вообще, слово "частный" здесь не очень подходит. Если бы контексты были взаимоисключающими, тогда да. А так этот частный случай используется вместе с другими частными случаями. Причем используется он всегда, за исключением тега script, но внутри тега скрипт ничего и работать не будет, если мы выведем туда объект через htmlspecialchars.
      • Если будет лишний пробел, то ничего не выведется, и это будет заметно. А также для этого надо короткие теги включить.

      Можете привести конкретный пример возможных проблем с кодом на PHP?


      1. SamDark
        28.06.2016 11:42
        +1

        Кстати, про лишний пробел Fesor прав: <? ~$test ?>.


  1. Finesse
    28.06.2016 07:16
    +2

    HTML — это лишь один из множества возможных форматов вывода PHP-приложения. Вы предлагаете отдать предпочтение одному формату, обделив остальные. Ваши примеры с экранированием URL и JS некорректны, потому что в них htmlspecialchars используется не для экранирования строки для вставки в URL и JS, а для вставки готового проэкранированного кода в HTML.


    1. michael_vostrikov
      28.06.2016 07:22

      HTML — это самый часто используемый формат вывода.

      Ваши примеры с экранированием URL и JS некорректны

      Ок, как по-вашему они должны быть написаны? Приведите конкретный код, пожалуйста.


      1. Finesse
        28.06.2016 10:06
        +1

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

        Пример использования PHP для генерации JS:
        var string = <?= json_encode( $string ) ?>;

        htmlspecialchars не требуется. А вот пример вставки JS-кода в HTML:
        Ссылка

        Тут он требуется, потому что это вставка в HTML. Следовательно, htmlspecialchars не обязателен для экранирования строк для JS.

        (прошу прощения за не подсвеченный код, у меня нет прав на это)


        1. michael_vostrikov
          28.06.2016 11:08

          а в практике другого человека не самый частый, он бы хотел

          Для этого и нужен процесс RFC. Кроме того, речь идет не об одном человеке, а о наиболее частом случае использования языка. Думаю, никто не будет отрицать, что PHP чаще всего применяется для веб-програмирования и обработки гипертекста.

          Да и способов экранировать тот же HTML множество, в разных случаях могут понадобиться разные.

          Приведите пример.

          Следовательно, htmlspecialchars не обязателен для экранирования строк для JS.

          Я про это и писал в статье. Это не универсальный оператор экранирования, он предназначен специально для контекста HTML. Потому что вывод в HTML делается во много раз чаще, чем передача переменых в JS, и потому что он может применяться совместно с другими контекстами, а не вместо них. Это просто замена постоянному вызову htmlspecialchars.


          1. VolCh
            28.06.2016 11:23
            +1

            Приведите пример.
            string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 [, string $encoding = ini_get(«default_charset») [, bool $double_encode = true ]]] ) http://php.net/manual/en/function.htmlspecialchars.php
            Как минимум 3 разных стандарта HTML (4.01, xhtml, 5)


            1. SamDark
              28.06.2016 11:44

              Вот, кстати, да. Тоже важный момент.


            1. michael_vostrikov
              28.06.2016 12:06

              Нет, я имел в виду пример, который может привести к проблемам в безопасности и/или нарушению разметки. Эти флаги для разных стандартов HTML различаются набором дополнительных сущностей. Насколько я понимаю, для правильного формирования разметки достаточно преобразовывать 5 базовых сущностей.


              1. VolCh
                28.06.2016 12:36

                Для правильного формирования разметки по соответствующему стандарту надо преобразовывать то, что нужно по этому конкретному стандарту. Только этот вариант будет правильным.


                1. michael_vostrikov
                  28.06.2016 13:01

                  Правильным — это в смысле не пройдет валидацию валидатором, если у нас в данных вдруг есть символ ©, который должен кодироваться & copy ;? Это полезное замечание конечно. Но основная цель — уменьшить повторяющиеся действия и повысить безопасность. Поэтому я и попросил привести пример, в котором будет видно именно проблему с применением.


          1. Fesor
            28.06.2016 11:46

            Это не универсальный оператор экранирования


            сделайте свой препроцессор и используйте в свое удовольствие. Операторы только для 80% случаев не нужны в языке программирования.


            1. michael_vostrikov
              28.06.2016 12:20

              Есть операторы, которые используются гораздо реже. Если бы их не было, я бы не стал поднимать этот вопрос. Опять же есть более короткие записи для уже существующих операторов. Вместо тернарного оператора можно использовать if, вместо <?= ?> оператор <?php echo ?>. Но ни у кого не вызывает вопросов необходимость их наличия в языке.

              Универсальное экранирование для всех случаев в принципе невозможно придумать. Делать возможность добавлять свои эскейперы технически сложно, да и не нужно. Это как раз к тому вопросу, что не стоит превращать язык в шаблонизатор. Раз-другой можно и вручную указать. Если это часто используется в каком-то отдельном проекте, то нужно действительно взять специализированный движок для шаблонов. Но на фоне общего числа проектов на PHP проекты с кастомными эскейперами это очень незначительная часть.


              1. Fesor
                28.06.2016 17:13

                Есть операторы, которые используются гораздо реже.


                От того что вы их не используете они не теряют полезности. К примеру взять spaceship оператор. Вроде штука бесполезная, особенно если мы циферки сравниваем. А что если сегодня была циферка, а завтра станет строка?:
                usort($arr, function ($a, $b) {
                    return $a['foo'] <=> $b['bar']; // и там не важно что за тип данных там
                });


                А главное — оператор покрывает все кейсы связанные со сравнением. Ваш же хорошо если 80% случаев покрывает. И смысл тогда его использовать, всеравно рано или поздно придется отказываться от этого в пользу функций хэлперов.

                А еще можно поступить вообще круто, перестать использовать php как шаблонизатор и взять какой twig.


                1. michael_vostrikov
                  28.06.2016 18:30

                  А главное — оператор покрывает все кейсы связанные со сравнением. Ваш же хорошо если 80% случаев покрывает.

                  Ну это уже демагогия) А «мой оператор» покрывает 100% случаев, связанных с экранированием вывода в HTML. Если вы не используете экранирование HTML, оно не теряет полезности для тех, кто его использует. А специальный оператор — это сокращение для этой частой операции. В проектах без шаблонизаторов экранирование в местах текущего использования <?= ?> нужно даже не в 80, а примерно в 99% случаев.
                  Кстати, если вы не используете экранирование, то этот оператор на ваши проекты не повлияет вообще никак. А тем, кто использует, поможет повысить безопасность и уменьшить копи-пасту одних и тех же вызовов.

                  все равно рано или поздно придется отказываться от этого в пользу функций хэлперов.

                  Например в каких случаях? Я снова прошу вас привести пример кода. Если задача будет связана с выводом в HTML, то и экранирование никуда не денется, независимо от дополнительной обработки через urlencode, json_encode или something_else_encode.

                  А еще можно поступить вообще круто, перестать использовать php как шаблонизатор и взять какой twig.

                  Речь не о том, нужны шаблонизаторы или нет. Я согласен с тем, что лучше использовать их. Есть проекты без них, и во многих случаях перейти на шаблонизатор уже нет возможности.


                  1. Fesor
                    28.06.2016 21:33
                    +1

                    А «мой оператор» покрывает 100% случаев, связанных с экранированием вывода в HTML


                    вам уже в рамках этой статьи приводили минимум 2-3 отличающихся от вашего способа экранирования (различия в опциях).
                    Есть проекты без них, и во многих случаях перейти на шаблонизатор уже нет возможности.


                    так напишите маленький препроцессор PHP-шаблонов, и там вводите свой новый синтаксис. Нечего подобным вещам делать на уровне языка. И в итоге мы имеем решение проблемы за 15 минут и отсутствие необходимости расширять синтаксические конструкции языка.


                    1. symbix
                      28.06.2016 21:56
                      +1

                      Laravel-овский blade фактически и есть такой препроцессор. Разве что не совсем маленький.


                    1. michael_vostrikov
                      28.06.2016 23:24

                      вам уже в рамках этой статьи приводили минимум 2-3 отличающихся от вашего способа экранирования (различия в опциях)

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

                      Symfony — ENT_QUOTES | ENT_SUBSTITUTE
                      Yii — ENT_QUOTES | ENT_SUBSTITUTE
                      Zend — ENT_QUOTES | ENT_SUBSTITUTE
                      Twig — ENT_QUOTES | ENT_SUBSTITUTE
                      Smarty — ENT_QUOTES
                      Blade — ENT_QUOTES
                      Facebook XHP — [по умолчанию]

                      Я даже больше скажу. При любых флагах в htmlspecialchars кодируется только 5 основных сущностей (а, ну да, можно кавычки не кодировать, только смысл тогда экранировать данные). Сочетания флагов влияют только на способ кодирования апострофа, остальное различие сводится к разным способам обработки невалидных последовательностей. Много у вас в БД невалидных последовательностей?)

                      htmlspecialchars()
                      php_escape_html_entities_ex()
                      determine_entity_table()
                      таблицы

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

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


                      1. VolCh
                        28.06.2016 23:33
                        +1

                        Больше половины используют ещё и чарсет (привет вин1251 на легаси), некоторые играются четвёртым параметром htmlspecialchars. А в базе, то, что ввёл пользователь :)


                        1. symbix
                          29.06.2016 01:56

                          Не, ну положить в нормальную базу невалидный юникод — это надо специально постараться.


                        1. michael_vostrikov
                          29.06.2016 06:55

                          Чарсет по умолчанию настраивается отдельно: string $encoding = ini_get("default_charset"), так что и данные в этой кодировке будут считаться валидными. Много ли таких проектов, у которых в базе одна кодировка, на странице другая, в приложении третья, и для всего этого еще и не сделана нормальная конвертация? Много ли таких «некоторых» от общего числа, кто использует четвертый параметр? Это наверное те, кто экранирует данные при сохранении в базу.

                          Как-то даже не очень хорошо получается. «Мы используем нестандартные параметры, поэтому из-за нас не должно быть оператора для стандартных параметров. И все обязаны делать так же, как мы, несмотря на то, что стандартных случаев гораздо больше».


                          1. VolCh
                            29.06.2016 07:42
                            +1

                            Дело не в «мы», дело в том, что если <?~ позиционируется как замена <?= htmlspecialchars, то нужно и все параметры её как-то использовать. И что значит «нестандартные»?


                            1. michael_vostrikov
                              29.06.2016 08:33
                              -1

                              Она позиционируется как замена самому частому случаю. Я бы даже не стал называть это именно «замена». Сама функция никуда не денется.
                              Нестандартные — значит встречающиеся очень редко и специфичные для задачи. Думаю, стандартными в данном случае можно считать параметры, использующиеся по умолчанию в большинстве фреймворков.


                              1. VolCh
                                29.06.2016 08:51
                                +1

                                Так вон во фреймворках разные значения, плюс чарсет, плюс у некоторых false четвертый параметр.


                                1. michael_vostrikov
                                  29.06.2016 10:12
                                  -1

                                  Во фреймворках ENT_QUOTES | ENT_SUBSTITUTE. Отдельно ENT_QUOTES это менее строгий режим, и следовательно менее безопасный. Чарсет берется из настроек приложения. Задач, когда в документе одна кодировка, а в данных другая, и это действительно надо, очень мало. Как и не кодировать HTML-сущности в функции их кодирования. Я же говорю, это не повод из-за отдельных случаев не добавлять оператор, которым будут пользоваться многие. Некоторые фиче-реквесты еще в 2002 году созданы.


  1. truezemez
    28.06.2016 08:59
    +3

    Какой-то «оператор частного случая» получается.
    Я вот, html экранирую так:

    htmlspecialchars($str, ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED | ENT_SUBSTITUTE, 'UTF-8');
    

    Заведем еще один тег?


    1. michael_vostrikov
      28.06.2016 09:56

      Конкретные флаги это уже детали реализации. Думаю, нужно делать такие, которые используются в большинстве крупных фреймворков. Сейчас вопрос в том, нужно ли наличие такого специализированного оператора для HTML контекста.


      1. VolCh
        28.06.2016 11:26

        У нас минимум 3 разных HTML контекста, особенно если речь о legacy коде. А шаблонизаторы зачастую позволяют устанавливать метод и флаги экранирования. То есть, как минимум, если и вводить эту инструкцию, то с возможностью гибкого конфигурирования на уровне php.ini, а то и declare.


        1. VolCh
          28.06.2016 11:36

          Ещё подумал, что если конфигурировать на уровне php.ini и(или) declare, то конфигурировать можно обычный <?=, добавив новую инструкцию, которая не экранирует. По умолчанию для <?= экранирование должно быть отключено, чтобы не нарушать BC, но должна быть возможность гибко его настроить.


          1. symbix
            28.06.2016 13:22

            И получится еще один magic_quotes. Спасибо, нет.


  1. devian3000
    28.06.2016 10:44

    А не проще решить всё абстракцией?))
    Ставим прослойку между контроллером и представлением и обрабатываем нужные данные, так как нужно (экранирование, преобразование даты и прочее)?
    Тем самым в представлении уже подготовленные данные, которые можно выводить как угодно.
    Прослойка мигрирует из проекта в проект.

    Даже в Bitrix можно воткнуть данное решение ( костылём правда ).

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


    1. michael_vostrikov
      28.06.2016 10:56

      Приведите пример, как это можно сделать. Даже с преобразованием даты будут проблемы — если для дэйтпикера нужен один формат с полным названием месяца на текущем языке пользователя, а в hidden-поле нужен другой технический формат, который можно отправить на сервер. Или другой пример — когда передается объект user, а свойство user->fullName это геттер, который конкатенирует отдельные составляющие имени.

      так как экранировать можно в миллионах разных вариаций

      Этот оператор не является супер-универсальным оператором на все случаи жизни. Как раз потому что такой оператор сделать нельзя. В этих вариациях есть 2 части — одна зависит от задачи, другая от внешнего контекста. Миллион вариаций — это про первую часть, а вторая у нас всегда HTML, исключения из этого правила описаны в статье. Для нее и нужен этот оператор.


      1. devian3000
        28.06.2016 11:15

        Декоратор?)

        Есть некий абстрактный сервис преобразования ( будем использовать немного магии ).

        ```php
        interface IDataPreparator{

        public function setData( array $data );

        /**
        * return array
        */
        public function getData();

        }
        ```

        Массив передаём предположим так.

        ```php

        $data = [
        'dateOne' => ['value' => '10 feb 2012', 'type' => 'date', 'format' => 'RU' ],
        'dateTwo' => [ 'value' => '10 feb 2012', 'type' => 'date', 'format' => 'EN' ],
        'HtmlString' => ['value' => 'Hello world', 'type' => 'html', 'options' => ['encoding' => 'utf-8']]
        ];

        ```
        Где ключ название переменной, а значения правила.

        Да, придётся много писать, но более универсальное средство.
        Да и делается это сколько угодно раз.
        Чтобы вернуть всё в состояние переменных сделаем extract();

        Profit, написали библиотеку для преобразований, а в представлении работаем уже с подготовленными данными.

        ```php

        class Controller {

        public function actionIndex(){
        $data = Model::getData();
        $preparedData = (new DataPreparator()) -> setData([ 'dateOne' => ['value' => $data['dateNewYork'], 'format' => 'EN' ] ])->getPreparedData();
        $this->renderView('view', $preparedData );
        }

        }

        ```


    1. VolCh
      28.06.2016 11:33
      +1

      Как минимум в шаблоне нам может понадобиться выводить:
      — экранированные значения элементов массивов
      — экранированные значения публичных свойств объектов
      — экранированные значения публичных методов объектов
      — экранированные значения метода __to_string объектов
      — экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической.

      Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования?


      1. devian3000
        28.06.2016 11:46

        — экранированные значения метода __to_string объектов ( вот это я впринципе не использую и ни разу не видел чтобы где-то использовалось, по мне, так плохой тон пытаться объект неявно приводить к строке ИМХО).

        — экранированные значения элементов массивов ( вы предлагаете мусорить в представлении? хотя по сути да, лишние хопы на двойной обход массива, но опять же мы, если массив содержит неоднородные данные, то переносится логика в представление, что не есть хорошо. Если однородные то можно пожертвовать одним лишним проходом для приведения значений к нужным данным )

        — экранированные значения публичных свойств объектов ( для этого существуют геттеры и сеттеры, инкапсуляция наше всё )

        — экранированные значения публичных методов объектов ( тут согласен, есть проблемы, хотя опять же вызывайте получающие данные методы ранее, и передавайте во вьюху только данные, тут вопрос где вызывать, с какими условиями и зависит конкретно от логики приложения и архитектуры)

        — экранированные значения выражений из всего этого, причём разница между экранированием результата выражения и экранированием его операндов может оказаться критической. ( опять же, зачем вам операции во вьюхе, готовьте данные в сервисе, отдавайте во вьюху результаты, вы снова пытаетесь логику закинуть туда, где её не должно быть).

        Представляете такую абстракцию, готовящую данные для представления заранее, а не декорированием по месту использования? (Да, фильтры, не вижу ничего страшного чтобы ввести ещё один уровень для подготовки данных)


        1. michael_vostrikov
          28.06.2016 12:30

          вы снова пытаетесь логику закинуть туда, где её не должно быть

          Это тема отдельного разговора. У представления есть своя внутренняя логика отображения. Не нужно путать ее с бизнес-логикой.

          Скрытый текст
          Пример — одни и те же данные (из сервиса их получения) можно представить и в виде таблицы, и в виде чарта, а в таблице сделать строку «Итого» вверху, внизу, либо и там и там, а так же еще и в тексте перед таблицей изложить, а для разных чартов сгруппировать данные по-разному. Предлагаете все эти знания, касающиеся отображения, поместить в контроллер или бизнес-логику?


          1. devian3000
            28.06.2016 12:41

            Да отображайте как хотите.
            Вы всё равно представляете готовый данные, в чём проблема в генерации чарта, таблицы или любой другой части используя уже преобразованные данные?

            Итого посчитать? Это задача бизнес логики, а никак не представления, или если у вас в 3-ёх представлениях «ИТОГО» нужно, а в 4-ом нет, вы будете считать отдельно в каждом из трёх?

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

            Группируйте сколько угодно, опять же какая разница группировать подготовленные данные или неподготовленные к выводу?
            А как это делать, уже вопрос задачи.

            Теперь по факту:
            Вы хотите узнать нужен ли оператор, я вам говорю, что по моему мнению не нужен, так как 99.9% проблем с обработкой вывода я могу решить так или иначе без его использования (и намного гибче), на каком либо уровне абстракции, там где мне это нужно.Лишняя фича просто.

            Вплоть до того что объект можно декорировать как угодно, для JSON, для XML, для HTML, да хоть для слепых в аудио виде выводить.


            1. VolCh
              28.06.2016 12:54

              Итого посчитать? Это задача бизнес логики, а никак не представления

              Обычно это как раз задача представления. В бизнес-логике «итого» часто не фигурирует никак, это лишь пожелание пользователей к видимому им результату. Ещё могут быть, например, пожелания показывать проценты от итого, проценты от максимума и(или) минимума и т. п. для каждого элемента, причём форматировать их по локали пользователя — это тоже всё в модель тащить?


        1. VolCh
          28.06.2016 12:48

          вот это я впринципе не использую и ни разу не видел чтобы где-то использовалось, по мне, так плохой тон пытаться объект неявно приводить к строке ИМХО

          Вообще много где используется.
          вы предлагаете мусорить в представлении?

          Передать в представление массив из элементов списка — это мусорить?
          для этого существуют геттеры и сеттеры, инкапсуляция наше всё

          1. Геттеры и сеттеры часто являются оверинжеренингом
          2. Не путайте инкапсуляцию с сокрытием. То, что мы перенесли какую-то переменную, связанную с объектом, в его свойство уже является инкапсуляцией.
          вызывайте получающие данные методы ранее, и передавайте во вьюху только данные

          Как минимум нарушение принципов MVC — представление должно получать данные от модели, вы же предлагаете реализовывать какой-то MVVM.
          опять же, зачем вам операции во вьюхе, готовьте данные в сервисе, отдавайте во вьюху результаты

          Нарушение границ ответственности, только вьюха должна решать в какой форме выдавать данные. Банальное прибавление 1 к индексу массива, чтобы получить номер элемента — это чисто функция представления. А если мы заэкранируем индекс, а потом прибавим заэкраннированную 1 то получим, мягко говоря, не совсем то, что ожидаем.


          1. devian3000
            28.06.2016 13:00

            Передать в представление массив из элементов списка — это мусорить? — нет, а вот проходить по массиву и делать с ним манипуляции не касающиеся вывода. ( складывать, вычитать, умножать, проверять на вхождения и прочее. )

            1. Геттеры и сеттеры часто являются оверинжеренингом
            2. Не путайте инкапсуляцию с сокрытием. То, что мы перенесли какую-то переменную, связанную с объектом, в его свойство уже является инкапсуляцией. — Мы ищем решение проблемы или что? Вам нужны данные которые вы ожидаете. Используйте get и set чтобы отдавать и получать те данные которые вы ожидаете. Не просто так же придумано.

            Нарушение границ ответственности, только вьюха должна решать в какой форме выдавать данные. Банальное прибавление 1 к индексу массива, чтобы получить номер элемента — это чисто функция представления.
            — Мы с вами об одном и том же говорим? Отображайте данные как хотите. Данная абстракция относится скорее в препроцесу представления, чем к логике модели, вы заранее приводите данные для отдачи клиенту (HTML, URL и прочее к виду который нужен htmlspecialchars и т.д. а далее навешивайте всё как хотите).

            А если мы заэкранируем индекс, а потом прибавим заэкраннированную 1 то получим, мягко говоря, не совсем то, что ожидаем. — А зачем вам экранировать int float double вы не знаете что где используете в вашем приложении? Экранируйте на этапе передачи то что нужно, зачем экранировать и преобразовывать всё?

            Мы с вами видимо про разные вещи разговариваем.


            1. VolCh
              28.06.2016 13:16

              вот проходить по массиву и делать с ним манипуляции не касающиеся вывода. ( складывать, вычитать, умножать, проверять на вхождения и прочее. )

              Если пользовательский интерфейс содержит настройки типа фильтров, сортировки, галочки «показывать итоги» и т. п., то всё это тащить в модель или контроллер и на каждый чих их дёргать?
              Мы с вами об одном и том же говорим? Отображайте данные как хотите.

              Вот именно, задача представления отобразить данные как оно хочет. Ни модели, ни контроллеру незачем знать как оно хочет, их задача обеспечить представлению данные, которые оно хочет отобразить, а уж оно само решает как их отображать.
              А зачем вам экранировать int float double вы не знаете что где используете в вашем приложении? Экранируйте на этапе передачи то что нужно, зачем экранировать и преобразовывать всё?

              Экранировать нужно всё по умолчанию, выводить сырые данные в каком-то контексте только при стопроцентной уверенности, что это данные уже готовы для этого контекста. Это требование безопасности.

              Видимо, да, про разные. Не похоже, что вы говорите о разделении ответственности подобному MVC.


              1. devian3000
                28.06.2016 13:33

                Если пользовательский интерфейс содержит настройки типа фильтров, сортировки, галочки «показывать итоги» и т. п., то всё это тащить в модель или контроллер и на каждый чих их дёргать? — а как вы ещё собираетесь это делать? Вы в любом случае будете дёргать бэкенд, или же как раз переходим к модели MVVM где вьюха решает что где как и в каких количествах (если это десктоп предположим). в вебе передавать все результаты, страннова-то, особенно если данных много.

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

                Экранировать нужно всё по умолчанию, выводить сырые данные в каком-то контексте только при стопроцентной уверенности, что это данные уже готовы для этого контекста. Это требование безопасности. — ключевое слово выводить. Вот то что выводите и экранируйте на этапе прекомпиляции представления. А индексы, и прочее не вижу смысла экранировать если они не выводятся, если они числовые. Расскажите мне как не экранированное число в выводе может навредить? Или как если у вас на этапе валидации проверяется имя, которое состоит только из букв (в России по крайней мере), внезапно начнёт содержать html текст который вы не вводили?

                Видимо, да, про разные. Не похоже, что вы говорите о разделении ответственности подобному MVC. — Да нормально разделяется ответственность. Группировки, сортировки, фильтры, делаются на уровне сервиса. А как выводить ( слева, справа, в столбик, таблицей или гридом ) решает представление. При изменении данных, представление сообщает это контроллеру, который в зависимости от того что изменилось дёргает тот или иной сервис, модель, метод. Я же вам не говорю, что вы должны представление готовить на уровне модели. Я говорю вам прямым текстом.

                Модель -> Препроцессор -> Представление.
                В препроцессоре дёргается библиотека которая приводит данные на вывод в нужный формат который использует представление.


                1. VolCh
                  28.06.2016 14:11

                  а как вы ещё собираетесь это делать?

                  Передать все данные представлению, а оно пускай фильтрует сортирует и т. п. Если что

                  Чем ваш препроцессор отличается от вынесенной в отдельный файл части представления? Он, как я понимаю, знаёт всё о представлении и знает о модели то, что обычно о ней знает представление. Для контроллера он заменяет представление в части передачи ему данных от модели, для модели заменяет представление в части получения данных от модели. По сути имеет интерфейс представления для контроллера и пользуется интерфейсом модели от имени представления, оставляя представлению лишь формирование команд для контроллера.


  1. kirilloid
    28.06.2016 12:27

    Хотя это и bloody enterprise, но вот хороший пример контекстно-зависимого экранирования, в AEM. У них там всё магически работает из коробки, т.к. парсер анализирует HTML в который он встраивается. Но чисто как список возможных контекстов пригодится.


    1. michael_vostrikov
      28.06.2016 12:47

      Вот то, что получается в результате действия styleToken и scriptToken

      style="color: ${properties.color @ context='styleToken'};"
      onclick="${properties.function @ context='scriptToken'}();"
      

      … должно быть еще и HTML-encoded. Это написано у них в таблице
      text | Default for content inside elements | Encodes all HTML special characters.
      attribute | Default for attribute values | Encodes all HTML special characters.

      … и об этом я и писал в статье.

      Многие элементы из этой таблицы не используются совместно с PHP. Например styleComment — часто вы встречаете генерацию комментов в теге style через PHP?


  1. oWeRQ
    28.06.2016 12:35

    Идея интересная, но как уже указали выше, зависит от контекста и тильда — не самый лучший вариант, а что если пойти с другой стороны, основная проблема — неудобство вставки, <?=h($hello)?> занимает 8 символов, длинно, неудобно и некрасиво. Как насчет сделать вызов функций и методов без скобок, <?h $hello?>


    1. michael_vostrikov
      28.06.2016 12:50

      Ну то что он зависит от контекста, я сам написал в статье. Тильда это уже детали реализации. С экранирующими функциями есть некоторая проблема с автозагрузкой, я писал выше. И проблема не в длине, я про это тоже писал.


    1. VolCh
      28.06.2016 12:57

      <?h $hello?>

      Логической разницы с <?=h($hello)?> никакой, а анализ кода усложнится. КРоме того, как вывести значение функций php(), xml() (и так с <?xml проблемы имеются у IDE)


      1. oWeRQ
        28.06.2016 15:43

        Логически никакой, но красивый синтаксис упростит поиск/замену, да и просто не будет вызывать боль при постоянной печати лишних скобок.


        С php и xml большой проблемы быть не должно, разве что через пробел надо писать, вопрос скорее в том, не будет ли конфликтов с существующим синтаксисом.


        1. VolCh
          28.06.2016 21:21

          В существующем синтаксисе три варианта после <?: <?php <? <?=


  1. idfUt74kex
    28.06.2016 13:12

    знаете, раньше все отлично делалось при помощи отвертки… а потом появились шуруповерты, только вот вкрутить шурупчик ровненько и аккуратненько при его помощи очень сложно, да и зачем и так сойдет… глядя на качество современного мира IT автоматизация только приводит в активизации лени и пофигизма человека, а вот написать самому персональный специализированый шаблонизатор или же постпроцессор для вывода или даже целый модуль под РНР который будет занимать постпроцесингом, почему нет? ЗАЧЕМ ЗАСОРЯТЬ ЯЗЫК, нет, если вы предлогаете это все динамическим модулей сделать, чтобы например все желающие могли нахрен это отключить — говно вопрос, ну тогда вы можете сами написать отдельный модуль.
    в общую корзину стоит пихать только то, что однозначно будет полезно всем и не принесет отрицательных моментов (производительность, совместимость)…


    1. michael_vostrikov
      28.06.2016 13:13

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


      1. idfUt74kex
        28.06.2016 17:00
        -1

        не скажу, что профи в написании компилятора и линковщика, но поправте, если мои суждения не верны, когда компилятор сканирует текст программы он выполняет схожую с RexExp операцией, дык вот какое правило RegEx отработает быстрее
        "[+]+" или же "[набор всех символов]+".Что-то мне подсказывает, что первый вариант :). Можно также сказать, что разница не велика… и в очередной раз пренебречь ресурсами планеты (а ведь клепать новые сервера не смогут бесконечно, а перерабатывать старые детишки в африке могут устать), либо выдать мол на мой век хватит… хотя таковое суждение сравнимо с саранчой.
        В РНр и так добавили хренову кучу всего (переменная переменных, премиси и прочие костыли вместо нормальной области видимости как в С), что по сути является специфическими костылями, дабы угодить популярным фрейворкам — это очевидно, кто платит, тот и заказывает музыку, в итоге язык превращает в папку system32 от винды — другими словами помойную яму. можно конешно собирать свою версию, к чему я для себя и пришел, но блин, это из разряда написания своей ОСи. «magic_quotes» и «registered_globals» добавили якобы чтобы сделать «лучше», и что вышло?

        PHP постига печальная судьба IP4 тоже думали, но видимо не тем местом… конечно можно сказать, что всего не учтешь, но обычно так говорят те, кому впадлу изрядно напрягать свои булки, хотя чисто физических предград для всеучтения нету…

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


  1. rdifb0
    28.06.2016 20:08

    А может вместо того чтобы плодить полумеры, в виде дополнительных операторов, взять и добавить в php шаблонизатор.
    Сделать какую-нибудь requre_template(array $data). Которая будет только для шаблонов, а внутри запилить полноценный шаблонизатор.
    Как именно реализовать можно уже проголосовать, взять ли готовый (имхо twig один из лучших) либо создать новый. За одно будет повод убрать укороченный синтаксис (без скобок) из php (чтобы не писали типа как в питоне), и в будущем убрать «php и так шаблонизатор».


    1. michael_vostrikov
      28.06.2016 20:15
      +1

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


      1. rdifb0
        28.06.2016 20:26

        А что заставит использовать ваш оператор? Если выпилить из php плохой шаблонизатор, в угоду полноценного, то выбора не останется. А в полноценном шабонизаторе можно включить экранирование по умолчанию.
        Я в общем не против нового оператора, пригодится. Но проблем он не совсем не решает.


        1. michael_vostrikov
          28.06.2016 21:19
          +1

          Он решает проблему экранирования и проблему случайного его отсутствия, этого достаточно. Данные выведутся экранированными, либо не выведутся совсем. С остальными видами экранирования такой проблемы нет — если вывести массив или объект в JS без json_encode, то будет просто ошибка в скрипте.
          Заставлять никого не надо, кому нужно — будет использовать, кому не нужно — все останется как есть.


  1. Settler1
    29.06.2016 10:59

    Открывающие теги указывают на язык, а то что вы предлагаете делать с помощью нового тега заменяет функцию. То есть эти вещи в принципе не совместимы и не должны таким образом применяться.


    1. michael_vostrikov
      29.06.2016 11:58

      Тег <?= ?> не только указывает на язык, но и выполняет действие.


      1. Settler1
        29.06.2016 12:04

        Действие какое? Вывод (echo), а echo это не функция, а конструкция языка. htmlspecialchars – ничто иное как функция, то есть совсем другое.


        1. michael_vostrikov
          29.06.2016 12:17
          -1

          Это спор о терминологии, а не аргументы за или против.
          — Новый оператор тоже будет конструкцией языка.
          — Оператор <?= ?> это вывод, а оператор <?~ ?> это HTML-экранированный вывод.
          — Вызов функции это тоже конструкция языка. Причем в прямом смысле слова: ZEND_AST_ECHO, ZEND_AST_CALL