📄 builder.pm
字号:
# Copyright 2001-2005 Six Apart.# SCRiPTMAFiA 2005 - THE DiRTY HANDS ON YOUR SCRiPTS## $Id: Builder.pm 10197 2005-03-09 00:27:57Z ezra $package MT::Builder;use strict;use MT::ErrorHandler;@MT::Builder::ISA = qw( MT::ErrorHandler );sub new { bless { }, $_[0] }# use Time::HiRes;# my @tag_stack = ('toplevel');# my @timer_stack = ( [ Time::HiRes::time(), 0 ] );# my $totaltime = 0;sub compile { my $build = shift; my($ctx, $text) = @_; return [ ] unless $text; my $state = local $build->{__state} = { tokens => [ ] }; $state->{text} = \$text; my $pos = 0; my $len = length $text; while ($text =~ m!(<\$?MT(.+?)[\$/]?>)!gs) { my($whole_tag, $tag) = ($1, $2); ($tag, my($args)) = split /\s+/, $tag, 2; my $sec_start = pos $text; my $tag_start = $sec_start - length $whole_tag; _text_block($state, $pos, $tag_start) if $pos < $tag_start; $args ||= ''; my %args; while ($args =~ /(\w+)\s*=\s*(["'])(.*?)\2/gs) { #" $args{$1} = $3; } my($h, $is_container) = $ctx->handler_for($tag); my $rec = [ $tag, \%args ]; if ($is_container) { if ($whole_tag !~ m|/>|) { my ($sec_end, $tag_end) = _consume_up_to(\$text,$sec_start,$tag); if ($sec_end) { my $sec = substr $text, $sec_start, $sec_end - $sec_start; $sec =~ s!^\n!!; $rec->[2] = $build->compile($ctx, $sec) or return; $rec->[3] = $sec; } else { return $build->error("<MT$tag> with no </MT$tag>"); } $pos = $tag_end + 1; (pos $text) = $tag_end; } else { $rec->[3] = ''; $pos = pos $text; } } else { $pos = pos $text; } push @{ $state->{tokens} }, $rec; $pos = pos $text; } _text_block($state, $pos, $len) if $pos < $len; $state->{tokens};}sub _consume_up_to { my($text, $start, $stoptag) = @_; my $pos; (pos $$text) = $start; while ($$text =~ m!(<([\$/]?)MT($stoptag[^>]*?)[\$/]?>)!g) { my($whole_tag, $prefix, $tag) = ($1, $2, $3); ($tag, my($args)) = split /\s+/, $tag, 2; next if $tag ne $stoptag; my $end = pos $$text; if ($prefix && ($prefix eq '/')) { return ($end - length($whole_tag), $end); } elsif ($whole_tag !~ m|/>|) { my ($sec_end, $end_tag) = _consume_up_to($text, $end, $tag); last if !$sec_end; (pos $$text) = $end_tag; } } return (0, 0);}sub _text_block { my $text = substr ${ $_[0]->{text} }, $_[1], $_[2] - $_[1]; push @{ $_[0]->{tokens} }, [ 'TEXT', $text ] if $text;}sub build { my $build = shift; my($ctx, $tokens, $cond) = @_; $cond ||= { }; $ctx->stash('builder', $build); my $res = ''; my $ph = $ctx->post_process_handler; for my $t (@$tokens) { if ($t->[0] eq 'TEXT') { $res .= $t->[1]; } else { my($tokens, $tokens_else, $uncompiled); if (exists $cond->{ $t->[0] } && !$cond->{ $t->[0] }) { # if there's a cond for this tag and it's false, # walk the children and look for an MTElse. # the children of the MTElse will become $tokens for my $tok (@{ $t->[2] }) { if ($tok->[0] eq 'Else') { $tokens = $tok->[2]; $uncompiled = $tok->[3]; last; } } next unless $tokens; } else { if ($t->[2] && ref($t->[2]) eq 'ARRAY') { # either there is no cond for this tag, or it's true, # so we want to partition the children into # those which are inside an else and those which are not. ($tokens, $tokens_else) = ([], []); for my $sub (@{ $t->[2] }) { if ($sub->[0] eq 'Else') { push @$tokens_else, $sub; } else { push @$tokens, $sub; } } } $uncompiled = $t->[3]; } my($h) = $ctx->handler_for($t->[0]); if ($h) { local($ctx->{__stash}->{tag}) = $t->[0]; local($ctx->{__stash}->{tokens}) = $tokens; local($ctx->{__stash}->{tokens_else}) = $tokens_else; local($ctx->{__stash}->{uncompiled}) = $uncompiled; local($t->[1]) = $t->[1]; my $out = $h->($ctx, $t->[1], $cond); return $build->error("Error in <MT$t->[0]> tag: " . $ctx->errstr) unless defined $out; if ($ph) { $out = $ph->($ctx, $t->[1], $out); } $res .= $out; } } } $res;}1;__END__=head1 NAMEMT::Builder - Parser and interpreter for MT templates=head1 SYNOPSIS use MT::Builder; use MT::Template::Context; my $build = MT::Builder->new; my $ctx = MT::Template::Context->new; my $tokens = $build->compile($ctx, '<$MTVersion$>') or die $build->errstr; defined(my $out = $build->build($ctx, $tokens)) or die $build->errstr;=head1 DESCRIPTIONI<MT::Builder> provides the parser and interpreter for taking a templatebody and turning it into a generated output page. An I<MT::Builder> objectknows how to parse a string of text into tokens, then take those tokens andbuild a scalar string representing the output of the page. It does not,however, know anything about the types of tags that it encounters; it handsoff this work to the I<MT::Template::Context> object, which can look up atag and determine whether it's valid, whether it's a container or substitutiontag, etc.All I<MT::Builder> knows is the basic structure of a Movable Type tag, andhow to break up a string into pieces: plain text pieces interspersed withtag callouts. It then knows how to take a list of these tokens/pieces andbuild a completed page, using the same I<MT::Template::Context> object toactually fill in the values for the Movable Type tags.=head1 USAGE=head2 MT::Builder->newConstructs and returns a new parser/interpreter object.=head2 $build->compile($ctx, $string)Given an I<MT::Template::Context> object I<$ctx>, breaks up the scalar stringI<$string> into tokens and returns the list of tokens as a reference to anarray. Returns C<undef> on compilation failure.=head2 $build->build($ctx, \@tokens [, \%cond ])Given an I<MT::Template::Context> object I<$ctx>, turns a list of tokensI<\@tokens> and generates an output page. Returns the output page on success,C<undef> on failure. Note that the empty string (C<''>) and the number zero(C<0>) are both valid return values for this method, so you should checkspecifically for an undefined value when checking for errors.The optional argument I<\%cond> specifies a list of conditions under whichthe tokens will be interpreted. If provided, I<\%cond> should be a referenceto a hash, where the keys are MT tag names (without the leading C<MT>), andthe values are boolean flags specifying whether to include the tag; a truevalue means that the tag should be included in the final output, a false valuethat it should not. This is useful when a template includes conditionalcontainer tags (eg C<E<lt>MTEntryIfExtendedE<gt>>), and you wish to influencethe inclusion of these container tags. For example, if a template containsthe container <MTEntryIfExtended> <$MTEntryMore$> </MTEntryIfExtended>and you wish to exclude this conditional, you could call I<build> like this: my $out = $build->build($ctx, $tokens, { EntryIfExtended => 0 });=head1 ERROR HANDLINGOn an error, the above methods return C<undef>, and the error message canbe obtained by calling the method I<errstr> on the object. For example: defined(my $out = $build->build($ctx, $tokens)) or die $build->errstr;=head1 AUTHOR & COPYRIGHTSPlease see the I<MT> manpage for author, copyright, and license information.=cut
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -