LINUX.ORG.RU

Повторное использование куска кода (шаблона) в цикле

 


1

2

Сильно упрощённый пример.

Используются нативные шаблоны PHP.

Есть основной шаблон, в нём в цикле для каждого элемента в массиве выполнить код дополнительного шаблона. Если наиболее тупо то:

<ul>
  <?php foreach($this->items as $this->item): ?>
    <?php include('templates/items.php'); ?>
  <?php endforeach; ?>
</ul>

Сам templates/item.php например таков:

    <li><?= $this->item ?></li>

Но в цикле инклудить многократно файл не очень эффективно. Это ведь тут пример упрощённый, реальный код сложнее.

Другой вариант, чтобы исключить многократное подключение одного и того же файла это:

<ul>
  <?php foreach($this->items as $this->item): ?>
    <?php $this->templaPart('templates/items.php'); ?>
  <?php endforeach; ?>
</ul>

где templatePart() это

    public function templatePart($template)
    {
        if(isset(!$this->cached_templates[$template]))
        {
            $this->cached_templates[$template] = file_get_contents($template);
        }
        eval('?>'. $this->cached_template[$template]);
    }

Второй вариант уже поэффективнее, но eval...

Есть ли что получше без использования сторонних библиотек?

Deleted

Последнее исправление: pyroman (всего исправлений: 3)

Возьми Twig и не *** мозг.

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

deep-purple ★★★★★
()

Но в цикле инклудить многократно файл не очень эффективно.

на спичках экономишь

если это так парит то возьми нормальный шаблонизатор (твиг)

Noob_Linux ★★★★
()
Ответ на: комментарий от deep-purple

Возьми Twig и не *** мозг. Там вот это все уже за тебя придумали. На том и успокоишься.

Тогда зачем PHP вообще, можно и джангу использовать.

Deleted
()

Всё верно, если напрямую делать include(), разница в производительности существенная.

А если сделать так, разницы почти нет:

$ cat 1.php
<?php for($i = 0; $i < 100000; $i++) { ?>

i = <?php echo $i; ?>

<?php } ?>
$ cat 2.php
<?php for($i = 0; $i < 100000; $i++) { ?>

i = <?php $f = $f ? $f : include('3.php'); $f($i); ?>

<?php } ?>
$ cat 3.php
<?php return function ($i) { echo $i;}  ?>
$ time php -n -f 1.php > /dev/null

real	0m0,305s
user	0m0,103s
sys	0m0,169s
$ time php -n -f 2.php > /dev/null

real	0m0,351s
user	0m0,155s
sys	0m0,127s
$ 
Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Noob_Linux

если это так парит то возьми нормальный шаблонизатор (твиг)

Написать шаблонизатор, которым неудобно шаблонизировать, чтобы потом написать на нём шаблонизатор, которым удобно шаблонизировать.

Вся суть IT.

Deleted
()

Второй вариант уже поэффективнее, но eval...

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

vadim@aquila:/tmp/111$ cat 4.php
<?php for($i = 0; $i < 100000; $i++) { ?>

i = <?php eval("echo $i;") ?>

<?php } ?>
vadim@aquila:/tmp/111$ time php -n -f 4.php > /dev/null

real	0m0,799s
user	0m0,455s
sys	0m0,181s
vadim@aquila:/tmp/111$ 
Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от goingUp

Ты время мерял?

Замерил сейчас на 500 000 и 1000000 итерациях. Интересно... Вообще я думал по времени примерно одинаково будет, ожидал разницу в потреблении памяти. Однако нет.

Память потребляется одинаково, а вот время... Под PHP 5.3 вариант с include выполняется дольше. А под PHP 5.5 наоборот, include быстрее. o_O
Но меня больше удивило отсутствие разницы в потреблении памяти.

Deleted
()
Ответ на: комментарий от Deleted

Есть еще вариант, чтобы не return-нить лямбды.

Файлы шаблона написать как обычно.

Сделать обёртку, которая делает file_get_contents() шаблонов, заворачивает их в лямбды, eval-ит их, и в $this->cached_template[$template] сохраняет уже готовые лямбды, а не сырой текст.

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

Не знаю, насколько этот вариант жизнеспособен, просто идея. Я сам уже сто лет боевого кода на php не писал.

Deleted
()
Ответ на: комментарий от Deleted

А под PHP 5.5 наоборот, include быстрее. o_O

O_O

Прямой инклуд:

$ php --version
PHP 7.2.5 (cli) (built: May 10 2018 20:21:23) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

$ time php -n -f 5.php > /dev/null

real	0m4,224s
user	0m1,759s
sys	0m1,717s
Deleted
()
Ответ на: комментарий от Deleted

Сделать обёртку, которая делает file_get_contents() шаблонов, заворачивает их в лямбды, eval-ит их, и в $this->cached_template[$template] сохраняет уже готовые лямбды, а не сырой текст.

А как это так можно сделать так чтобы eval выполнялся только однократно? Не догоняю что-то. Шаблоны да, хотелось бы сохранить с минимум php кода. В идеале чтобы содержали только <?= ?>, управляющие конструкции (if/else) и иногда циклы.

Deleted
()
Ответ на: комментарий от VladimirMalyk

li не должен находиться во вложенном темплейте

Что это не должен? Там на самом деле html код гораздо сложнее с кучей div'ов, тут просто для упрощения.

зы. с импортами совсем никак? use которые. вообще глянь в качестве примера как напиленоhttps://github.com/yiisoft/yii2-app-basic/blob/master/views/site/contact.php

Не очень и такие вещи в темплейты совать не желательно:

$this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title;
Ну и вот это вот убивает весь смысл темплейтов:
<?= $form->field($model, 'body')->textarea(['rows' => 6]) ?>

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

Deleted
()
Ответ на: комментарий от Deleted

По тем меркам когда его делали он был нормальным, даже очень неплохим.

Noob_Linux ★★★★
()

eval -> evil

Генерируй код вместо eval и потом его подставляй в виде функции или класса.

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

Альтернатива MVC - разделяй данные, логику, представления на слои.

Это возможно использовать рекурсивно в каждом уровне при соответствующем уровне абстрагирования.

Самые крутые нативные шаблоны это XML+XSLT и им подобные технологии, их единственный недостаток сложность и неосиляторство. Можно свой велосипед: html-шаблон с плейсхолдерами и файл/поток данных для вставки. Можно в формате YAML или JSON. Наконец, задуматься о шаблонизации на стороне клиента, хотя там свои pros & cons

Лучше избегать безконтрольных include - организовать единую точку вызова, через автолоад.

Не хочешь зависимостей - в php достаточно встроенных средств.

anonymous
()
Ответ на: комментарий от Deleted

li неотделимы от ul, емнип. выносить li в шаблон глупо. содержимое li там должно быть и только.

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

VladimirMalyk ★★★★★
()
Последнее исправление: VladimirMalyk (всего исправлений: 1)
Ответ на: комментарий от VladimirMalyk

li неотделимы от ul, емнип.

Данный пример не предполагает их отдельного использования.

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

Там такой старый проект, который использует тупо кучу echo '<div class="class1">'.$content1.'</div>';. Хотелось просто вёрстку вынести отдельно, переписывать всё с нуля нет желания. Иначе бы я и на Django переписал бы чем в этой куче г ковыряться.

Deleted
()
Ответ на: комментарий от anonymous

Генерируй код вместо eval и потом его подставляй в виде функции или класса.

Ну что-то такое шаблонизаторы типа Twig и делают, imho оверхед.

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

Это не php вставки, это имитация шаблонных тегов. По сути php имеет встроенный шаблонизатор. Какая разница, было бы в коде что-то типа

<ul>
{% for item in list %}
    <li>{{ item }}</li>
{% endfor %}
</ul>

вместо

<ul>
  <?php foreach($list as $item): ?>
    <li><?= $item ?></li>
  <?php endforeach; ?>
</ul>
Что это меняет?

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

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

Самые крутые нативные шаблоны это XML+XSLT и им подобные технологии, их единственный недостаток сложность и неосиляторство. Можно свой велосипед: html-шаблон с плейсхолдерами и файл/поток данных для вставки. Можно в формате YAML или JSON. Наконец, задуматься о шаблонизации на стороне клиента, хотя там свои pros & cons

Оверхед. Последний вариант не нравится. Ну он может сгодиться если это не совсем сайт, а интерактивное веб приложение какое-то, а не динамический документ типа wiki, форумов, cms и т.д.

Deleted
()
Ответ на: комментарий от deep-purple

Причем тут джанга, речь о пыхе.

Прост.

Deleted
()
Ответ на: комментарий от Deleted

yii2 лучшее из того что я видел по склейке вёрстки и сырого пхп. если охота сделать красиво — лучше делать как там.

VladimirMalyk ★★★★★
()
Ответ на: комментарий от VladimirMalyk

Ну вот пример темплейта в твоём примере содержит в себе многое, чего в темплейтах быть по хорошему не должно, это убивает саму идею темплейтов и выноса вёрстки отдельно. В идеале должно быть как в шаблонизаторах со своим отдельным упрощённым языком, как в темплейтах Django или его PHP аналогах типа Twig. Если использовать нативные PHP шаблоны, то должно быть как-то так: https://pastebin.com/e0avCQ2B

Deleted
()
Ответ на: комментарий от anonymous

Да уж, в начале 2000 я делал мега-проект в котором как раз использовал в выводе xml+xslt, мне это казалось на то время верх совершенства. Что может быть универсальней генерировать XML, а потом трансформировать его через xslt? В то время, насколько помню, его понимали все браузеры(кажется за исключением IE, для него я прогонял через php+xslt и отдавал уже чистый html). Прошли года, а он так и не стал популярным. Да он сложный, нудный и для обычных сайтов это больше обуза, чем выигрыш. Хотя насколько я помню одно время он был «модным» и я часто замечал на сайтах эту связку.

gobot ★★★★
()
Ответ на: комментарий от Deleted

Файлы шаблона написать как обычно.
Сделать обёртку, которая делает file_get_contents() шаблонов, заворачивает их в лямбды, eval-ит их, и в $this->cached_template[$template] сохраняет уже готовые лямбды, а не сырой текст.

Вобщем, попробовал реализовать что-то такое, если я правильно понял.

<?php /* template.php */ ?>

    <li><?= $i ?></li>
<?php
// main.php

$count = 1000000;
  
function makeTemplate($file)
{
    $fn_body = file_get_contents($file);
    $fn_body = 'return function($i){ ?> '.$fn_body.' <?php };';
    return eval($fn_body);
}

$template = null;

for($i=0; $i<$count; $i++) {
    if(is_null($template))
        $template = makeTemplate('./part.template.php');
    $template($i);
}

?>

Разница такова:
Этот вариант выполняется 8с,
вариант с eval в цикле 10с,
вариант с include в цикле 4c.

Видимо не стоит париться.

Deleted
()
Ответ на: комментарий от Deleted

Хм, измерения неправильные. Проверка is_null() добавляет заметные задержки. Если убрать is_null, то время выполнения в разы уменьшается:

вариант выше: 4.36c,
вариант с eval в цикле: 6,70с,
вариант с include в цикле 4,30с.

Такие дела.

Deleted
()
Ответ на: комментарий от Deleted

А что ещё что странно. Измерения выше делались с запуском скриптов под сервером. А вот если запускать просто из командной строки скрипты, то результаты 0.8с, 2с и 8с соответственно.
Почему вариант с include из под командной строки выполняется дольше чем под сервером? o_O

Deleted
()
Ответ на: комментарий от Deleted

если буферизация ob_ не используется - результаты замеров могут быть неадекватными

anonymous
()
Ответ на: комментарий от Deleted

оверхед.

кому как

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

- это «самый ранний Рубенс» (c) - пока еще сложно назвать разделением на слои...

man logicless для начала

anonymous
()
Ответ на: комментарий от Deleted

А под PHP 5.5 наоборот, include быстрее. o_O

Под 5.5 просто есть opcache.

Но в цикле инклудить многократно файл не очень эффективно

Вообще-то гораздо эффективнее чем все твои потуги с eval и т.п.

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

Потому что в командной строке используется другой конфиг php.

no-such-file ★★★★★
()
Ответ на: комментарий от Deleted

чтобы потом верстальщики могли легко работать над дизайном сайта, не разбираясь в программировании

Это только в сказках бывает. А в реальности верстальщики берут psd и делают из него html+css, который потом всё те же программисты растаскивают по вьюхам.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Вообще-то гораздо эффективнее чем все твои потуги с eval и т.п.

Измерения показали что потуги с eval быстрее, но то на цикле с миллионными итерациями, на реальном коде разница будет несущественная.

Потому что в командной строке используется другой конфиг php.

Да, точно, там же специально для cli свой ini. Ну скормил я ini файл от cgi (у меня php работает как fastsgi), время выполнения теста наоборот увеличилось в два раза.

Deleted
()
Ответ на: комментарий от Deleted

Измерения показали что потуги с eval быстрее

вариант выше: 4.36c,
вариант с eval в цикле: 6,70с,
вариант с include в цикле 4,30с.

Сам себя обманул? eval парсит код каждый раз, а «вариант выше» и include берут из кэша. Причём в «варианте выше» ты ещё и делаешь ненужные телодвижения по ручному считыванию файла и его заворачивание в функцию.

Ну скормил я ini файл от cgi

Наверное надо было не скармливать, а посмотреть в чём разница. А разница, как я вангую, в том что на сервере включён opcache. Для cli он включается отдельной настройкой.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Сам себя обманул?

Блин, я уже запутался :D Просто у меня разные версии php и они ведут себя по разному. Я сейчас сравнил ini файлы, obpache выключен у обоих, но в php 5.5 был включен xdebug, а он вносит задержки. Выключил, прогнал тесты заново под сервером (fcgi):

## предварительный eval
2018-05-19 11:14:59: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.38172316551208 MEMORY: 216160 - 22110832
2018-05-19 11:15:03: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.37619209289551 MEMORY: 216160 - 22110832
2018-05-19 11:15:07: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.28086686134338 MEMORY: 216160 - 22110832

## тупо eval в цикле
2018-05-19 11:15:23: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 1.8249011039734 MEMORY: 216152 - 20108768
2018-05-19 11:15:27: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 1.7442200183868 MEMORY: 216152 - 20108768
2018-05-19 11:15:31: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 1.8100001811981 MEMORY: 216152 - 20108768

## include в цикле
2018-05-19 11:15:41: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.42358207702637 MEMORY: 216144 - 20106984
2018-05-19 11:15:45: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.41076493263245 MEMORY: 216144 - 20106984
2018-05-19 11:15:47: (mod_fastcgi.c.2673) FastCGI-stderr: __DEBUG__ TIME: 0.4132399559021 MEMORY: 216144 - 20106984

Вобщем да, тут вариант выше всё же чуть быстрее чем include, но несущественно.

include берут из кэша.

Я у них не нашёл в документации как работает кеширование для include. То есть include обращается к файлу только один раз? Я посмотрел ini файл, opcache выключен.
Ещё меня удивило отсутствие существенного влияния на потребление памяти, хотя по идее должно в случае include, ведь он судя по описанию как бы вставляет содержимое php скрипта в текущий скрипт.

Deleted
()
Ответ на: комментарий от x3al

https://secure.php.net/supported-versions.php

Я в курсе. Зависимость от другого проекта, которым я не занимаюсь, живущего на том же сервере. Как только тот проект портируют, этот тоже будет переведён на новый php.

Deleted
()
Ответ на: комментарий от Deleted

Я у них не нашёл в документации как работает кеширование для include. То есть include обращается к файлу только один раз?

Если opcache включён и файл не изменился то само-собой он не читается заново. Более того даже если opcache выключен, всё равно нет никакой нужды читать не изменившийся include файл, т.к. его скомпилированный вариант уже есть в памяти без всяких кэшей.

no-such-file ★★★★★
()
Последнее исправление: no-such-file (всего исправлений: 1)
Ответ на: комментарий от Deleted

включил opcache, не повлияло, результаты такие же.

А ты попробуй цикл вынести из php в bash, например жахни 100000 запросов через ab.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Если opcache включён и файл не изменился то само-собой он не читается заново. Более того даже если opcache выключен, всё равно нет никакой нужды читать не изменившийся include файл, т.к. его скомпилированный вариант уже есть в памяти без всяких кэшей.

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

Deleted
()
Ответ на: комментарий от no-such-file

А ты попробуй цикл вынести из php в bash, например жахни 100000 запросов через ab.

Ну это да, должно ускорить конечно...

Deleted
()
Ответ на: комментарий от anonymous

совсем не обязательно в браузере его рендерить - можно и на сервере

Я естественно выполнял не в браузере, а вгетом, выкидывая выхлоп в /dev/null.

Deleted
()
Ответ на: комментарий от Deleted

Я ожидал гораздо больший расход памяти при инклудинге одного и того же кода в цикле.

Непонятно почему? Даже если предположить, что каждый раз для чтения что-то там выделяется, то предыдущие буферы должны освобождаться.

no-such-file ★★★★★
()
Ответ на: комментарий от anonymous

man logicless для начала

А есть реальный пример? А то посмотрел я Mustache, который преподносится как logicless, но на самом деле логика там урезанная вполне себе есть, просто завуалированы. В чём смысл по сравнению с темплейтами типа django/jinja2 не понял.

Deleted
()
Ответ на: комментарий от Deleted

+100500

Абсолютно поддерживаю...

Единтвенная проблема что php слишком мощный, неопытный разработчик может запороть концепцию logicless view наговнякав бизнес-логики прямо во view...

dimzon
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.