Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Кто-нибудь писал шаблонизатор?

365
24 августа 2005 года
MasterSID
230 / / 23.02.2003
Постигаю все прелести ООП, всвязи с этим решил написать свой простенький шаблонизатор.

Так вот мне интересно кто как решал проблему с тем, что перемнные типа {USER_NAME}, используемые в шаблонах, могут присутствовать в контенте парсируемой информации и будут заменены шаблонизатором на значение данной переменой, хотя этого бы как раз и не хотелось :)
2.2K
24 августа 2005 года
Web-master
113 / / 23.12.2004
У меня сделано примерно так:

index.php :
Код:
<?
// Тут пропущено подключение к бд..
$templ=implode("",file("tmplt/main.php"));

// Тут пропущены запросы в бд

$templ=str_replace("@content@", $content, $templ);
$templ=str_replace("@login_form@", $login_form, $templ);
$templ=str_replace("@site@", $site, $templ);
$templ=str_replace("@profile_info@", $profile_info, $templ);
print $templ;
mysql_close();
flush();
?>


tmplt/main.php:
 
Код:
просто html
с тегами например
@content@
@login_form@
@site@
и т.д.

Работает быстро.
365
24 августа 2005 года
MasterSID
230 / / 23.02.2003
А теперь представим, что в перменной $content имеется запись @login_form@ - получится, что она запарсится дважды. вот это я и хочу обойти. Это может создать проблемы, если использвать данный шаблон для вывода данных, представленных пользователем. Ведь он может ввести любую переменную в текст и тем самым нарушить логику работы скрипта и даже осуществить взлом.

Кстати, мне кажется, что данная конструкция:
$templ=implode("",file("tmplt/main.php"));
будет работать гораздо медленнее, чем
file_get_contents("tmplt/main.php");
или
$filename = "tmplt/main.php";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));
2.2K
24 августа 2005 года
Web-master
113 / / 23.12.2004
Цитата:
Originally posted by MasterSID
А теперь представим, что в перменной $content имеется запись @login_form@ - получится, что она запарсится дважды.



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

299
24 августа 2005 года
3D Bob
885 / / 18.04.2005
Цитата:
Originally posted by MasterSID
А теперь представим, что в перменной $content имеется запись @login_form@ - получится, что она запарсится дважды. вот это я и хочу обойти. Это может создать проблемы, если использвать данный шаблон для вывода данных, представленных пользователем. Ведь он может ввести любую переменную в текст и тем самым нарушить логику работы скрипта и даже осуществить взлом.

Кстати, мне кажется, что данная конструкция:
$templ=implode("",file("tmplt/main.php"));
будет работать гораздо медленнее, чем
file_get_contents("tmplt/main.php");
или
$filename = "tmplt/main.php";
$handle = fopen($filename, "r");
$contents = fread($handle, filesize($filename));


Тебе нужен ограничитель. Например

<login_form>

А данные которые присыалет пользователь обрабатывать через htmlspeshial.... как-то так. кароче функция которая заменят <> на ХТМЛ мнемоники.

А вообще, для пользователя угадать твой
@login_form@ равносильно угадать пароль счета в банке США=)))))))

Дыры всегда были есть и будут, но о них по сути должен знать только разработчик.
ТОЛЬКО ЭТО ВСЁ К ООП пока что и близко отношения не имеет)

338
24 августа 2005 года
chigevara
529 / / 29.09.2003
Цитата:
Originally posted by 3D Bob
Тобрабатывать через htmlspeshial.... как-то так. кароче функция которая заменят <> на ХТМЛ мнемоники.


htmlspecialchars();
Вообще то такие вещи надо знать по памяти. Тогда появится надежда что дыр все таки не будет. А миф о том что надежных приложений не бывает - не более чем миф.

365
24 августа 2005 года
MasterSID
230 / / 23.02.2003
хм, интереное решение, но все же имеет недостаток - браузер понимает эту конструкцию как тэг и не выводит его, к томуже этот вариант плох тем, что не дает использовать схожие с тэгами названия для переменных. Можно, конечно использовать что-то вроде <- sad ->, но мне нужен более универсальный метод. К тому же уже стало интересно - как отловить в тексте то место, куда нужно парсить

Веб-мастер, вот пример твоего когда, когда он парсит дважды:

$templ="@content@
@login_form@";
$content='content@login_form@';
$login_form='login_form';

$templ=str_replace("@content@", $content, $templ);
$templ=str_replace("@login_form@", $login_form, $templ);
print $templ;
2.2K
24 августа 2005 года
Web-master
113 / / 23.12.2004
Да, сорри, попробывал другой тег вида @tag@ парсится дважды. Надо что-то придумать.

Появилась идея сделать что-то типа этого:
 
Код:
$npl="";
$npl.=str_replace("@content@", $content, $templ);
$npl.=str_replace("@login_form@", $login_form, $templ);
print $npl;


Но, почему то, выводится в браузер, так, что кажется что это все было обработано циклом.
365
24 августа 2005 года
MasterSID
230 / / 23.02.2003
Код:
<?
// Определяем какие переменные используем в шаблоне
$vars[]='content';
$vars[]='login_form';

// Загружаем содержимое шаблона
$templ="@content@
@login_form@"
;

// Заменяем переменные шаблона с @var@ на $var
foreach($vars as $k=>$v){
    $templ=str_replace('@'.$v.'@', '$'.$v, $templ);
}

// Определяем содержимое переменных шаблона
$content='content';
$login_form='login_form';

// Делаем преобразование
eval("\$output = \"$templ\";");

// Выводим результат
echo $output;

?>

Вот - придумал такое решение. Один только косяк - если в тексте шаблона попадется что-то вроде $content - то оно будет заменено на его содержимое. Тоже не очень приятный момент.
253
24 августа 2005 года
Proger_XP
1.5K / / 07.08.2004
Народ, что-то я не понял
Вам надо просто что бы один шаблон не заменялся 2 раза?
З.Ы: Юзайте RegEx. Хотя работает медленнее
365
25 августа 2005 года
MasterSID
230 / / 23.02.2003
Нам нужно сделать так, чтобы парсились только те переменные, которые содержатся в файлах шаблонов и не парсились те, конструкции, которые выглядят как эти переменные, но содержаться в тексте парируемых переменных, т.е в подставляемых в шаблон данных.

Как эту задачу решить?
365
25 августа 2005 года
MasterSID
230 / / 23.02.2003
Вот еще вариант, только правилен ли он с т.з. безопасности скрипта?

$template='{TITLE}
{BODY}';
$tags["{TITLE}"]="Заголовок {BODY}{TITLE}";
$tags["{BODY}"]="Тело странички{BODY}{TITLE}";
echo strtr($template, $tags);
271
25 августа 2005 года
MrXaK
721 / / 31.12.2002
$templ = preg_replace("\@content\@", $content, $templ, 1); и заменять в обратном порядке (снизу вверх)...
365
25 августа 2005 года
MasterSID
230 / / 23.02.2003
Цитата:
Originally posted by Mr.Hacker
$templ = preg_replace("\@content\@", $content, $templ, 1); и заменять в обратном порядке (снизу вверх)...



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

2.2K
26 августа 2005 года
Web-master
113 / / 23.12.2004
Цитата:
Originally posted by MasterSID
Вот еще вариант, только правилен ли он с т.з. безопасности скрипта?

$template='{TITLE}
{BODY}';
$tags["{TITLE}"]="Заголовок {BODY}{TITLE}";
$tags["{BODY}"]="Тело странички{BODY}{TITLE}";
echo strtr($template, $tags);



Хороший и верный вариант, респект тебе ! :)

287
26 августа 2005 года
Shiizoo
958 / / 14.03.2004
Если я правильно понял задачу то все сводится к такому коду:

Код:
$target = '';
$tag = '';
$chr = '';
$src=preg_split('//','{TITLE}
{BODY}');
$state = 0;
$def = 'Undefined';

$tags = array(
    'TITLE' => "Заголовок {BODY}{TITLE}",
    'BODY'  => "Тело странички{BODY}{TITLE}"
);

for (;($chr = array_shift($src)) !== null;) {
    switch ($chr) {
        case '{':
            $state = 1;
            break;
        case '}':
            if ($state == 1) {
                $target .= isset($tags[$tag]) ? $tags[$tag] : $def;
                $state = 0;
                $tag = '';
            }
            break;
        default:
            if ($state == 1) $tag .= $chr;
            elseif ($state == 0) $target .= $chr;
            break;
    }
}

echo $target;


А если так, то re по-моему работает быстрее (ну или они равносильны c этим вариантом). Мне больше нравятся варианты как выше, imho, ближе к телу, больше контроля над ситуацией. Можно довести до ума, чтоб реагировать не на односимвольный экранизатор тэгов, а к примеру на те же .
287
26 августа 2005 года
Shiizoo
958 / / 14.03.2004
А вообще лучше оформить это бинарную либу под php и юзать там, где это разрешается;)
2.2K
26 августа 2005 года
Web-master
113 / / 23.12.2004
Shiizoo, громоздкий код =\ А то, что написал МастерСИД быстро работает и всех устраивает. :)
287
26 августа 2005 года
Shiizoo
958 / / 14.03.2004
Естессна проще закодить
 
Код:
<?php exit(0); ?>
чем
 
Код:
<?php if (isset($_POST['user'])) echo 'Hola ' . $_POST['user'] . '!'; ?>
.

если я не ошибаюсь, strstr не поможет тебе проделать то же самое, если параметры даёт класс способом типа '$this->param($name)', и не выставит дефолтные значения при отсутствии необходимого параметра в шаблонизаторе, не ругнется на синтаксис и т.п. и т.д.;) Я же не предлагаю забивать гвозди микроскопом, если задача настолько проста, можно и вовсе ограничиться таким примером (копирайт есть, но кто хозяин кода непомню)
Код:
<?php
class Template {
    var $vars; /// Holds all the template variables

    /**
     * Constructor
     *
     * @param $file string the file name you want to load
     */
    function Template($file = null) {
        $this->file = $file;
    }

    /**
     * Set a template variable.
     */
    function set($name, $value) {
        $this->vars[$name] = is_object($value) ? $value->fetch() : $value;
    }

    /**
     * Open, parse, and return the template file.
     *
     * @param $file string the template file name
     */
    function fetch($file = null) {
        if(!$file) $file = $this->file;

        extract($this->vars);          // Extract the vars to local namespace
        ob_start();                    // Start output buffering
        include($file);                // Include the file
        $contents = ob_get_contents(); // Get the contents of the buffer
        ob_end_clean();                // End buffering and discard
        return $contents;              // Return the contents
    }
}
?>


И писать в шаблоных обычный php, что будет работать быстрее практически любого другого шаблонизатора;)
287
26 августа 2005 года
Shiizoo
958 / / 14.03.2004
И кстати, если я не ошибаюсь, чем больше будет текст (или количество итераций поиска), тем меньше будет проигрывать мой пример примеру MasterSID'а. Потому что strstr ищет содержимое в контенте, а мой пример ничего не ищет, а лишь филтьрует. И выходит, что strstr, во-первых, будет тратить лишние ресурсы там где не все содержимое $tags будет присутствовать в контенте да и вообще всегда, потому что врезать одну строку в середину (ну или левее/правее) другой строки более накладно, чем просто расширять строку как это сделал я. Ну это все конечно не факт, а просто умозаключение, надо проверить, глянуть сырцы php, затестить. Разбудило интерес;)

added:
Кажется промахнулся я со своими заключениями, во всяком случае по поводу врезания, писал о strstr, а думал в этот момент о str_replace. Может кто поделится инфой, если уже таковая у кого имеется, по поводу работы .= и strstr?

added:
и кстати можно было бы как вариант вынести это в функцию (мой вариант), и принимать ссылку на контент, и по значению какого-нибудь флага решать, создавать массив символов для прогона на основе контента не трогая его (контент) или же убивать контент. Так можно сэкономить RAM, т.к. strstr не меняет "жертву". Ну, это тоже в теории=)
300
26 августа 2005 года
ReDrum
689 / / 20.04.2000
Гм, я считал что таких приблуд для php предостаточно. И все уже давно забили на писанину своих собственных движков ;) Ан нет - ошибался ;)
299
26 августа 2005 года
3D Bob
885 / / 18.04.2005
Цитата:
Originally posted by chigevara
htmlspecialchars();
Вообще то такие вещи надо знать по памяти. Тогда появится надежда что дыр все таки не будет. А миф о том что надежных приложений не бывает - не более чем миф.



Если программируешь ежедневно на пхп.
И если работа связана с пхп.;) Когда-то и я помнил все функции.
А я знаю его основы. Я писал на нем достаточно крупные проекты.
Я рад что я знаю его основы, я не хачу на нем писать крупные проекты.

Ребят а вам не кажется что хранит всю страничку в переменной обрабатывать всю страничку на каждый код, и прочее что это является ОГРОМНОЙ УЧТЕСКОЙ ПАМЯТИ и РЕСУРСОВ? Стоит задуматься что изначально это неверный способ.

Скажу свой алгаритм шалонизатора.
Функцции герерирующие разные части текста(будь то имаги, блоки, заголовок), в зависимости от странички и передаваемых в функции параметров генерировать соостветсвующие коды. А уж в самих функциях сделать чтение из внещних ресурсов.

Грубо говоря

 
Код:
<?php
function_title($url);
function_center($url)
function_footer($url);
?>


То есть я говорю что вместо этого
Цитата:

$templ="@content@
@login_form@";


Сделать вот это

$templ=function_content()."
".function_login_form();

287
27 августа 2005 года
Shiizoo
958 / / 14.03.2004
А с чего ты решил что все хранится в переменной и парсится для каждого отдельного запроса?:) Обсуждаем способ подстановки значений параметрам шаблонов, а уж как это кто завернет это уже личное дело каждого отдельно взятого индивидума.

2ReDrum: ну так жизнь в движении;) написано-то много приблуд, но и качества они приблудного, а все хорошее - дорого, или вовсе не существует;)
253
28 августа 2005 года
Proger_XP
1.5K / / 07.08.2004
3D Bob в чем-то прав
Я например делал и делаю так
<?php
$pageid=PageID;
require("Header.php"); ?>
Здесь весь изменяемый текст страницы
Это любая страница которую смотрит юзер
В Header.php написано например так:
<?php
function ShutDown {
include("Footer.php");
}
register_shutdown_function("ShutDown");
// потом вывод Header'ов, подключение библиотек и т.д
<!DOCTYPE ....>
<html>
<head>
<title> <?=GetPageTitle();?> </title>
</head>
<body>
<table>
<tr>
<td>
....
</td>
</tr>
<tr>
<td>
В Footer.php конец страницы, например, так:
</td>
</tr>
</table>
<?php
// завершающие операции, например, закрытие файлов
?>
Заголовок страницы и т.д выясняются по $pageid или по $HTTP_SERVER_VARS['REQUEST_URI'] а потом задается $pageid
Вот так примерно
365
29 августа 2005 года
MasterSID
230 / / 23.02.2003
to: Shiizoo
Я провел тут небольшой тест скорости работы наших вариантов парсера шаблонов. Результаты такие:
$n - количество левых символов в шаблоне, т.е. фактически размер файла шаблона
В процентах показывается время выполнения скрипта, т.е. нагрузка на сервер.

при $n=10
Shiizoo: 91%
MasterSID: 9%

при $n=100
Shiizoo: 95%
MasterSID: 5%

при $n=1000
Shiizoo: 99%
MasterSID: 1%

при $n=10000
Shiizoo: 100%
MasterSID: 0%

Самое интересное, что при $n==10000 твой вариант выполнялся 4.96485686302 секунд, а мой 0.00316095352173
Стандартные функции php работают намного быстрее собственных

Кстати, мне кажется for (;($chr = array_shift($src)) !== null;) можно заменить на while(($chr = array_shift($src)) !== null)

код скрипта-тестера:
Код:
<?
// Функция для расчета Microtime (взята из мануала)
function microtime_float()
{
    list($usec, $sec) = explode(" ", microtime());
    return ((float)$usec + (float)$sec);
}

// Английский алфавит для генерации в шаблон случайного набора символов
$a='abcdefghijklmnopqrstuvwxyz';

// Базовый текст шаблона
$template='{TITLE}
{BODY}'
;

// Задает сколько левых символов нужно добавить в шаблон
$n=10;

// Генерируем левые символы на основе английского алфавита.
for($i=0;$i<$n;$i++){
    $template.=$a[rand(0, (strlen($a)-1))];
}

// Задаем значения переменным шаблона
$title='Заголовок {TITLE}{BODY}';
$body="Тело странички{TITLE}{BODY}";


$time_start = microtime_float();
//////////////////////////////////////////////////////////////////////////////
// Начало варианта Shiizoo
//////////////////////////////////////////////////////////////////////////////

$target = '';
$tag = '';
$chr = '';
$src=preg_split('//',$template);
$state = 0;
$def = 'Undefined';

$tags = array(
    'TITLE' => $title,
    'BODY'  => $body
);

for (;($chr = array_shift($src)) !== null;) {
    switch ($chr) {
        case '{':
            $state = 1;
            break;
        case '}':
            if ($state == 1) {
                $target .= isset($tags[$tag]) ? $tags[$tag] : $def;
                $state = 0;
                $tag = '';
            }
            break;
        default:
            if ($state == 1) $tag .= $chr;
            elseif ($state == 0) $target .= $chr;
            break;
    }
}
echo $target;
//////////////////////////////////////////////////////////////////////////////
// Конец варианта Shiizoo
//////////////////////////////////////////////////////////////////////////////
$time_end = microtime_float();

// Время выполнения варианта Shiizoo
$time_v1=$time_end-$time_start;


echo '<hr>';


$time_start = microtime_float();
//////////////////////////////////////////////////////////////////////////////
// Начало варианта MasterSID'a
//////////////////////////////////////////////////////////////////////////////
$tags["{TITLE}"]=$title;
$tags["{BODY}"]=$body;
echo strtr($template, $tags);
//////////////////////////////////////////////////////////////////////////////
// Конец варианта MasterSID'a
//////////////////////////////////////////////////////////////////////////////
$time_end = microtime_float();

// Время выполнения варианта MasterSID
$time_v2=$time_end-$time_start;

// Общее время работы двух вариантов
$total=$time_v1+$time_v2;

// Вывод расчетов
echo '<hr>Shiizoo: '.round($time_v1*100/$total).'%
MasterSID: '
.round($time_v2*100/$total).'%';
?>
365
29 августа 2005 года
MasterSID
230 / / 23.02.2003
In addition to my previous post, у меня не strstr, а strtr
287
29 августа 2005 года
Shiizoo
958 / / 14.03.2004
;) Естественно компиленный код будет рботать быстрее.

Цитата:
Shiizoo: 91%
MasterSID: 9%

при $n=100
Shiizoo: 95%
MasterSID: 5%

при $n=1000
Shiizoo: 99%
MasterSID: 1%

при $n=10000
Shiizoo: 100%
MasterSID: 0%



Это отчасти результат разбиения строки на символы preg_split'ом. Если заменить на прямое обращение к символу строки str{n}, будет примерно 20 на 80. Если умело заоптимизировать код то возможно он будет выдавать приемлимые результаты. А смысл как я уже писал есть&#151; полноценный контроль над обработкой шаблона без необходимости делать callback'и т.п., что опять же сожрало бы лишние ресурсы.

287
29 августа 2005 года
Shiizoo
958 / / 14.03.2004
Цитата:
Originally posted by Shiizoo
;) Естественно компиленный код будет рботать быстрее.



Это отчасти результат разбиения строки на символы preg_split'ом. Если заменить на прямое обращение к символу строки str{n}, будет примерно 20 на 80. Если умело заоптимизировать код то возможно он будет выдавать приемлимые результаты. А смысл как я уже писал есть&#151; полноценный контроль над обработкой шаблона без необходимости делать callback'и т.п., что опять же сожрало бы лишние ресурсы.



added:

Ой нагнал, 5 и 95 =)

6.5K
13 мая 2006 года
viTTas
20 / / 02.04.2005
Здравствуйте!
Я нашел быстрое решение!
Вот код:
 
Код:
function parse_template ($filename, $replaces) {
  $template = file_get_contents ($filename);
  foreach ($replaces as $search => $replace) {
    $replace = str_replace ("%", "%\\\\", $replace);
    $template = str_replace ("%" . $search . "%", $replace, $template);
  }
  $template = str_replace ("%\\\\", "%", $template);
  return ($template);
}
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог