Template::Manual::Internals(3pm) - phpMan

Command: man perldoc info search(apropos)  


Template::Manual::Internals(3pmUser Contributed Perl DocumentatioTemplate::Manual::Internals(3pm)

NAME
       Template::Manual::Internals - Template Toolkit internals

Introduction
       This section of the documentation is aimed at developers wishing to know more about how
       the Template Toolkit works on the inside in order to extend or adapt it to their own
       needs.

       If that doesn't sound like you then you probably don't need to read this.  There is no
       test afterwards.

Outside Looking In
       The Template module is simply a front end module which creates and uses a
       Template::Service and pipes the output wherever you want it to go ("STDOUT" by default, or
       maybe a file, scalar, etc).  The "Apache::Template" module (available separately from
       CPAN) is another front end.  That creates a "Template::Service::Apache" object, calls on
       it as required and sends the output back to the relevant "Apache::Request" object.

       These front-end modules are really only there to handle any specifics of the environment
       in which they're being used.  The "Apache::Template" front end, for example, handles
       "Apache::Request" specifics and configuration via the httpd.conf.  The regular Template
       front-end deals with "STDOUT", variable refs, etc.  Otherwise it is Template::Service (or
       subclass) which does all the work.

       The Template::Service module provides a high-quality template delivery service, with
       bells, whistles, signed up service level agreement and a 30-day no quibble money back
       guarantee.  "Have a good time, all the time", that's our motto.

       Within the lower levels of the Template Toolkit, there are lots of messy details that we
       generally don't want to have to worry about most of the time.  Things like templates not
       being found, or failing to parse correctly, uncaught exceptions being thrown, missing
       plugin modules or dependencies, and so on.  Template::Service hides that all away and
       makes everything look simple to the outsider. It provides extra features, like
       "PRE_PROCESS", "PROCESS" and "POST_PROCESS", and also provides the error recovery
       mechanism via "ERROR".  You ask it to process a template and it takes care of everything
       for you. The "Template::Service::Apache" module goes a little bit further, adding some
       extra headers to the Apache::Request, setting a few extra template variables, and so on.

       For the most part, the job of a service is really just one of scheduling and dispatching.
       It receives a request in the form of a call to its process() method and schedules the
       named template specified as an argument, and possibly several other templates
       ("PRE_PROCESS", etc) to be processed in order. It doesn't actually process the templates
       itself, but instead makes a process() call against a Template::Context object.

       Template::Context is the runtime engine for the Template Toolkit - the module that hangs
       everything together in the lower levels of the Template Toolkit and that one that does
       most of the real work, albeit by crafty delegation to various other friendly helper
       modules.

       Given a template name (or perhaps a reference to a scalar or file handle) the context
       process() method must load and compile, or fetch a cached copy of a previously compiled
       template, corresponding to that name.  It does this by calling on a list of one or more
       Template::Provider objects (the "LOAD_TEMPLATES" posse) who themselves might get involved
       with a Template::Parser to help turn source templates into executable Perl code (but more
       on that later).

       Thankfully, all of this complexity is hidden away behind a simple template() method. You
       call it passing a template name as an argument, and it returns a compiled template in the
       form of a Template::Document object, or otherwise raises an exception.

       A Template::Document is a thin object wrapper around a compiled template subroutine. The
       object implements a process() method which performs a little bit of housekeeping and then
       calls the template subroutine. The object also defines template metadata (defined in "[%
       META ... %]" directives) and has a block() method which returns a hash of any additional
       "[% BLOCK xxxx %]" definitions found in the template source.

       So the context fetches a compiled document via its own template() method and then gets
       ready to process it. It first updates the stash (the place where template variables get
       defined - more on that shortly) to set any template variable definitions specified as the
       second argument by reference to hash array. Then, it calls the document process() method,
       passing a reference to itself, the context object, as an argument. In doing this, it
       provides itself as an object against which template code can make callbacks to access
       runtime resources and Template Toolkit functionality.

       What we're trying to say here is this:  not only does the Template::Context object receive
       calls from the outside, i.e. those originating in user code calling the process() method
       on a Template object, but it also receives calls from the inside, i.e. those originating
       in template directives of the form "[% PROCESS template %]".

       Before we move on to that, here's a simple structure diagram showing the outer layers of
       the Template Toolkit heading inwards, with pseudo code annotations showing a typical
       invocation sequence.

            ,--------.
            | Caller |     use Template;
            `--------'     my $tt = Template->new( ... );
                 |         $tt->process($template, \%vars);
                 |                                                     Outside
           - - - | - - - - - - - - - - - - - - - - - - - - - - - - - - - - T T
                 |         package Template;                            Inside
                 V
           +----------+    sub process($template, \%vars) {
           | Template |        $out = $self->SERVICE->process($template, $vars);
           +----------+        print $out or send it to $self->OUTPUT;
                 |         }
                 |
                 |         package Template::Service;
                 |
                 |         sub process($template, \%vars) {
                 |             try {
           +----------+            foreach $p in @self->PRE_PROCESS
           | Service  |                $self->CONTEXT->process($p, $vars);
           +----------+
                 |                 $self->CONTEXT->process($template, $vars);
                 |
                 |                 foreach $p @self->POST_PROCESS
                 |                     $self->CONTEXT->process($p, $vars);
                 |             }
                 |             catch {
                 |                 $self->CONTEXT->process($self->ERROR);
                 |             }
                 |         }
                 |
                 V         package Template::Context;
           +----------+
           | Context  |    sub process($template, \%vars) {
           +----------+        # fetch compiled template
                 |             $template = $self->template($template)
                 |             # update stash
                 |             $self->STASH->update($vars);
                 |             # process template
                 |             $template->process($self)
                 |         }
                 V
           +----------+    package Template::Document;
           | Document |
           +----------+    sub process($context) {
                               $output = &{ $self->BLOCK }($context);
                           }

Inside Looking Out
       To understand more about what's going on in these lower levels, we need to look at what a
       compiled template looks like.  In fact, a compiled template is just a regular Perl sub-
       routine.  Here's a very simple one.

           sub my_compiled_template {
               return "This is a compiled template.\n";
           }

       You're unlikely to see a compiled template this simple unless you wrote it yourself but it
       is entirely valid.  All a template subroutine is obliged to do is return some output
       (which may be an empty of course).  If it can't for some reason, then it should raise an
       error via "die()".

           sub my_todo_template {
               die "This template not yet implemented\n";
           }

       If it wants to get fancy, it can raise an error as a Template::Exception object.  An
       exception object is really just a convenient wrapper for the '"type"' and '"info"' fields.

           sub my_solilique_template {
               die (Template::Exception->new('yorrick', 'Fellow of infinite jest'));
           }

       Templates generally need to do a lot more than just generate static output or raise
       errors. They may want to inspect variable values, process another template, load a plugin,
       run a filter, and so on. Whenever a template subroutine is called, it gets passed a
       reference to a Template::Context object. It is through this context object that template
       code can access the features of the Template Toolkit.

       We described earlier how the Template::Service object calls on Template::Context to handle
       a process() request from the outside. We can make a similar request on a context to
       process a template, but from within the code of another template. This is a call from the
       inside.

           sub my_process_template {
               my $context = shift;
               my $output = $context->process('header', { title => 'Hello World' })
                          . "\nsome content\n"
                          . $context->process('footer');
           }

       This is then roughly equivalent to a source template something like this:

           [% PROCESS header
               title = 'Hello World'
           %]
           some content
           [% PROCESS footer %]

       Template variables are stored in, and managed by a Template::Stash object.  This is a
       blessed hash array in which template variables are defined. The object wrapper provides
       get() and set() method which implement all the magical.variable.features of the Template
       Toolkit.

       Each context object has its own stash, a reference to which can be returned by the
       appropriately named stash() method. So to print the value of some template variable, or
       for example, to represent the following source template:

           <title>[% title %]</title>

       we might have a subroutine definition something like this:

           sub {
               my $context = shift;
               my $stash = $context->stash();
               return '<title>' . $stash->get('title') . '</title>';
           }

       The stash get() method hides the details of the underlying variable types, automatically
       calling code references, checking return values, and performing other such tricks. If
       '"title"' happens to be bound to a subroutine then we can specify additional parameters as
       a list reference passed as the second argument to get().

           [% title('The Cat Sat on the Mat') %]

       This translates to the stash call:

           $stash->get([ 'title', ['The Cat Sat on the Mat'] ]);

       Dotted compound variables can be requested by passing a single list reference to the
       "get()" method in place of the variable name.  Each pair of elements in the list should
       correspond to the variable name and reference to a list of arguments for each dot-
       delimited element of the variable.

           [% foo(1, 2).bar(3, 4).baz(5) %]

       is thus equivalent to

           $stash->get([ foo => [1,2], bar => [3,4], baz => [5] ]);

       If there aren't any arguments for an element, you can specify an empty, zero or null
       argument list.

           [% foo.bar %]
           $stash->get([ 'foo', 0, 'bar', 0 ]);

       The set() method works in a similar way. It takes a variable name and a variable value
       which should be assigned to it.

           [% x = 10 %]
           $stash->set('x', 10);

           [% x.y = 10 %]
           $stash->set([ 'x', 0, 'y', 0 ], 10);

       So the stash gives us access to template variables and the context provides the higher
       level functionality.

       Alongside the process() method lies the include() method. Just as with the "PROCESS" /
       "INCLUDE" directives, the key difference is in variable localisation. Before processing a
       template, the "process()" method simply updates the stash to set any new variable
       definitions, overwriting any existing values. In contrast, the "include()" method creates
       a copy of the existing stash, in a process known as cloning the stash, and then uses that
       as a temporary variable store. Any previously existing variables are still defined, but
       any changes made to variables, including setting the new variable values passed aas
       arguments will affect only the local copy of the stash (although note that it's only a
       shallow copy, so it's not foolproof). When the template has been processed, the
       "include()" method restores the previous variable state by decloning the stash.

       The context also provides an insert() method to implement the "INSERT" directive, but no
       "wrapper()" method. This functionality can be implemented by rewriting the Perl code and
       calling "include()".

           [% WRAPPER foo -%]
              blah blah [% x %]
           [%- END %]

           $context->include('foo', {
               content => 'blah blah ' . $stash->get('x'),
           });

       Other than the template processing methods "process()", "include()" and "insert()", the
       context defines methods for fetching plugin objects, plugin(), and filters, filter().

           # TT USE directive
           [% USE foo = Bar(10) %]

           # equivalent Perl
           $stash->set('foo', $context->plugin('Bar', [10]));

           # TT FILTER block
           [% FILTER bar(20) %]
              blah blah blah
           [% END %]

           # equivalent Perl
           my $filter = $context->filter('bar', [20]);
           &$filter('blah blah blah');

       Pretty much everything else you might want to do in a template can be done in Perl code.
       Things like "IF", "UNLESS", "FOREACH" and so on all have direct counterparts in Perl.

           # TT IF directive
           [% IF msg %]
              Message: [% msg %]
           [% END %];

           # equivalent Perl
           if ($stash->get('msg')) {
               $output .=  'Message: ';
               $output .= $stash->get('msg');
           }

       The best way to get a better understanding of what's going on underneath the hood is to
       set the $Template::Parser::DEBUG flag to a true value and start processing templates.
       This will cause the parser to print the generated Perl code for each template it compiles
       to "STDERR".  You'll probably also want to set the $Template::Directive::PRETTY option to
       have the Perl pretty-printed for human consumption.

           use Template;
           use Template::Parser;
           use Template::Directive;

           $Template::Parser::DEBUG = 1;
           $Template::Directive::PRETTY = 1;

           my $template = Template->new();
           $template->process(\*DATA, { cat => 'dog', mat => 'log' });

           __DATA__
           The [% cat %] sat on the [% mat %]

       The output sent to "STDOUT" remains as you would expect:

           The dog sat on the log

       The output sent to "STDERR" would look something like this:

           compiled main template document block:
           sub {
               my $context = shift || die "template sub called without context\n";
               my $stash   = $context->stash;
               my $output  = '';
               my $error;

               eval { BLOCK: {
                   $output .=  "The ";
                   $output .=  $stash->get('cat');
                   $output .=  " sat on the ";
                   $output .=  $stash->get('mat');
                   $output .=  "\n";
               } };
               if ($@) {
                   $error = $context->catch($@, \$output);
                   die $error unless $error->type eq 'return';
               }

               return $output;
           }

Hacking on the Template Toolkit
       Please feel free to hack on the Template Toolkit.  If you find a bug that needs fixing, if
       you have an idea for something that's missing, or you feel inclined to tackle something on
       the TODO list, then by all means go ahead and do it!

       If you're contemplating something non-trivial then you'll probably want to bring it up on
       the mailing list first to get an idea about the current state of play, find out if
       anyone's already working on it, and so on.

       The source code repository for the Template Toolkit is hosted at Github.

           https://github.com/abw/Template2

       Clone the repository, make your changes, commit them, then send a pull request.

       Once you've made your changes, please remember to update the test suite by adding extra
       tests to one of the existing test scripts in the "t" sub-directory, or by adding a new
       test script of your own.  And of course, run "make test" to ensure that all the tests pass
       with your new code.

       Don't forget that any files you do add will need to be added to the MANIFEST.  Running
       "make manifest" will do this for you, but you need to make sure you haven't got any other
       temporary files lying around that might also get added to it.

       Documentation is often something that gets overlooked but it's just as important as the
       code. If you're adding a new module, a plugin module, for example, then it's OK to include
       the POD documentation in with the module, but please write it all in one piece at the end
       of the file, after the code (just look at any other "Template::*" module for an example).
       It's a religious issue, I know, but I have a strong distaste for POD documentation
       interspersed throughout the code. In my not-so-humble opinion, it makes both the code and
       the documentation harder to read (same kinda problem as embedding Perl in HTML).

       Then add a line to the Changes file giving a very brief description of what you've done.
       There's no need to go into detail here (save that for the commit message, comments in code
       or docuemtation where appropriate).

       Please also make sure you add your name to the lib/Template/Manual/Credits.pod file (if it
       isn't already there).

       Then commit your changes and send a pull request.

perl v5.30.0                                2019-10-19           Template::Manual::Internals(3pm)

Generated by $Id: phpMan.php,v 4.55 2007/09/05 04:42:51 chedong Exp $ Author: Che Dong
On Apache
Under GNU General Public License
2024-04-23 05:26 @3.138.134.107 CrawledBy Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; +claudebot@anthropic.com)
Valid XHTML 1.0!Valid CSS!