Более быстрая альтернатива eval? - PullRequest
11 голосов
/ 28 декабря 2011

Я имею дело с веб-приложением, которое использует собственную систему шаблонов, которая позволяет встроить код Perl в HTML.Эти операторы выполняются синтаксическим анализатором шаблона во время выполнения с использованием eval EXPR.

. Это очень гибко, но эти операторы разбросаны повсюду и выполняются lot .eval EXPR (в отличие от eval BLOCK) требует, чтобы Perl каждый раз запускал интерпретатор, и мое профилирование показывает, что они являются довольно значительным источником замедления.

Многие из встроенных операторов Perl оченьпросто.Например, шаблон может иметь такую ​​строку:

<p>Welcome, <!--E: $user->query('name') -->.

Или:

<p>Ticket number <!--E: $user->generate_ticket_number() --> has been generated.

То есть они просто вызывают методы объекта.Однако есть и более сложные.

Я надеюсь оптимизировать это, и пока у меня есть две идеи, обе из которых ужасны.Первый - переписать все шаблоны, чтобы заменить простые вызовы токенами, такими как USER:NAME и USER:GENERATETICKETNUMBER, которые затем анализатор может сканировать и вызывать соответствующий объектный метод.Но тогда вместо того, чтобы иметь дело с шаблонами, которые смешивают HTML и Perl, у меня были бы шаблоны, которые смешивают HTML, Perl и токены.

Вторая идея - попытаться проанализировать встроенный Perl, выяснить, чего хочет операторделать, и, если это достаточно просто, вызвать соответствующий метод объекта через символьную ссылку.Это явно безумие.

Есть ли какое-то логическое решение, которое я упускаю из виду?

Ответы [ 4 ]

9 голосов
/ 28 декабря 2011

Попробуйте применить подход, аналогичный тому, который mod_perl использует для компиляции CGI:

  1. Преобразование шаблона в код Perl.Например, ваш первый пример может преобразоваться во что-то вроде:

    print "<p>Welcome, ";
    print $user->query('name');
    print ".\n";
    
  2. Оберните sub { ... } вокруг этого кода вместе с некоторым кодом для распаковки аргументов (например, для таких вещей, как $user в образце).

  3. eval этот код.Обратите внимание, что он возвращает кодовую ссылку.

  4. Повторно вызывайте эту кодовую ссылку.:)

3 голосов
/ 28 декабря 2011

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

К вашему сведению, синтаксис механизма шаблонов Mojolcious позволяет соответствующим образом смешивать следующие формы с HTML

<% Perl code %>
<%= Perl expression, replaced with result %>
<%== Perl expression, replaced with XML escaped result %>
<%# Comment, useful for debugging %>
<%% Replaced with "<%", useful for generating templates %>
% Perl code line, treated as "<% line =%>"
%= Perl expression line, treated as "<%= line %>"
%== Perl expression line, treated as "<%== line %>"
%# Comment line, treated as "<%# line =%>"
%% Replaced with "%", useful for generating templates
2 голосов
/ 28 декабря 2011

Возможно, вы захотите взглянуть на внутренности Text :: MicroTemplate .Реально, вы можете использовать Text :: MicroTemplate, так как это, вероятно, соответствует вашим потребностям.Он создает подпрограмму, которая объединяет строки по мере необходимости, так же, как предложено duskwuff.Вот результат build_mt('hello, <?= $_[0] ?>') в re.pl:

$CODE1 = sub {
       package Devel::REPL::Plugin::Packages::DefaultScratchpad;
       use warnings;
       use strict 'refs';
       local $SIG{'__WARN__'} = sub {
         print STDERR $_mt->_error(shift(), 4, $_from);
       }
       ;
       Text::MicroTemplate::encoded_string(sub {
         my $_mt = '';
         local $_MTREF = \$_mt;
         my $_from = '';
         $_mt .= 'hello, ';
         $_from = $_[0];
         $_mt .= ref $_from eq 'Text::MicroTemplate::EncodedString' ? $$_from : do {
           $_from =~ s/([&><"'])/$Text::MicroTemplate::_escape_table{$1};/eg;
           $_from
         };
         return $_mt;
       }
       ->(@_));
     };
0 голосов
/ 29 декабря 2011

Вы не должны использовать 'eval' для вызова методов в вашем шаблоне.Извините за грубость, но цель отдельного представления - удалить код обработки из слоя представления.Системы шаблонов, описанные выше вместе с Template Toolkit, просто передают объект / хэш, чтобы вы могли получить к нему доступ.

почему бы не передать $ user в виде хеш-кода, например:

$user = {
        'name' => 'John',
        'id' => '3454'
      };

, это позволит вам получить доступ к имени с помощью:

$user->{'name'};

В противном случае, вероятно, чтоу вас есть что-то вроде:

  1. шаблон вызывает $ user-> query ();
  2. метод вызывает DB для получения значения
  3. метод возвращает значение

Это намного дороже сделать запрос к базе данных, чем передать ссылку на хеш / объект в шаблон.Возможно, вы захотите проверить некоторые инструменты профилирования кода, такие как Devel :: NYTProf, чтобы увидеть, какая часть выполнения кода действительно замедляет вас.Я скептически отношусь к тому, что eval так сильно тормозит вашу программу, что вам нужно оптимизировать eval.Похоже, код внутри eval - это то, что замедляет тебя.

...