2007-05-21 03:34:53 +04:00
|
|
|
|
#!/usr/bin/perl
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# Новая версия шаблонного движка VMX::Template!
|
|
|
|
|
|
|
|
|
|
# Уйти от assign_vars(), assign_block_vars()
|
|
|
|
|
# Передавать, как и в обычных движках, просто
|
|
|
|
|
# $hash =
|
|
|
|
|
# {
|
|
|
|
|
# key => "value",
|
|
|
|
|
# block =>
|
|
|
|
|
# [
|
|
|
|
|
# {
|
|
|
|
|
# key => "value",
|
|
|
|
|
# },
|
|
|
|
|
# {
|
|
|
|
|
# key => "value",
|
|
|
|
|
# },
|
|
|
|
|
# ],
|
|
|
|
|
# }
|
|
|
|
|
|
|
|
|
|
# Вне BEGIN - {block} будет иметь значение ARRAY(0x...) т.е. массив всех итераций
|
|
|
|
|
# А {block.0} будет иметь значение HASH(0x...) т.е. первую итерацию
|
|
|
|
|
|
|
|
|
|
# <!-- BEGIN block -->
|
|
|
|
|
# Внутри BEGIN - {block} будет иметь значение HASH(0x...) т.е. уже значение конкретной итерации
|
|
|
|
|
# А {block.#} будет иметь значение - номер текущей итерации
|
|
|
|
|
# {block.var}
|
|
|
|
|
# <!-- END block -->
|
|
|
|
|
# На <!-- END другоеимя --> ругнётся, ибо нефиг.
|
|
|
|
|
# Если block в хеше данных - не массив, а хешреф - значит, итерация только одна.
|
|
|
|
|
|
|
|
|
|
# Функции нескольких аргументов
|
|
|
|
|
# <!-- function(block.key, 0, "abc") -->
|
|
|
|
|
|
|
|
|
|
# Функции одного аргумента
|
|
|
|
|
# <!-- function(block.key) -->
|
|
|
|
|
# <!-- function block.key -->
|
|
|
|
|
# {block.key/L}
|
|
|
|
|
# {L block.key}
|
|
|
|
|
|
|
|
|
|
# IF -
|
|
|
|
|
# <!-- IF function(block.key) --><!-- ELSEIF ... --><!-- END -->
|
|
|
|
|
# <!-- IF NOT block.key -->...<!-- END -->
|
|
|
|
|
|
|
|
|
|
# Операторов НЕТ, только функции
|
|
|
|
|
# <!-- IF OR(function(block.key1),AND(block.key2,block.key3)) -->
|
|
|
|
|
|
|
|
|
|
# Есть SET
|
|
|
|
|
# <!-- SET block.key -->...<!-- END -->
|
|
|
|
|
# или
|
|
|
|
|
# <!-- SET block.key = ... -->
|
|
|
|
|
|
|
|
|
|
# Функции
|
|
|
|
|
# OR, AND, NOT
|
|
|
|
|
# EVEN, ODD
|
|
|
|
|
# INT, ADD, MUL, DIV, MOD
|
|
|
|
|
# EQ, SEQ, GT, LT, GE, LE, SGT, SLT, SGE, SLE (== eq > < >= <= gt lt ge le)
|
|
|
|
|
# CONCAT, JOIN, SPLIT, LC=LOWER=LOWERCASE, UC=UPPER=UPPERCASE
|
|
|
|
|
# L=TRANSLATE, LZ=TRANSLATE_NULL
|
|
|
|
|
# S=HTML, T=STRIP, H=STRIP_UNSAFE
|
|
|
|
|
# Q=QUOTE, REQUOTE=RE_QUOTE=PREG_QUOTE
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
|
|
|
|
package VMX::Template;
|
|
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
|
use VMX::Common qw(:all);
|
2009-03-13 20:47:24 +03:00
|
|
|
|
use Digest::MD5 qw(md5_hex);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
use Hash::Merge;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my $mtimes = {}; # время изменения файлов
|
|
|
|
|
my $uncompiled_code = {}; # нескомпилированный код
|
|
|
|
|
my $compiled_code = {}; # скомпилированный код (sub'ы)
|
|
|
|
|
my $langhashes = {}; # хеши ленгпаков
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $assigncache = {}; # кэш eval'ов присвоений
|
2007-10-28 19:16:50 +03:00
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Конструктор
|
|
|
|
|
# $obj = new VMX::Template, %params
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub new
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $class = shift;
|
|
|
|
|
$class = ref ($class) || $class;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
my $self =
|
|
|
|
|
{
|
2007-10-27 23:44:31 +04:00
|
|
|
|
root => '.', # каталог с шаблонами
|
2009-01-06 01:39:48 +03:00
|
|
|
|
reload => 1, # если 0, шаблоны не будут перечитываться с диска, и вызовов stat() происходить не будет
|
2007-10-28 19:16:50 +03:00
|
|
|
|
wrapper => undef, # фильтр, вызываемый перед выдачей результата parse
|
2009-01-06 01:39:48 +03:00
|
|
|
|
tpldata => {}, # сюда будут сохранены: данные
|
2009-02-01 19:37:33 +03:00
|
|
|
|
lang => {}, # ~ : языковые данные
|
2009-03-13 20:47:24 +03:00
|
|
|
|
cache_dir => undef, # необязательный кэш, ускоряющий работу только в случае частых инициализаций интерпретатора
|
2009-01-03 14:56:08 +03:00
|
|
|
|
use_utf8 => undef, # шаблоны в UTF-8 и с флагом UTF-8
|
2009-01-06 01:39:48 +03:00
|
|
|
|
@_,
|
2007-10-27 23:44:31 +04:00
|
|
|
|
};
|
2009-03-13 20:47:24 +03:00
|
|
|
|
$self->{cache_dir} =~ s!/*$!/!so if $self->{cache_dir};
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$self->{root} =~ s!/*$!/!so;
|
2007-10-27 23:44:31 +04:00
|
|
|
|
bless $self, $class;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция задаёт имена файлов для хэндлов
|
|
|
|
|
# $obj->set_filenames (handle1 => 'template1.tpl', handle2 => \'{CODE} - Template code', ...)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub set_filenames
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
|
|
|
|
my %fns = @_;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
while (my ($k, $v) = each %fns)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-01-06 01:39:48 +03:00
|
|
|
|
if (ref $v && ref $v ne 'SCALAR')
|
|
|
|
|
{
|
|
|
|
|
$v = "$v";
|
|
|
|
|
}
|
|
|
|
|
$self->{filenames}->{$k} = $v;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция загружает файлы переводов (внутри хеши)
|
|
|
|
|
# $obj->load_lang ($filename, $filename, ...);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub load_lang
|
|
|
|
|
{
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $self = shift;
|
|
|
|
|
return $self->load_lang_hashes(map
|
2009-01-06 01:39:48 +03:00
|
|
|
|
{
|
|
|
|
|
my $load = 0;
|
|
|
|
|
my $mtime;
|
|
|
|
|
if (!defined($mtimes->{$_}) || $self->{reload})
|
|
|
|
|
{
|
|
|
|
|
$mtime = [ stat($_) ] -> [ 9 ];
|
|
|
|
|
$load = 1 if !defined($mtimes->{$_}) || $mtime > $mtimes->{$_};
|
|
|
|
|
}
|
|
|
|
|
if ($load)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2007-11-07 03:12:06 +03:00
|
|
|
|
$mtimes->{$_} = $mtime;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$langhashes->{$_} = do $_;
|
2007-11-07 03:12:06 +03:00
|
|
|
|
}
|
2008-08-15 21:31:24 +04:00
|
|
|
|
$langhashes->{$_};
|
2007-11-07 03:12:06 +03:00
|
|
|
|
} @_);
|
2007-09-12 00:56:37 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция загружает хеши переводов
|
|
|
|
|
# $obj->load_lang_hashes ($hash, $hash, ...);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub load_lang_hashes
|
|
|
|
|
{
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $self = shift;
|
|
|
|
|
my $i = 0;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
Hash::Merge::set_behavior('RIGHT_PRECEDENT');
|
|
|
|
|
$self->{lang} = Hash::Merge::merge ($self->{lang}, $_) foreach @_;
|
2009-02-01 19:37:33 +03:00
|
|
|
|
return $i;
|
2007-09-11 02:46:55 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция уничтожает данные шаблона
|
|
|
|
|
# $obj->clear()
|
2008-10-16 19:27:36 +04:00
|
|
|
|
sub clear
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-01-06 01:39:48 +03:00
|
|
|
|
shift->{tpldata} = {};
|
2007-05-21 03:34:53 +04:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция загружает, компилирует и возвращает результат для хэндла
|
|
|
|
|
# $obj->parse('handle')
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub parse
|
|
|
|
|
{
|
2007-06-02 02:30:09 +04:00
|
|
|
|
my $self = shift;
|
2008-10-18 17:56:52 +04:00
|
|
|
|
my ($handle) = @_;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my $fn = $self->{filenames}->{$handle};
|
|
|
|
|
my $textref;
|
|
|
|
|
unless (ref $fn)
|
|
|
|
|
{
|
|
|
|
|
die "[Template] unknown handle '$handle'"
|
|
|
|
|
unless $fn;
|
|
|
|
|
$fn = $self->{root}.$fn
|
|
|
|
|
if $fn !~ m!^/!so;
|
|
|
|
|
die "[Template] couldn't load template file '$fn' for handle '$handle'"
|
|
|
|
|
unless $textref = $self->loadfile($fn);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$textref = $fn;
|
|
|
|
|
$fn = undef;
|
|
|
|
|
}
|
|
|
|
|
my $sub = $self->compile($textref, $handle, $fn);
|
|
|
|
|
my $str = eval { &$sub($self) };
|
|
|
|
|
die "[Template] error running '$handle': $@" if $@;
|
2009-02-07 16:16:04 +03:00
|
|
|
|
&{$self->{wrapper}} ($str) if $self->{wrapper};
|
2007-06-02 02:30:09 +04:00
|
|
|
|
return $str;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция загружает файл с кэшированием
|
|
|
|
|
# $textref = $obj->loadfile($file)
|
|
|
|
|
sub loadfile
|
|
|
|
|
{
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $self = shift;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my ($fn) = @_;
|
|
|
|
|
my $load = 0;
|
|
|
|
|
my $mtime;
|
|
|
|
|
if (!$uncompiled_code->{$fn} || $self->{reload})
|
|
|
|
|
{
|
|
|
|
|
$mtime = [ stat($fn) ] -> [ 9 ];
|
|
|
|
|
$load = 1 if !$uncompiled_code->{$fn} || $mtime > $mtimes->{$fn};
|
|
|
|
|
}
|
|
|
|
|
if ($load)
|
|
|
|
|
{
|
|
|
|
|
# если файл изменился - перезасасываем
|
|
|
|
|
my ($fd, $text);
|
|
|
|
|
if (open $fd, "<", $fn)
|
|
|
|
|
{
|
|
|
|
|
local $/ = undef;
|
|
|
|
|
$text = <$fd>;
|
|
|
|
|
close $fd;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
# удаляем старый скомпилированный код
|
|
|
|
|
delete $compiled_code->{$uncompiled_code->{$fn}}
|
|
|
|
|
if $uncompiled_code->{$fn};
|
|
|
|
|
$uncompiled_code->{$fn} = \$text;
|
|
|
|
|
$mtimes->{$fn} = $mtime;
|
|
|
|
|
}
|
|
|
|
|
return $uncompiled_code->{$fn};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Функция присваивает переменные блока в новую итерацию
|
|
|
|
|
# $obj->assign_block_vars ($block, varname1 => value1, varname2 => value2, ...)
|
2009-02-01 19:37:33 +03:00
|
|
|
|
# Так тоже можно (при этом избежим лишнего копирования хеша!):
|
|
|
|
|
# $obj->assign_block_vars ($block, { varname1 => value1, varname2 => value2, ... })
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub assign_block_vars
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
|
|
|
|
my $block = shift;
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $vararray;
|
|
|
|
|
if (@_ > 1)
|
|
|
|
|
{
|
|
|
|
|
# копирование хеша, да...
|
|
|
|
|
$vararray = { @_ };
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
# а так можно и не копировать
|
|
|
|
|
($vararray) = @_;
|
|
|
|
|
}
|
2008-03-03 14:29:35 +03:00
|
|
|
|
$block =~ s/^\.+//so;
|
|
|
|
|
$block =~ s/\.+$//so;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
if (!$block)
|
|
|
|
|
{
|
|
|
|
|
# если не блок, а корневой уровень
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$self->assign_vars($vararray);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
elsif ($block !~ /\.[^\.]/so)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
|
|
|
|
# если блок, но не вложенный
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$block =~ s/\.*$/./so; # добавляем . в конец, если надо
|
|
|
|
|
$self->{tpldata}->{$block} ||= [];
|
2009-01-06 01:39:48 +03:00
|
|
|
|
push @{$self->{tpldata}->{$block}}, $vararray;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
# если вложенный блок
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $ev;
|
|
|
|
|
$block =~ s/\.+$//so; # обрезаем точки в конце (хоть их 10 там)
|
2009-06-27 17:20:21 +04:00
|
|
|
|
unless ($ev = $assigncache->{"=$block"})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$ev = '$_[0]';
|
|
|
|
|
my @blocks = split /\./, $block;
|
|
|
|
|
my $lastblock = pop @blocks;
|
|
|
|
|
foreach (@blocks)
|
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$ev .= "{'$_'}";
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$ev .= "[\$\#\{$ev\}]";
|
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$ev .= "{'$lastblock'}";
|
2009-02-01 23:01:45 +03:00
|
|
|
|
$ev = "return sub { $ev ||= []; push \@\{$ev\}, \$_[1]; }";
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$ev = $assigncache->{"=$block"} = eval $ev;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
&$ev($self->{tpldata}, $vararray);
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция добавляет переменные к текущей итерации блока
|
|
|
|
|
# $obj->append_block_vars ($block, varname1 => value1, varname2 => value2, ...)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub append_block_vars
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
|
|
|
|
my $block = shift;
|
|
|
|
|
my %vararray = @_;
|
|
|
|
|
my $lastit;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
if (!$block || $block eq '.')
|
|
|
|
|
{
|
|
|
|
|
# если не блок, а корневой уровень
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$self->assign_vars(@_);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
elsif ($block !~ /\../so)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
|
|
|
|
# если блок, но не вложенный
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$block =~ s/\.*$/./so; # добавляем . в конец, если надо
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$self->{tpldata}{$block} ||= [];
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$lastit = $#{$self->{tpldata}{$block}};
|
2008-12-27 02:40:58 +03:00
|
|
|
|
$lastit = 0 if $lastit < 0;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$self->{tpldata}{$block}[$lastit]{$_} = $vararray{$_}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
for keys %vararray;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
# если вложенный блок
|
2009-02-01 19:37:33 +03:00
|
|
|
|
my $ev;
|
|
|
|
|
$block =~ s/\.+$//so; # обрезаем точки в конце (хоть их 10 там)
|
2009-06-27 17:20:21 +04:00
|
|
|
|
unless ($ev = $assigncache->{"+$block"})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$ev = '$_[0]';
|
|
|
|
|
my @blocks = split /\.+/, $block;
|
|
|
|
|
foreach (@blocks)
|
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$ev .= "{'$_'}";
|
2009-02-01 19:37:33 +03:00
|
|
|
|
$ev .= "[\$#\{$ev\}]";
|
|
|
|
|
}
|
2009-02-01 23:01:45 +03:00
|
|
|
|
$ev = 'return sub { for my $k (keys %{$_[1]}) { '.$ev.'{$k} = $_[1]->{$k}; } }';
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$ev = $assigncache->{"+$block"} = eval $ev;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
&$ev($self->{tpldata}, \%vararray);
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция присваивает переменные корневого уровня
|
|
|
|
|
# $obj->assign_vars (varname1 => value1, varname2 => value2, ...)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub assign_vars
|
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $h;
|
|
|
|
|
if (@_ > 1 || !ref $_[0])
|
2009-02-01 19:37:33 +03:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$h = { @_ };
|
2009-02-01 19:37:33 +03:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$h = $_[0];
|
2009-02-01 19:37:33 +03:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$self->{tpldata} ||= {};
|
|
|
|
|
$self->{tpldata}->{$_} = $h->{$_} for keys %$h;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция компилирует код
|
|
|
|
|
# $sub = $self->compile(\$code, $handle, $fn);
|
|
|
|
|
# print &$sub($self);
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub compile
|
|
|
|
|
{
|
2007-10-28 19:16:50 +03:00
|
|
|
|
my $self = shift;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my ($coderef, $handle, $fn) = @_;
|
|
|
|
|
return $compiled_code->{$coderef} if $compiled_code->{$coderef};
|
2009-06-27 17:20:21 +04:00
|
|
|
|
|
|
|
|
|
# кэширование на диске
|
2009-03-13 20:47:24 +03:00
|
|
|
|
my $h;
|
|
|
|
|
if ($self->{cache_dir})
|
|
|
|
|
{
|
|
|
|
|
$h = $self->{cache_dir}.md5_hex($$coderef).'.pl';
|
|
|
|
|
if (-e $h)
|
|
|
|
|
{
|
|
|
|
|
$compiled_code->{$coderef} = do $h;
|
|
|
|
|
if ($@)
|
|
|
|
|
{
|
|
|
|
|
warn "[Template] error compiling '$handle': [$@] in FILE: $h";
|
|
|
|
|
unlink $h;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return $compiled_code->{$coderef};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# прописываем путь к текущему шаблону в переменную
|
2008-08-15 21:31:24 +04:00
|
|
|
|
$self->{cur_template_path} = $self->{cur_template} = '';
|
2009-01-06 01:39:48 +03:00
|
|
|
|
if ($fn)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$self->{cur_template} = $fn;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$self->{cur_template} = substr $self->{cur_template}, length $self->{root}
|
|
|
|
|
if substr($self->{cur_template}, 0, length $self->{root}) eq $self->{root};
|
2008-08-15 21:31:24 +04:00
|
|
|
|
$self->{cur_template} =~ s/\.[^\.]+$//iso;
|
|
|
|
|
$self->{cur_template} =~ s/:+//gso;
|
|
|
|
|
$self->{cur_template} =~ s!/+!:!gso;
|
|
|
|
|
$self->{cur_template} =~ s/[^\w_:]+//gso;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$self->{cur_template_path} = '->{"' . join('"}->{"',
|
|
|
|
|
map { lc } split /:/, $self->{cur_template}) . '"}';
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
my $code = $$coderef;
|
2009-02-23 22:33:41 +03:00
|
|
|
|
Encode::_utf8_on($code) if $self->{use_utf8};
|
2009-01-25 19:56:18 +03:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# удаляем комментарии <!--# ... #-->
|
2007-05-21 03:34:53 +04:00
|
|
|
|
$code =~ s/\s*<!--#.*?#-->//gos;
|
2008-02-13 18:10:23 +03:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$self->{blocks} = [];
|
|
|
|
|
$self->{in} = [];
|
|
|
|
|
$self->{included} = {};
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $r = '';
|
|
|
|
|
my ($p, $c, $t);
|
|
|
|
|
my $pp = 0;
|
2007-09-11 02:46:55 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# ищем фрагменты кода
|
|
|
|
|
$code =~ /^/gcso;
|
|
|
|
|
while ($code =~ /<!--(.*?)-->|\{(.*?)\}/gcso)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$c = $1 ? $self->compile_code_fragment($1) : $self->compile_substitution($2);
|
|
|
|
|
next unless $c;
|
|
|
|
|
if (($t = pos($code) - $pp - length $&) > 0)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$p = substr $code, $pp, $t;
|
|
|
|
|
$p =~ s/\\|\'/\\$&/gso;
|
|
|
|
|
$r .= "\$t.='$p';\n";
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$r .= $c if $c;
|
|
|
|
|
$pp = pos $code;
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
2009-02-01 19:37:33 +03:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# дописываем начало и конец кода
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$code = ($self->{use_utf8} ? "\nuse utf8;\n" : "").
|
|
|
|
|
'sub {
|
|
|
|
|
my $self = shift;
|
|
|
|
|
my $t = "";
|
|
|
|
|
my $_current_template = [ split /:/, \'' . $self->{cur_template} . '\' ];
|
2009-06-27 17:20:21 +04:00
|
|
|
|
' . $r . '
|
2009-01-06 01:39:48 +03:00
|
|
|
|
return $t;
|
|
|
|
|
}';
|
2009-06-27 17:20:21 +04:00
|
|
|
|
undef $r;
|
|
|
|
|
|
|
|
|
|
# кэшируем код на диск
|
2009-03-13 20:47:24 +03:00
|
|
|
|
if ($h)
|
|
|
|
|
{
|
|
|
|
|
my $fd;
|
|
|
|
|
if (open $fd, ">$h")
|
|
|
|
|
{
|
|
|
|
|
print $fd $code;
|
|
|
|
|
close $fd;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
warn "[Template] error caching '$handle': $! while opening $h";
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-01-06 01:39:48 +03:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# компилируем код
|
2009-01-06 01:39:48 +03:00
|
|
|
|
$compiled_code->{$coderef} = eval $code;
|
|
|
|
|
die "[Template] error compiling '$handle': [$@] in CODE:\n$code" if $@;
|
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# возвращаем ссылку на процедуру
|
2009-01-06 01:39:48 +03:00
|
|
|
|
return $compiled_code->{$coderef};
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# компиляция фрагмента кода <!-- ... -->. это может быть:
|
|
|
|
|
# 1) [ELSE] IF выражение
|
|
|
|
|
# 2) BEGIN имя блока
|
|
|
|
|
# 3) END [имя блока]
|
|
|
|
|
# 4) SET переменная
|
|
|
|
|
# 5) SET переменная = выражение
|
|
|
|
|
# 6) INCLUDE имя_файла_шаблона
|
|
|
|
|
# 7) выражение
|
|
|
|
|
sub compile_code_fragment
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2008-02-13 18:10:23 +03:00
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my ($e) = @_;
|
|
|
|
|
my $t;
|
|
|
|
|
$e =~ s/^\s+//so;
|
|
|
|
|
$e =~ s/\s+$//so;
|
|
|
|
|
if ($e =~ /^(ELS(?:E\s+)?)?IF\s+/iso)
|
|
|
|
|
{
|
|
|
|
|
$t = $self->compile_expression($');
|
|
|
|
|
unless ($t)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($')";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
return $1 ? "} elsif ($t) {\n" : "if ($t) {\n";
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
elsif ($e =~ /^BEGIN\s+([a-z_][a-z0-9_]*)(?:\s+AT\s+(.+))?(?:\s+BY\s+(.+))?(?:\s+TO\s+(.+))?$/iso)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $ref = $self->varref([@{$self->{blocks}}, $1]);
|
|
|
|
|
my $at = 0;
|
|
|
|
|
if ($2)
|
|
|
|
|
{
|
|
|
|
|
$at = $self->compile_expression($2);
|
|
|
|
|
unless ($at)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($2) in AT";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
my $by = '++';
|
|
|
|
|
if ($3)
|
|
|
|
|
{
|
|
|
|
|
$by = $self->compile_expression($3);
|
|
|
|
|
unless ($by)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($3) in BY";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
$by = '+=' . $by;
|
|
|
|
|
}
|
|
|
|
|
my $to = '';
|
|
|
|
|
if ($4)
|
|
|
|
|
{
|
|
|
|
|
$to = $self->compile_expression($4);
|
|
|
|
|
unless ($to)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($4) in TO";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
$to = "\$blk_${1}_count = $to if $to < \$blk_${1}_count;";
|
|
|
|
|
}
|
|
|
|
|
push @{$self->{blocks}}, $1;
|
|
|
|
|
push @{$self->{in}}, [ 'begin', $1 ];
|
|
|
|
|
return <<EOF;
|
|
|
|
|
my \$blk_${1}_count = ref($ref) && $ref =~ /ARRAY/so ? scalar \@{$ref} : $ref ? 1 : 0;
|
|
|
|
|
${to}
|
|
|
|
|
for (my \$blk_${1}_i = $at; \$blk_${1}_i < \$blk_${1}_count; \$blk_${1}_i $by) {
|
|
|
|
|
my \$blk_${1}_vars = ref($ref) && $ref =~ /ARRAY/so ? $ref ->{\$blk_${1}_i} : $ref;
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
elsif ($e =~ /^END(?:\s+([a-z_][a-z0-9_]*))?$/iso)
|
|
|
|
|
{
|
|
|
|
|
unless (@{$self->{in}})
|
|
|
|
|
{
|
|
|
|
|
warn "$& without BEGIN, IF or SET";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
my $l = $self->{in}->{$#{$self->{in}}};
|
|
|
|
|
if ($1 && ($l->[0] ne 'begin' || !$l->[1] || $l->[1] ne $1) ||
|
|
|
|
|
!$1 && $l->[1])
|
|
|
|
|
{
|
|
|
|
|
warn "$& after ".uc($l->[0])." $l->[1]";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
pop @{$self->{in}};
|
|
|
|
|
pop @{$self->{blocks}} if $1;
|
|
|
|
|
return $l->[0] eq 'set' ? "return \$t;\n};\n" : "} # $&\n";
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
elsif ($e =~ /^SET\s+((?:[a-z0-9_]+\.)*[a-z0-9_]+)(\s*=\s*)?$/iso)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
if ($2)
|
|
|
|
|
{
|
|
|
|
|
$t = $self->compile_expression($');
|
|
|
|
|
unless ($t)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($')";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
push @{$self->{in}}, [ 'set', $1 ];
|
|
|
|
|
return $self->varref($1) . ' = ' . ($t || 'eval { my $t = ""') . ";\n";
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
elsif ($e =~ /^INCLUDE\s+(\S+)$/iso)
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $n = $1;
|
|
|
|
|
$n =~ s/\'|\\/\\$&/gso;
|
|
|
|
|
$t = "\$t .= \$self->parse('_INCLUDE$n');\n";
|
|
|
|
|
unless ($self->{included}->{$n})
|
|
|
|
|
{
|
|
|
|
|
$t = "\$self->set_filenames('_INCLUDE$n' => '$n');\n$t";
|
|
|
|
|
$self->{included}->{$n} = 1;
|
|
|
|
|
}
|
|
|
|
|
return $t;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
else
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$t = $self->compile_expression($e);
|
|
|
|
|
return "\$t .= $t;\n" if $t;
|
2008-02-13 18:10:23 +03:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
return undef;
|
2008-02-13 18:10:23 +03:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# компиляция подстановки переменной {...} это просто выражение
|
|
|
|
|
sub compile_substitution
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my ($e) = @_;
|
|
|
|
|
$e = $self->compile_expression($e);
|
|
|
|
|
return undef unless $e;
|
|
|
|
|
return "\$t .= $e;\n";
|
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# компиляция выражения. это может быть:
|
|
|
|
|
# 1) "строковой литерал"
|
|
|
|
|
# 2) 123.123 или 0123 или 0x123
|
|
|
|
|
# 3) переменная
|
|
|
|
|
# 4) функция(выражение,выражение,...,выражение)
|
|
|
|
|
# 5) функция выражение
|
|
|
|
|
# 6) для legacy mode: переменная/имя_функции
|
|
|
|
|
sub compile_expression
|
|
|
|
|
{
|
|
|
|
|
my $self = shift;
|
|
|
|
|
my ($e, $after) = @_;
|
|
|
|
|
$after = undef if $after && ref $after ne 'SCALAR';
|
|
|
|
|
$e =~ s/^\s+//so;
|
|
|
|
|
$e =~ s/\s+$//so unless $after;
|
|
|
|
|
# переменная плюс legacy-mode переменная/функция
|
|
|
|
|
if ($e =~ /^((?:[a-z0-9_]+\.)*(?:[a-z0-9_]+|\#))(?:\/([a-z]+))?\s*/iso)
|
|
|
|
|
{
|
|
|
|
|
if ($')
|
|
|
|
|
{
|
|
|
|
|
return undef unless $after;
|
|
|
|
|
$$after = $';
|
|
|
|
|
}
|
|
|
|
|
$e = $self->varref($1);
|
|
|
|
|
if ($2)
|
|
|
|
|
{
|
|
|
|
|
my $f = lc $2;
|
|
|
|
|
unless ($self->can("function_$f"))
|
|
|
|
|
{
|
|
|
|
|
warn "Unknown function: '$f' called in legacy mode ($&)";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
$f = "function_$f";
|
|
|
|
|
$e = $self->$f($e);
|
|
|
|
|
}
|
|
|
|
|
return $e;
|
2009-01-06 01:39:48 +03:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# функция нескольких аргументов
|
|
|
|
|
elsif ($e =~ /^([a-z_][a-z0-9_]*)\s*\(/iso)
|
2008-09-02 00:19:55 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $f = lc $1;
|
|
|
|
|
unless ($self->can("function_$f"))
|
2008-09-02 00:19:55 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
warn "Unknown function: '$f'";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
my $a = $';
|
|
|
|
|
my @a;
|
|
|
|
|
while ($e = $self->compile_expression($a, \$a))
|
|
|
|
|
{
|
|
|
|
|
push @a, $e;
|
|
|
|
|
if ($a =~ /^\s*\)/so)
|
2008-09-02 00:19:55 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
last;
|
2008-09-02 00:19:55 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
elsif ($a !~ s/^\s*,//so)
|
2008-09-02 00:19:55 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
warn "Unexpected token: '$a' in $f() parameter list";
|
|
|
|
|
return undef;
|
2008-09-02 00:19:55 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
if ($a !~ s/^\s*\)\s*//so)
|
|
|
|
|
{
|
|
|
|
|
warn "Unexpected token: '$a' in the end of $f() parameter list";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
if ($a)
|
|
|
|
|
{
|
|
|
|
|
return undef unless $after;
|
|
|
|
|
$$after = $a;
|
|
|
|
|
}
|
|
|
|
|
$f = "function_$f";
|
|
|
|
|
return $self->$f(@a);
|
|
|
|
|
}
|
|
|
|
|
# функция одного аргумента
|
|
|
|
|
elsif ($e =~ /^([a-z_][a-z0-9_]*)\s+/iso)
|
|
|
|
|
{
|
|
|
|
|
my $f = lc $1;
|
|
|
|
|
unless ($self->can("function_$f"))
|
|
|
|
|
{
|
|
|
|
|
warn "Unknown function: '$f'";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
my $a = $';
|
|
|
|
|
my $arg = $self->compile_expression($a, \$a);
|
|
|
|
|
unless ($arg)
|
|
|
|
|
{
|
|
|
|
|
warn "Invalid expression: ($e)";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
$a =~ s/^\s*//so;
|
|
|
|
|
if ($a)
|
|
|
|
|
{
|
|
|
|
|
return undef unless $after;
|
|
|
|
|
$$after = $a;
|
|
|
|
|
}
|
|
|
|
|
$f = "function_$f";
|
|
|
|
|
return $self->$f($arg);
|
2008-09-02 00:19:55 +04:00
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# строковой или числовой литерал
|
|
|
|
|
elsif ($e =~ /^((\")(?:[^\"\\]+|\\.)+\"|\'(?:[^\'\\]+|\\.)+\'|[1-9]\d*(\.\d+)?|0\d*|0x\d+)\s*/iso)
|
|
|
|
|
{
|
|
|
|
|
if ($')
|
|
|
|
|
{
|
|
|
|
|
return undef unless $after;
|
|
|
|
|
$$after = $';
|
|
|
|
|
}
|
|
|
|
|
$e = $1;
|
|
|
|
|
$e =~ s/[\$\@\%]/\\$&/gso if $2;
|
|
|
|
|
return $e;
|
|
|
|
|
}
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# генерация ссылки на переменную
|
|
|
|
|
sub varref
|
|
|
|
|
{
|
|
|
|
|
my $self = shift;
|
|
|
|
|
return "" unless $_[0];
|
|
|
|
|
my @e = ref $_[0] ? @{$_[0]} : split /\.+/, $_[0];
|
|
|
|
|
$self->{last_varref_path} = join '.', @e;
|
|
|
|
|
my $t = '$self->{tpldata}';
|
|
|
|
|
EQBLOCK: if (@{$self->{blocks}})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
for (0..$#{$self->{blocks}})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
last EQBLOCK unless $self->{blocks}->[$_] eq $e[$_];
|
|
|
|
|
}
|
|
|
|
|
splice @e, 0, @{$self->{blocks}};
|
|
|
|
|
if (@e == 1 && $e[0] eq '#')
|
|
|
|
|
{
|
|
|
|
|
# номер итерации блока
|
|
|
|
|
@e = ();
|
|
|
|
|
$t = '$blk_'.$self->{blocks}->[$#{$self->{blocks}}].'_i';
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# локальная переменная
|
|
|
|
|
$t = '$blk_'.$self->{blocks}->[$#{$self->{blocks}}].'_vars';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (@e)
|
|
|
|
|
{
|
|
|
|
|
if (/^\d+$/so)
|
|
|
|
|
{
|
|
|
|
|
$t .= "->[$_]";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s/\'|\\/\\$&/gso;
|
|
|
|
|
$t .= "->{'$_'}";
|
2008-08-15 21:31:24 +04:00
|
|
|
|
}
|
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
return $t;
|
|
|
|
|
}
|
2008-08-15 21:31:24 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# операция над аргументами
|
|
|
|
|
sub fmop
|
|
|
|
|
{
|
|
|
|
|
my $op = shift;
|
|
|
|
|
shift; # my $self = shift;
|
|
|
|
|
return "((" . join(") $op (", @_) . "))";
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# функции
|
|
|
|
|
sub function_or { fmop('||', @_) }
|
|
|
|
|
sub function_and { fmop('&&', @_) }
|
|
|
|
|
sub function_add { fmop('+', @_) }
|
|
|
|
|
sub function_sub { fmop('-', @_) }
|
|
|
|
|
sub function_mul { fmop('*', @_) }
|
|
|
|
|
sub function_div { fmop('/', @_) }
|
|
|
|
|
sub function_concat { fmop('.', @_) }
|
|
|
|
|
sub function_not { "!($_[1])" }
|
|
|
|
|
sub function_even { "!(($_[1]) & 1)" }
|
|
|
|
|
sub function_odd { "(($_[1]) & 1)" }
|
|
|
|
|
sub function_int { "int($_[1])" }
|
|
|
|
|
sub function_eq { "(($_[1]) == ($_[2]))" }
|
|
|
|
|
sub function_gt { "(($_[1]) > ($_[2]))" }
|
|
|
|
|
sub function_lt { "(($_[1]) < ($_[2]))" }
|
|
|
|
|
sub function_ge { "(($_[1]) >= ($_[2]))" }
|
|
|
|
|
sub function_le { "(($_[1]) <= ($_[2]))" }
|
|
|
|
|
sub function_seq { "(($_[1]) eq ($_[2]))" }
|
|
|
|
|
sub function_sgt { "(($_[1]) gt ($_[2]))" }
|
|
|
|
|
sub function_slt { "(($_[1]) lt ($_[2]))" }
|
|
|
|
|
sub function_sge { "(($_[1]) ge ($_[2]))" }
|
|
|
|
|
sub function_sle { "(($_[1]) le ($_[2]))" }
|
|
|
|
|
sub function_lc { "lc($_[1])" } *function_lower = *function_lowercase = \&function_lc;
|
|
|
|
|
sub function_uc { "uc($_[1])" } *function_upper = *function_uppercase = \&function_uc;
|
|
|
|
|
sub function_requote { "requote($_[1])" } *function_re_quote = *function_preg_quote = \&function_requote;
|
|
|
|
|
sub function_split { "split($_[1], $_[2], $_[3])" }
|
|
|
|
|
sub function_quote { "quotequote($_[1])" } *function_q = \&function_quote;
|
|
|
|
|
sub function_html { "htmlspecialchars($_[1])" } *function_s = \&function_html;
|
|
|
|
|
sub function_strip { "strip_tags($_[1])" } *function_t = \&function_strip;
|
|
|
|
|
sub function_h { "strip_unsafe_tags($_[1])" } *function_strip_unsafe = \&function_h;
|
|
|
|
|
sub function_l { f_translate(undef, @_) } *function_translate = \&function_l;
|
|
|
|
|
sub function_lz { f_translate(1, @_) } *function_translate_null = \&function_lz;
|
|
|
|
|
|
|
|
|
|
# объединяет не просто скаляры, а также все элементы массивов
|
|
|
|
|
sub function_join
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2007-05-21 03:34:53 +04:00
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my $e = shift;
|
|
|
|
|
$e = "join($e";
|
|
|
|
|
$e .= ", ref($_) eq 'ARRAY' ? \@{$_} : ($_)" for @_;
|
|
|
|
|
$e .= ")";
|
|
|
|
|
return $e;
|
|
|
|
|
}
|
2007-05-21 03:34:53 +04:00
|
|
|
|
|
2009-06-27 17:20:21 +04:00
|
|
|
|
# автоматически выбирает, в compile-time или в run-time делать перевод
|
|
|
|
|
sub f_translate
|
|
|
|
|
{
|
|
|
|
|
my $ifnull = shift;
|
|
|
|
|
my $e = eval $_[1];
|
|
|
|
|
if ($@)
|
|
|
|
|
{
|
|
|
|
|
# выражение - не константа, т.к. не вычисляется без $self
|
|
|
|
|
return $_[0]->language_ref($_[0]->{last_varref_path}, $_[1], $ifnull);
|
|
|
|
|
}
|
|
|
|
|
# выражение - константа
|
|
|
|
|
return $_[0]->language_xform($e);
|
2007-05-21 03:34:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Функция компилирует ссылку на данные ленгпака
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub language_ref
|
|
|
|
|
{
|
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my ($var, $varref, $emptyifnull) = @_;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
my $code = '';
|
|
|
|
|
$code .= '->{' . lc($_) . '}' foreach split /\.+/, $var;
|
|
|
|
|
$code .= '->{' . $varref . '}';
|
2009-01-28 02:32:22 +03:00
|
|
|
|
$code = ($self->{cur_template_path} ?
|
2008-08-15 21:31:24 +04:00
|
|
|
|
'(($self->{lang}' . $self->{cur_template_path} . $code . ') || ' : '') .
|
2009-01-28 02:32:22 +03:00
|
|
|
|
'($self->{lang}' . $code . ')';
|
2009-06-27 17:20:21 +04:00
|
|
|
|
$code .= ' || (' . $varref . ')' unless $emptyifnull;
|
2009-01-28 02:32:22 +03:00
|
|
|
|
$code .= ')';
|
2008-08-15 21:31:24 +04:00
|
|
|
|
return $code;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-06 01:39:48 +03:00
|
|
|
|
# Compile-time вычисление language_ref
|
2008-08-15 21:31:24 +04:00
|
|
|
|
sub language_xform
|
|
|
|
|
{
|
|
|
|
|
my $self = shift;
|
2009-06-27 17:20:21 +04:00
|
|
|
|
my ($value) = @_;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
my ($ca, $cb) = ($self->{lang}, $self->{lang});
|
|
|
|
|
foreach (split /:/, $self->{cur_template})
|
|
|
|
|
{
|
|
|
|
|
$cb = $cb->{lc $_} if $cb;
|
|
|
|
|
}
|
2009-06-27 17:20:21 +04:00
|
|
|
|
if (@{$self->{blocks}})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
2009-06-27 17:20:21 +04:00
|
|
|
|
foreach (@{$self->{blocks}})
|
2008-08-15 21:31:24 +04:00
|
|
|
|
{
|
|
|
|
|
$ca = $ca->{lc $_} if $ca;
|
|
|
|
|
$cb = $cb->{lc $_} if $cb;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$ca = $ca->{$value} if $ca;
|
|
|
|
|
$cb = $cb->{$value} if $cb;
|
|
|
|
|
return $ca || $cb;
|
|
|
|
|
}
|
|
|
|
|
|
2007-05-21 03:34:53 +04:00
|
|
|
|
1;
|
2008-08-15 21:31:24 +04:00
|
|
|
|
__END__
|