Быстрее альтернатива eval?

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

Это очень гибко, но эти заявления разбросаны повсюду, и они выполняются много. 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, выяснить, что хочет сделать оператор, и, если он достаточно прост, вызовите соответствующий метод объекта с помощью символической ссылки. Это, безусловно, безумие.

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

Ответ 1

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

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

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

  • eval этот код. Обратите внимание, что он возвращает coderef.

  • Повторно вызовите этот coderef.:)

Ответ 2

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

FYI Синтаксис мозаичного моделирования 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

Ответ 3

Вы можете посмотреть на кишки Текст:: 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;
       }
       ->(@_));
     };

Ответ 4

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

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

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

это позволит вам получить доступ к 'name' с помощью:

$user->{'name'};

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

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

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