我正在处理一个使用本土模板系统的Web应用程序,该系统允许将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,弄清楚语句想要做什么,如果它足够简单,通过符号引用调用适当的对象方法。这显然是疯了。
我有一些合理的解决方案吗?
尝试采取类似于那种方法 mod_perl
用于编译CGI:
将模板转换为Perl代码。例如,您的第一个示例可能会转换为:
print "<p>Welcome, ";
print $user->query('name');
print ".\n";
包裹一个 sub {
... }
围绕该代码,以及一些解包参数的代码(例如,类似于 $user
在样本中)。
eval
那段代码。请注意,它返回coderef。
重复调用该coderef。 :)
你可以看看 Mojolicious。它有一个 模板引擎 它允许语法接近你正在使用的语法。您可以切换到使用它或查看其来源(单击上一个链接左侧的源)以查看是否可以绘制一些想法。
仅供参考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
你可能想看看它的内脏 文字::微模板。实际上,你可能想要 使用 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;
}
->(@_));
};
您不应该使用'eval'来调用模板中的方法。很抱歉听起来很苛刻,但分离视图的目的是从视图层中删除处理代码。上面描述的模板系统和Template Toolkit只传入一个对象/哈希,因此您可以访问它。
为什么不将$ user作为hashref传递:
$user = {
'name' => 'John',
'id' => '3454'
};
这将允许您通过以下方式访问“名称”:
$user->{'name'};
否则,你可能会做以下事情:
- 模板调用$ user-> query();
- 方法调用DB来获取值
- 方法返回值
制作数据库查询比将哈希/对象引用传递给模板要昂贵得多。您可能需要查看一些代码分析工具,如Devel :: NYTProf,以查看代码执行的哪些部分确实会让您失望。我怀疑eval是否会使你的程序陷入困境,以至于你需要优化eval。听起来像eval中的代码正在减慢你的速度。