За последние 24 часа нас посетил 22821 программист и 1227 роботов. Сейчас ищут 764 программиста ...

Буферизация вывода и быстрые шаблоны

Тема в разделе "PHP для новичков", создана пользователем 440Hz, 11 авг 2006.

  1. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    Буферизация вывода и быстрые шаблоны

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

    Как же можно упростить работу с этими кусками?

    Рассмотрим конкретный пример выводящий на странице текущую дату и время:

    index.php:
    Код (PHP):
    1. <html>
    2. <title>Моя первая страница</title>
    3. <body>
    4. <table width="100%" height="100%" border=0>
    5. <tr valign="middle">
    6. <td align="center">Сегодня: <? echo date('d.m.Y H:i'); ?></td>
    7. </tr>
    8. </table>
    9. </body>
    10. </html>
    Для начала уясним для себя очень простые вещи:

    1. Не надо смешивать HTML и PHP код
    2. PHP первичен по отношению к HTML т.к. HTML является результатом работы PHP.

    Исходя из этих правил преобразуем наш пример к нормальному виду:

    index.php:
    Код (PHP):
    1. <?
    2.  
    3. $title = 'Моя первая страница';
    4. $now = date('d.m.Y H:i');
    5.  
    6. $HTML = '<html><title>'.$title.'</title><body>
    7. <table width="100%" height="100%" border=0><tr valign="middle"><td align="center">Сегодня: '.$now.'</td></tr></table>
    8. </body></html>';
    9.  
    10. print $HTML;
    11.  
    12. ?>
    Что мы добились этим? Во первых мы разнесли логику и данные. Во вторых сделали наш код читабельным. В третьих мы немного приблизились к пониманию шаблонов.

    Давайте еще немного поработаем над нашим кодом и освоим первые шаги буферизации.

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

    Напишем первый класс и попробуем его на нашем примере.

    создадим файл buffer.php:

    buffer.php:
    Код (PHP):
    1. <?
    2.  
    3. class Buffer {
    4.     
    5.     var $buffer;
    6.  
    7.     # инициализация
    8.     function Buffer() {
    9.         $this->buffer = "";
    10.     }
    11.  
    12.     # очистка буфера
    13.     function Clear() {
    14.         $this->buffer = "";
    15.     }
    16.  
    17.     # заполнение буфера
    18.     function Send($str) {
    19.         $this->buffer .= $str;
    20.     }
    21.  
    22.     # Чтение буфера
    23.     function Read() {
    24.         $tmp = $this->buffer;
    25.         $this->Clear();
    26.         return $tmp;
    27.     }
    28.  
    29.     # выдача буфера
    30.     function Show() {
    31.         print($this->Read());
    32.     }
    33. }
    34.  
    35. ?>
    и применим этот класс к нашему примеру. Для этого еще немного поколдуем с HTML и PHP:

    index.php:
    Код (PHP):
    1. <? 
    2.  
    3. include_once('buffer.inc');
    4.  
    5. $title = 'Моя первая страница';
    6. $now = date('d.m.Y H:i');
    7.  
    8. $HTML = new Buffer();
    9.  
    10. $HTML->Send('<html><title>'.$title.'</title><body>
    11. <table width="100%" height="100%" border=0><tr valign="middle"><td align="center">Сегодня: '.$now.'</td></tr></table>
    12. </body></html>');
    13.  
    14. $HTML->Show();
    15.  
    16. ?>
    Казалось бы что мы идем по усложнению, хотя это не так. Чуть позже мы увидим, что такой подход дает большие преимущества в создании динамических страниц и генерации HTML любой сложности.

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

    Код (PHP):
    1. <?
    2.  
    3. class Buffer {
    4.  
    5. ...
    6.     # шаблонизация или парсинг
    7.     # замена и подстановка переменных
    8.     function Parse() {
    9.  
    10.  
    11.         # внимание! использование такого подхода НЕ безопасно
    12.         # модифицируйте сами этот метод, для работы с массивами.
    13.  
    14.  
    15.         reset($GLOBALS);
    16.  
    17.         while(list($name,$val) = each($GLOBALS)) {
    18.  
    19.             # выбираем только строки и числа
    20.             if(is_string($val) OR is_int($val)) {
    21.                 $this->buffer = str_replace('{'.$name.'}',$val,$this->buffer);
    22.             }
    23.         }
    24.     }
    25.  
    26. }
    27.  
    28. ?>
    и переделаем нашу страницу с учетом того, что мы теперь имеем такой мощный метод как шаблон!

    Код (PHP):
    1. <? 
    2.  
    3. include_once('buffer.inc');
    4.  
    5. $title = 'Моя первая страница';
    6. $now = date('d.m.Y H:i');
    7.  
    8. $HTML = new Buffer();
    9.  
    10. $HTML->Send('<html><title>{title}</title><body>
    11. <table width="100%" height="100%" border=0><tr valign="middle"><td align="center">Сегодня: {now}</td></tr></table>
    12. </body></html>');
    13.  
    14. $HTML->Parse();
    15. $HTML->Show();
    16.  
    17. ?>
    Что изменилось? Измелилось то, что в нашем выводимом HTML появились {title} и {now}, которые наш буфер на моменте шаблонизации заменит на $title и $now.

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

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

    Код (PHP):
    1. <?
    2.  
    3. class Buffer {
    4.  
    5. ...
    6.     # заполнение буфера из файла
    7.     function SendFile($filename) {
    8.  
    9.         if(file_exists($filename)) {
    10.             $this->buffer .= join('',file($filename));
    11.         }
    12.     }
    13.  
    14. ?>
    Теперь имея такой мощное средство как работа с файлами вынесем все лишнее и упростим наш скрипт:

    создадим файл template.html и положим туда наш HTML код.

    template.html:
    Код (Text):
    1.  
    2. <html><title>{title}</title><body>
    3. <table width="100%" height="100%" border=0><tr valign="middle"><td align="center">Сегодня: {now}</td></tr></table>
    4. </body></html>
    поправим наш код:

    Код (PHP):
    1. <? 
    2.  
    3. include_once('buffer.inc');
    4.  
    5. $title = 'Моя первая страница';
    6. $now = date('d.m.Y H:i');
    7.  
    8. $HTML = new Buffer();
    9.  
    10. $HTML->SendFile('template.html');
    11.  
    12. $HTML->Parse();
    13. $HTML->Show();
    14.  
    15. ?>
    Казалось бы это все, что можно выжать из этой странички. Но это не так. Давайте посмотрим на HTML и увидим, что и он состоит из кусков, которые будут переходить от одной страницы к другой не изменяясь.

    1. <html><title>{title}</title><body>
    2. </body></html>

    Вынесем их в отдельные файлы и посмотрим как работает наш буффер:

    header.html:
    Код (Text):
    1. <html><title>{title}</title><body>
    footer.html:
    Код (Text):
    1. </body></html>
    template.html:
    Код (Text):
    1. <table width="100%" height="100%" border=0>
    2. <tr valign="middle"><td align="center">Сегодня: {now}</td></tr>
    3. </table>
    и изменим наш код:

    Код (PHP):
    1. <? 
    2.  
    3. include_once('buffer.inc');
    4.  
    5. $title = 'Моя первая страница';
    6. $now = date('d.m.Y H:i');
    7.  
    8. $HTML = new Buffer();
    9.  
    10. $HTML->SendFile('header.html');
    11. $HTML->SendFile('template.html');
    12. $HTML->SendFile('footer.html');
    13.  
    14. $HTML->Parse();
    15. $HTML->Show();
    16.  
    17. ?>

    Что мы этим добились? Мы добились очень простой и очень важной вещи! А именно: мы разбили наш HTML на куски и умеем собирать их вместе. Это первый шаг к пониманию шаблонизации и буферизации. Теперь для изменения страницы нам не надо перепахивать код, а надо просто изменить наш шаблон!

    И последним шагом станет написание основного шаблона.

    main.html:
    Код (Text):
    1.  
    2. {HEADER}
    3. {BODY}
    4. {FOOTER}
    Добавим в коструктор немного красоты, позволяющей нам загружать файл в буффер непосредственно при создании объекта:

    Код (PHP):
    1. <?
    2.  
    3. class Buffer {
    4.     
    5.     var $buffer;
    6.  
    7.     # инициализация
    8.     function Buffer($filename='') {
    9.         $this->buffer = "";
    10.         if(!empty($filename))
    11.             $this->SendFile($filename);
    12.     }
    13.  
    14. ...
    15.  
    16. }
    17.  
    18. ?>

    перепишем наш код к окончательному варианту:

    Код (PHP):
    1. <? 
    2.  
    3.  
    4. include_once('./buffer.inc');
    5.  
    6. $title = 'Моя первая страница';
    7. $now = date('d.m.Y H:i.s');
    8.  
    9. // загружаем header И устанавливаем title
    10. $HEADER = new Buffer('./header.html');
    11. $HEADER->Parse();
    12. $HEADER = $HEADER->Read();
    13.  
    14. // загружаем body и устанавливаем now
    15. $BODY   = new Buffer('./template.html');
    16. $BODY->Parse();
    17. $BODY   = $BODY->Read();
    18.  
    19. // загружаем footer
    20. $FOOTER = new Buffer('./footer.html');
    21. $FOOTER = $FOOTER->Read();
    22.  
    23. // загружаем основной шаблон и выводим его
    24. $HTML = new Buffer('./main.html');
    25. $HTML->Parse();
    26. $HTML->Show();
    27.  
    28. ?>
    Теперь создание новых страничек будет занимать у нас намного меньше времени потому, что основные части страниц у нас уже готовы и мы будем теперь уделать больше времени наполнению этих страниц, а не тупому копированию.

    Все это видно на <!-- m --><a class="postlink" href="http://440hz.ru/trash/buffer/">http://440hz.ru/trash/buffer/</a><!-- m -->
    Скачать архив можно <!-- m --><a class="postlink" href="http://440hz.ru/trash/buffer/buffer.tar.gz">http://440hz.ru/trash/buffer/buffer.tar.gz</a><!-- m -->
     
  2. svk

    svk Активный пользователь

    С нами с:
    7 авг 2006
    Сообщения:
    506
    Симпатии:
    0
    Адрес:
    NetByNet
    респект, но в небольших проектах легче обходиться include('header.php');
     
  3. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    я в начале так и написал.
    и я говорю о принципиальном подходе и понимании.
     
  4. svk

    svk Активный пользователь

    С нами с:
    7 авг 2006
    Сообщения:
    506
    Симпатии:
    0
    Адрес:
    NetByNet
    440hz
    при принципиальном подходе твой код потенциально опасен. Пользователь сможет прочесть любую переменную кода оставив где-нить сообщение содержащее {dbpass}. при некоторых условиях оно заменится на пароль от твоей БД (в случае если он там хранится естественно):)
     
  5. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    svk

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

    но в этом случае - да. такой код НЕ безопасен.
     
  6. svk

    svk Активный пользователь

    С нами с:
    7 авг 2006
    Сообщения:
    506
    Симпатии:
    0
    Адрес:
    NetByNet
    440hz
    именно с массивом обычно и делают. пример - код тогоже phpBB. в нем это $template->assing_vars() если не ошибаюсь.

    кстати лучше вынеси про небезопасность кода в начало статьи, думаю тут найдется много горе-"программистов" которые будут использовать твой класс в своих страницах
     
  7. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    svk

    поправил исходник. пусть сами модифицируют.
     
  8. X-Cray

    X-Cray Активный пользователь

    С нами с:
    10 июл 2006
    Сообщения:
    255
    Симпатии:
    0
    Адрес:
    Москва
    Пусть используют, а сюда ссылочки на страницы с получившимися лазейками в безопасности ;)
     
  9. NIKO

    NIKO Активный пользователь

    С нами с:
    10 фев 2006
    Сообщения:
    655
    Симпатии:
    0
    Адрес:
    Armenia
    440hz
    А зачем сам урок то ?Просто так или причина есть?
     
  10. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    NIKO

    чловек просил в другом топике показать как работают шаблоны и что это такое.
     
  11. NIKO

    NIKO Активный пользователь

    С нами с:
    10 фев 2006
    Сообщения:
    655
    Симпатии:
    0
    Адрес:
    Armenia
    А понятно!
    Не понятно одно как человек не знает как работает шаблон но знает ПХП?
     
  12. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    ты же юзаешь винды не зная как они устроены.
    8)
     
  13. D.Lans

    D.Lans Активный пользователь

    С нами с:
    31 июл 2006
    Сообщения:
    345
    Симпатии:
    0
    440hz респект тебе огромный за твою статью!
    В мемориз ее и в закладки!

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

    На одном сайте я нашел способ как это сделать:

    Код (Text):
    1.  
    2. <html><body>
    3. <table>
    4. <%loop {array} as {var}%>
    5. <tr>
    6.    <td>{var:news_title}<br>{var:news_text}</td>
    7. </tr>
    8. <%loop end%>
    9. </table>
    10. </body></html>
    Будь добр, подскажи, как это осуществить!
    Мои познания, пока что, к сожалению, не позволяют мне самому этого сделать. :)
     
  14. svk

    svk Активный пользователь

    С нами с:
    7 авг 2006
    Сообщения:
    506
    Симпатии:
    0
    Адрес:
    NetByNet
    я для этого использую блоки...
    PHP:
    1.  
    2. <?php
    3. function blocks($telo, $block) {
    4. $pos1 = strpos ($telo, "<!-- BEGIN $block -->");
    5. $pos2 = strpos ($telo, "<!-- END $block -->", $pos1);
    6.  
    7. $do = substr($telo, 0, $pos1);
    8. $posle = substr($telo, $pos2+(13+strlen($block)), strlen($telo));
    9. $tt = substr($telo, $pos1, $pos2-$pos1);
    10.  
    11. return $tt;
    12. }
    13. ?>
    14.  
     
  15. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    Красиво, у меня тоже есть статья (набросок статьи) на эту тему, однако она требует сильной редакторской правки, (грамматика у меня хромает) могу дать ссылку в личку кому интересно...
     
  16. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    я в свое время отказался от динамики ибо PHP и так шаблонизатор, а навешивать на шаблон еще и логику - это противоречит самому понятию быстрого шаблона.

    практика показала, что того, что есть с избытком хватает. поверь на слово.
     
  17. D.Lans

    D.Lans Активный пользователь

    С нами с:
    31 июл 2006
    Сообщения:
    345
    Симпатии:
    0
    Но вышепреведнный мною пример мне кажется весьма прост и понятен.

    Вот хочу его реализовать, думаю этого будет достаточно.
     
  18. svk

    svk Активный пользователь

    С нами с:
    7 авг 2006
    Сообщения:
    506
    Симпатии:
    0
    Адрес:
    NetByNet
    у меня в шаблонах реализована логика авторизации/неавторизации пользователя, статус пользователя (админ/не админ). Ну и блоки канешно.... и не скажу что противоречит
     
  19. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда
    зашли-ка - покумекаем вместе?
     
  20. NIKO

    NIKO Активный пользователь

    С нами с:
    10 фев 2006
    Сообщения:
    655
    Симпатии:
    0
    Адрес:
    Armenia
    440hz
    в твоем пример шаблон должен быть на .php?
     
  21. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    D.Lans
    А чем родной РНР синитаксис сложен ?

    Код (Text):
    1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
    2. <html>
    3.    <head>
    4.       <title><?=$title?></title>
    5.       <meta name="description" content="<?=$description?>">
    6.    </head>
    7.    <body>
    8.       <h1><?=$title?></h1>
    9. <?foreach($news as $item):?>
    10.       <h2><?=$item['title']?></h2>
    11.       <p><?=$item['text']?></p>
    12. <?endforeach?>
    13.    </body>
    14. </html>
    По моему не сложнее чем у тебя :)
     
  22. 440Hz

    440Hz Старожил
    Команда форума Модератор

    С нами с:
    21 дек 2012
    Сообщения:
    8.003
    Симпатии:
    1
    Адрес:
    Оттуда

    вот я все о том же, что выносить логику в шаблоны можно, но не нужно.

    8)
     
  23. simpson

    simpson Активный пользователь

    С нами с:
    11 фев 2006
    Сообщения:
    1.650
    Симпатии:
    0
    Адрес:
    Санкт-Петербург
    NIKO
    да хоть template.blah :) ты файл все равно читаешь в строку.
     
  24. NIKO

    NIKO Активный пользователь

    С нами с:
    10 фев 2006
    Сообщения:
    655
    Симпатии:
    0
    Адрес:
    Armenia
    simpson
    А в template.blah че надо писать?
     
  25. simpson

    simpson Активный пользователь

    С нами с:
    11 фев 2006
    Сообщения:
    1.650
    Симпатии:
    0
    Адрес:
    Санкт-Петербург
    ничего не надо туда писать. это один из примеров названия файла шаблона. в первом посте он называется "template.html".