2011-12-28 63 views
11

我正在处理一个使用本地模板系统的Web应用程序,该系统可以将Perl代码嵌入到HTML中。这些语句由模板解析器在运行时使用eval EXPR执行。eval的更快选择?

这是非常灵活的,但是这些语句分散到处,并得到执行很多eval EXPR(而不是eval BLOCK)要求Perl每次启动解释器,并且我的配置文件显示它们是相当重要的放缓来源。

许多嵌入式Perl语句非常简单。例如,一个模板可能有这样一行:

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

或者:

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

也就是说,他们只是调用对象的方法。但是,也有更复杂的。

我希望能够优化这个,到目前为止有两个想法,两者都很糟糕。第一种方法是重写所有模板,以便使用USER:NAMEUSER:GENERATETICKETNUMBER之类的标记替换简单的调用,然后解析器可以扫描并调用适当的对象方法。但是,不用处理混合HTML和Perl的模板,我可以使用混合HTML,Perl和令牌的模板。

第二个想法是尝试解析嵌入式Perl,找出语句想要做什么,如果足够简单,可以通过符号引用调用适当的对象方法。这显然是疯了。

我忽略了一些合乎逻辑的解决方案吗?

+1

+1“这显然是疯了。” – 2011-12-28 05:56:16

回答

9

尝试采取类似mod_perl用于编译的CGI的一个做法:

  1. 将模板转换成Perl代码。例如,你的第一个例子可能转换为类似:

    print "<p>Welcome, "; 
    print $user->query('name'); 
    print ".\n"; 
    
  2. sub { ... }周围的代码,一些代码一起拆封参数(例如,对于像样品中$user)。

  3. eval该代码。请注意,它返回一个coderef。

  4. 反复调用coderef。 :)

+5

一个相关的方法是将生成的代码写入'.pm'文件,然后加载该模块。这样,模板只需要在更改时重新进行修改。哪种方式更好取决于应用程序的实现细节。 – cjm 2011-12-28 06:01:46

+0

哇,太棒了!它可以让我解决这个问题,而不必重写一个巨大的模板。我会试试看,谢谢! – parsim 2011-12-28 23:08:50

+0

这正是[模板工具包](http://template-toolkit.org)所做的。 – 2011-12-30 02:45:22

2

你可能想看看Text::MicroTemplate的胆量。实际上,你可能想使用 Text :: MicroTemplate,因为它很可能符合你的需求。它构建了一个子程序,根据需要连接字符串,非常类似于暗示的建议。下面是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

感谢您的帮助!我希望不必更换当前的模板系统就能摆脱这种困境,因为这将是一项相当大的工作。但如果它变得太麻烦了,我会看MicroTemplate。 – parsim 2011-12-28 23:14:18

3

你可以看看Mojolicious。它有一个templating engine它允许一个语法接近你正在使用的。您可以切换使用它或查看其来源(点击上一链接左侧的源代码),看看您是否可以提出一些想法。

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 
+0

谢谢!当我发布给Josh时,我希望避免更换整个模板系统,仅仅因为它是一个有10年历史的软件,并且很难修改。但我听说过关于Mojolicious的好消息。 – parsim 2011-12-28 23:15:53

+0

我完全理解!也许它的代码可能会帮助你一些想法。 – 2011-12-28 23:37:24

+1

@JoelBerger在其他地方这个问题会得到一个“Idiot使用CPAN”的回应。 +1因为我的朋友真棒。 – Hawken 2012-11-03 15:07:39

0

你不应该使用“EVAL”打电话给你的模板方法如下形式。对不起,听起来很刺耳,但分离的观点是从视图层中删除处理代码。上面描述的模板系统和模板工具包只传递一个对象/散列,以便您可以访问它。

为什么不通过$用户像一个hashref:

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

这将允许您访问“名”与:

$user->{'name'}; 

否则,很可能是你有你做类似:

  1. 模板调用$ user-> query();
  2. 方法调用数据库中获取价值
  3. 方法返回看重

这是SOOOO更加昂贵,使数据库查询,而不是通过散列/对象引用模板。您可能想要查看一些代码分析工具,如Devel :: NYTProf,以查看代码执行的哪一部分确实会让您放慢速度。我怀疑eval会让你的程序陷入困境,你需要优化eval。听起来像eval内的代码是什么让你放慢速度。

+0

我确实运行过NYTProf,它似乎表明eval本身是减速的重要来源。它只占服务器总时间的5-10%,但它也是最大的一点。使用$ user - > {$ value}替换$ user-> query($ value)调用并不是真正可行的,因为除了查询值之外,&query子通常确实会做某些事情。而且,这只是众多人中的一个。 – parsim 2011-12-28 23:11:53

+0

parsim,你为什么要调用子例程在视图层上工作?在输出结果之前不应该把所有这些工作都照顾好吗?也许你需要考虑构建一个不同类型的API?如果由于用户交互而致电,那么可能是通过AJAX进行一些服务器端调用的时候了。 – 2011-12-29 22:41:23

+0

答案是Web应用程序没有MVC体系结构。 (我提到它差不多有10年了吗?)模板有效地驱动了应用程序。改变这一点将是一项巨大的工作,所以我坚持不懈。 – parsim 2011-12-30 02:52:23