1#============================================================= -*-Perl-*-
2#
3# Template::Provider
4#
5# DESCRIPTION
6#   This module implements a class which handles the loading, compiling
7#   and caching of templates.  Multiple Template::Provider objects can
8#   be stacked and queried in turn to effect a Chain-of-Command between
9#   them.  A provider will attempt to return the requested template,
10#   an error (STATUS_ERROR) or decline to provide the template
11#   (STATUS_DECLINE), allowing subsequent providers to attempt to
12#   deliver it.   See 'Design Patterns' for further details.
13#
14# AUTHORS
15#   Andy Wardley <abw@wardley.org>
16#
17#   Refactored by Bill Moseley for v2.19 to add negative caching (i.e.
18#   tracking templates that are NOTFOUND so that we can decline quickly)
19#   and to provide better support for subclassing the provider.
20#
21# COPYRIGHT
22#   Copyright (C) 1996-2007 Andy Wardley.  All Rights Reserved.
23#
24#   This module is free software; you can redistribute it and/or
25#   modify it under the same terms as Perl itself.
26#
27# WARNING:
28#   This code is ugly and contorted and is being totally re-written for TT3.
29#   In particular, we'll be throwing errors rather than messing around
30#   returning (value, status) pairs.  With the benefit of hindsight, that
31#   was a really bad design decision on my part. I deserve to be knocked
32#   to the ground and kicked around a bit by hoards of angry TT developers
33#   for that one.  Bill's refactoring has made the module easier to subclass,
34#   (so you can ease off the kicking now), but it really needs to be totally
35#   redesigned and rebuilt from the ground up along with the bits of TT that
36#   use it.                                           -- abw 2007/04/27
37#============================================================================
38
39package Template::Provider;
40
41use strict;
42use warnings;
43use base 'Template::Base';
44use Template::Config;
45use Template::Constants;
46use Template::Document;
47use File::Basename;
48use File::Spec;
49
50use constant PREV    => 0;
51use constant NAME    => 1;   # template name -- indexed by this name in LOOKUP
52use constant DATA    => 2;   # Compiled template
53use constant LOAD    => 3;   # mtime of template
54use constant NEXT    => 4;   # link to next item in cache linked list
55use constant STAT    => 5;   # Time last stat()ed
56use constant MSWin32 => $^O eq 'MSWin32';
57
58our $VERSION = '3.010';
59our $DEBUG   = 0 unless defined $DEBUG;
60our $ERROR   = '';
61
62# name of document class
63our $DOCUMENT = 'Template::Document' unless defined $DOCUMENT;
64
65# maximum time between performing stat() on file to check staleness
66our $STAT_TTL = 1 unless defined $STAT_TTL;
67
68# maximum number of directories in an INCLUDE_PATH, to prevent runaways
69our $MAX_DIRS = 64 unless defined $MAX_DIRS;
70
71# UNICODE is supported in versions of Perl from 5.007 onwards
72our $UNICODE = $] > 5.007 ? 1 : 0;
73
74my $boms = [
75    'UTF-8'    => "\x{ef}\x{bb}\x{bf}",
76    'UTF-32BE' => "\x{0}\x{0}\x{fe}\x{ff}",
77    'UTF-32LE' => "\x{ff}\x{fe}\x{0}\x{0}",
78    'UTF-16BE' => "\x{fe}\x{ff}",
79    'UTF-16LE' => "\x{ff}\x{fe}",
80];
81
82# regex to match relative paths
83our $RELATIVE_PATH = qr[(?:^|/)\.+/];
84
85#========================================================================
86#                         -- PUBLIC METHODS --
87#========================================================================
88
89#------------------------------------------------------------------------
90# fetch($name)
91#
92# Returns a compiled template for the name specified by parameter.
93# The template is returned from the internal cache if it exists, or
94# loaded and then subsequently cached.  The ABSOLUTE and RELATIVE
95# configuration flags determine if absolute (e.g. '/something...')
96# and/or relative (e.g. './something') paths should be honoured.  The
97# INCLUDE_PATH is otherwise used to find the named file. $name may
98# also be a reference to a text string containing the template text,
99# or a file handle from which the content is read.  The compiled
100# template is not cached in these latter cases given that there is no
101# filename to cache under.  A subsequent call to store($name,
102# $compiled) can be made to cache the compiled template for future
103# fetch() calls, if necessary.
104#
105# Returns a compiled template or (undef, STATUS_DECLINED) if the
106# template could not be found.  On error (e.g. the file was found
107# but couldn't be read or parsed), the pair ($error, STATUS_ERROR)
108# is returned.  The TOLERANT configuration option can be set to
109# downgrade any errors to STATUS_DECLINE.
110#------------------------------------------------------------------------
111
112sub fetch {
113    my ($self, $name) = @_;
114    my ($data, $error);
115
116
117    if (ref $name) {
118        # $name can be a reference to a scalar, GLOB or file handle
119        ($data, $error) = $self->_load($name);
120        ($data, $error) = $self->_compile($data)
121            unless $error;
122        $data = $data->{ data }
123            unless $error;
124    }
125    elsif (File::Spec->file_name_is_absolute($name)) {
126        # absolute paths (starting '/') allowed if ABSOLUTE set
127        ($data, $error) = $self->{ ABSOLUTE }
128            ? $self->_fetch($name)
129            : $self->{ TOLERANT }
130                ? (undef, Template::Constants::STATUS_DECLINED)
131            : ("$name: absolute paths are not allowed (set ABSOLUTE option)",
132               Template::Constants::STATUS_ERROR);
133    }
134    elsif ($name =~ m/$RELATIVE_PATH/o) {
135        # anything starting "./" is relative to cwd, allowed if RELATIVE set
136        ($data, $error) = $self->{ RELATIVE }
137            ? $self->_fetch($name)
138            : $self->{ TOLERANT }
139                ? (undef, Template::Constants::STATUS_DECLINED)
140            : ("$name: relative paths are not allowed (set RELATIVE option)",
141               Template::Constants::STATUS_ERROR);
142    }
143    else {
144        # otherwise, it's a file name relative to INCLUDE_PATH
145        ($data, $error) = $self->{ INCLUDE_PATH }
146            ? $self->_fetch_path($name)
147            : (undef, Template::Constants::STATUS_DECLINED);
148    }
149
150    return ($data, $error);
151}
152
153
154#------------------------------------------------------------------------
155# store($name, $data)
156#
157# Store a compiled template ($data) in the cached as $name.
158# Returns compiled template
159#------------------------------------------------------------------------
160
161sub store {
162    my ($self, $name, $data, $mtime) = @_;
163    $self->_store($name, {
164        data  => $data,
165        load  => 0,
166        mtime => $mtime
167    });
168}
169
170
171#------------------------------------------------------------------------
172# load($name)
173#
174# Load a template without parsing/compiling it, suitable for use with
175# the INSERT directive.  There's some duplication with fetch() and at
176# some point this could be reworked to integrate them a little closer.
177#------------------------------------------------------------------------
178
179sub load {
180    my ($self, $name) = @_;
181    my ($data, $error);
182    my $path = $name;
183
184    if (File::Spec->file_name_is_absolute($name)) {
185        # absolute paths (starting '/') allowed if ABSOLUTE set
186        $error = "$name: absolute paths are not allowed (set ABSOLUTE option)"
187            unless $self->{ ABSOLUTE };
188    }
189    elsif ($name =~ m[$RELATIVE_PATH]o) {
190        # anything starting "./" is relative to cwd, allowed if RELATIVE set
191        $error = "$name: relative paths are not allowed (set RELATIVE option)"
192            unless $self->{ RELATIVE };
193    }
194    else {
195      INCPATH: {
196          # otherwise, it's a file name relative to INCLUDE_PATH
197          my $paths = $self->paths()
198              || return ($self->error(), Template::Constants::STATUS_ERROR);
199
200          foreach my $dir (@$paths) {
201              $path = File::Spec->catfile($dir, $name);
202              last INCPATH
203                  if defined $self->_template_modified($path);
204          }
205          undef $path;      # not found
206      }
207    }
208
209    # Now fetch the content
210    ($data, $error) = $self->_template_content($path)
211        if defined $path && !$error;
212
213    if ($error) {
214        return $self->{ TOLERANT }
215            ? (undef, Template::Constants::STATUS_DECLINED)
216            : ($error, Template::Constants::STATUS_ERROR);
217    }
218    elsif (! defined $path) {
219        return (undef, Template::Constants::STATUS_DECLINED);
220    }
221    else {
222        return ($data, Template::Constants::STATUS_OK);
223    }
224}
225
226
227
228#------------------------------------------------------------------------
229# include_path(\@newpath)
230#
231# Accessor method for the INCLUDE_PATH setting.  If called with an
232# argument, this method will replace the existing INCLUDE_PATH with
233# the new value.
234#------------------------------------------------------------------------
235
236sub include_path {
237     my ($self, $path) = @_;
238     $self->{ INCLUDE_PATH } = $path if $path;
239     return $self->{ INCLUDE_PATH };
240}
241
242
243#------------------------------------------------------------------------
244# paths()
245#
246# Evaluates the INCLUDE_PATH list, ignoring any blank entries, and
247# calling and subroutine or object references to return dynamically
248# generated path lists.  Returns a reference to a new list of paths
249# or undef on error.
250#------------------------------------------------------------------------
251
252sub paths {
253    my $self   = shift;
254    my @ipaths = @{ $self->{ INCLUDE_PATH } };
255    my (@opaths, $dpaths, $dir);
256    my $count = $MAX_DIRS;
257
258    while (@ipaths && --$count) {
259        $dir = shift @ipaths || next;
260
261        # $dir can be a sub or object ref which returns a reference
262        # to a dynamically generated list of search paths.
263
264        if (ref $dir eq 'CODE') {
265            eval { $dpaths = &$dir() };
266            if ($@) {
267                chomp $@;
268                return $self->error($@);
269            }
270            unshift(@ipaths, @$dpaths);
271            next;
272        }
273        elsif (ref($dir) && UNIVERSAL::can($dir, 'paths')) {
274            $dpaths = $dir->paths()
275                || return $self->error($dir->error());
276            unshift(@ipaths, @$dpaths);
277            next;
278        }
279        else {
280            push(@opaths, $dir);
281        }
282    }
283    return $self->error("INCLUDE_PATH exceeds $MAX_DIRS directories")
284        if @ipaths;
285
286    return \@opaths;
287}
288
289
290#------------------------------------------------------------------------
291# DESTROY
292#
293# The provider cache is implemented as a doubly linked list which Perl
294# cannot free by itself due to the circular references between NEXT <=>
295# PREV items.  This cleanup method walks the list deleting all the NEXT/PREV
296# references, allowing the proper cleanup to occur and memory to be
297# repooled.
298#------------------------------------------------------------------------
299
300sub DESTROY {
301    my $self = shift;
302    my ($slot, $next);
303
304    $slot = $self->{ HEAD };
305    while ($slot) {
306        $next = $slot->[ NEXT ];
307        undef $slot->[ PREV ];
308        undef $slot->[ NEXT ];
309        $slot = $next;
310    }
311    undef $self->{ HEAD };
312    undef $self->{ TAIL };
313}
314
315
316
317
318#========================================================================
319#                        -- PRIVATE METHODS --
320#========================================================================
321
322#------------------------------------------------------------------------
323# _init()
324#
325# Initialise the cache.
326#------------------------------------------------------------------------
327
328sub _init {
329    my ($self, $params) = @_;
330    my $size = $params->{ CACHE_SIZE   };
331    my $path = $params->{ INCLUDE_PATH } || '.';
332    my $cdir = $params->{ COMPILE_DIR  } || '';
333    my $dlim = $params->{ DELIMITER    };
334    my $debug;
335
336    # tweak delim to ignore C:/
337    unless (defined $dlim) {
338        $dlim = MSWin32 ? ':(?!\\/)' : ':';
339    }
340
341    # coerce INCLUDE_PATH to an array ref, if not already so
342    $path = [ split(/$dlim/, $path) ]
343        unless ref $path eq 'ARRAY';
344
345    # don't allow a CACHE_SIZE 1 because it breaks things and the
346    # additional checking isn't worth it
347    $size = 2
348        if defined $size && ($size == 1 || $size < 0);
349
350    if (defined ($debug = $params->{ DEBUG })) {
351        $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PROVIDER
352                                    | Template::Constants::DEBUG_FLAGS );
353    }
354    else {
355        $self->{ DEBUG } = $DEBUG;
356    }
357
358    if ($self->{ DEBUG }) {
359        local $" = ', ';
360        $self->debug("creating cache of ",
361                     defined $size ? $size : 'unlimited',
362                     " slots for [ @$path ]");
363    }
364
365    # create COMPILE_DIR and sub-directories representing each INCLUDE_PATH
366    # element in which to store compiled files
367    if ($cdir) {
368        require File::Path;
369        foreach my $dir (@$path) {
370            next if ref $dir;
371            my $wdir = $dir;
372            $wdir =~ tr[:][]d if MSWin32;
373            {
374                no warnings 'syntax';
375                $wdir = each %{ { $wdir => undef } } if ${^TAINT};    #untaint
376            }
377            $wdir = File::Spec->catfile($cdir, $wdir);
378            File::Path::mkpath($wdir) unless -d $wdir;
379        }
380    }
381
382    $self->{ LOOKUP       } = { };
383    $self->{ NOTFOUND     } = { };  # Tracks templates *not* found.
384    $self->{ SLOTS        } = 0;
385    $self->{ SIZE         } = $size;
386    $self->{ INCLUDE_PATH } = $path;
387    $self->{ DELIMITER    } = $dlim;
388    $self->{ COMPILE_DIR  } = $cdir;
389    $self->{ COMPILE_EXT  } = $params->{ COMPILE_EXT } || '';
390    $self->{ ABSOLUTE     } = $params->{ ABSOLUTE } || 0;
391    $self->{ RELATIVE     } = $params->{ RELATIVE } || 0;
392    $self->{ TOLERANT     } = $params->{ TOLERANT } || 0;
393    $self->{ DOCUMENT     } = $params->{ DOCUMENT } || $DOCUMENT;
394    $self->{ PARSER       } = $params->{ PARSER   };
395    $self->{ DEFAULT      } = $params->{ DEFAULT  };
396    $self->{ ENCODING     } = $params->{ ENCODING };
397#   $self->{ PREFIX       } = $params->{ PREFIX   };
398    $self->{ STAT_TTL     } = $params->{ STAT_TTL } || $STAT_TTL;
399    $self->{ PARAMS       } = $params;
400
401    # look for user-provided UNICODE parameter or use default from package var
402    $self->{ UNICODE      } = defined $params->{ UNICODE }
403                                    ? $params->{ UNICODE } : $UNICODE;
404
405    return $self;
406}
407
408
409#------------------------------------------------------------------------
410# _fetch($name, $t_name)
411#
412# Fetch a file from cache or disk by specification of an absolute or
413# relative filename.  No search of the INCLUDE_PATH is made.  If the
414# file is found and loaded, it is compiled and cached.
415# Call with:
416#   $name       = path to search (possible prefixed by INCLUDE_PATH)
417#   $t_name     = template name
418#------------------------------------------------------------------------
419
420sub _fetch {
421    my ($self, $name, $t_name) = @_;
422    my $stat_ttl = $self->{ STAT_TTL };
423
424    $self->debug("_fetch($name)") if $self->{ DEBUG };
425
426    # First see if the named template is in the memory cache
427    if ((my $slot = $self->{ LOOKUP }->{ $name })) {
428        # Test if cache is fresh, and reload/compile if not.
429        my ($data, $error) = $self->_refresh($slot);
430
431        return $error
432            ? ( $data, $error )     # $data may contain error text
433            : $slot->[ DATA ];      # returned document object
434    }
435
436    # Otherwise, see if we already know the template is not found
437    if (my $last_stat_time = $self->{ NOTFOUND }->{ $name }) {
438        my $expires_in = $last_stat_time + $stat_ttl - time;
439        if ($expires_in > 0) {
440            $self->debug(" file [$name] in negative cache.  Expires in $expires_in seconds")
441                if $self->{ DEBUG };
442            return (undef, Template::Constants::STATUS_DECLINED);
443        }
444        else {
445            delete $self->{ NOTFOUND }->{ $name };
446        }
447    }
448
449    my($template,$error);
450    my $uncompiled_template_mtime = $self->_template_modified( $name );  # does template exist?
451
452    # some templates like Provider::FromDATA does not provide mtime information
453    $uncompiled_template_mtime = 0 unless defined $uncompiled_template_mtime;
454
455    # Is there an up-to-date compiled version on disk?
456    if (my $template_mtime = $self->_compiled_is_current($name, $uncompiled_template_mtime)) {
457        # require() the compiled template.
458        my $compiled_template = $self->_load_compiled( $self->_compiled_filename($name) );
459
460        # Store and return the compiled template
461        return $self->store( $name, $compiled_template, $template_mtime ) if $compiled_template;
462
463        # Problem loading compiled template:
464        # warn and continue to fetch source template
465        warn($self->error(), "\n");
466    }
467
468    # load template from source
469    ($template, $error) = $self->_load($name, $t_name);
470
471    if ($error) {
472        # Template could not be fetched.  Add to the negative/notfound cache.
473        $self->{ NOTFOUND }->{ $name } = time;
474        return ( $template, $error );
475    }
476
477    # compile template source
478    ($template, $error) = $self->_compile($template, $self->_compiled_filename($name) );
479
480    if ($error) {
481        # return any compile time error
482        return ($template, $error);
483    }
484    else {
485        # Store compiled template and return it
486        return $self->store($name, $template->{data}) ;
487    }
488}
489
490
491#------------------------------------------------------------------------
492# _fetch_path($name)
493#
494# Fetch a file from cache or disk by specification of an absolute cache
495# name (e.g. 'header') or filename relative to one of the INCLUDE_PATH
496# directories.  If the file isn't already cached and can be found and
497# loaded, it is compiled and cached under the full filename.
498#------------------------------------------------------------------------
499
500sub _fetch_path {
501    my ($self, $name) = @_;
502
503    $self->debug("_fetch_path($name)") if $self->{ DEBUG };
504
505    # the template may have been stored using a non-filename name
506    # so look for the plain name in the cache first
507    if ((my $slot = $self->{ LOOKUP }->{ $name })) {
508        # cached entry exists, so refresh slot and extract data
509        my ($data, $error) = $self->_refresh($slot);
510
511        return $error
512            ? ($data, $error)
513            : ($slot->[ DATA ], $error );
514    }
515
516    my $paths = $self->paths
517        || return ( $self->error, Template::Constants::STATUS_ERROR );
518
519    # search the INCLUDE_PATH for the file, in cache or on disk
520    foreach my $dir (@$paths) {
521        my $path = File::Spec->catfile($dir, $name);
522
523        $self->debug("searching path: $path\n") if $self->{ DEBUG };
524
525        my ($data, $error) = $self->_fetch( $path, $name );
526
527        # Return if no error or if a serious error.
528        return ( $data, $error )
529            if !$error || $error == Template::Constants::STATUS_ERROR;
530
531    }
532
533    # not found in INCLUDE_PATH, now try DEFAULT
534    return $self->_fetch_path( $self->{DEFAULT} )
535        if defined $self->{DEFAULT} && $name ne $self->{DEFAULT};
536
537    # We could not handle this template name
538    return (undef, Template::Constants::STATUS_DECLINED);
539}
540
541sub _compiled_filename {
542    my ($self, $file) = @_;
543
544    return $self->{ COMPILEDPATH }{$file} if $self->{ COMPILEDPATH }{$file};
545
546    my ($compext, $compdir) = @$self{ qw( COMPILE_EXT COMPILE_DIR ) };
547    my ($path, $compiled);
548
549    return undef
550        unless $compext || $compdir;
551
552    $path = $file;
553    $path or die "invalid filename: $path";
554    $path =~ tr[:][]d if MSWin32;
555
556
557    $compiled = "$path$compext";
558    $self->{ COMPILEDPATH }{$file} = $compiled = File::Spec->catfile($compdir, $compiled) if length $compdir;
559
560    return $compiled;
561}
562
563sub _load_compiled {
564    my ($self, $file) = @_;
565
566    # Implicitly Relative paths are not supported
567    # by "require" and invoke @INC traversal, where relative
568    # paths only traditionally worked prior to Perl 5.26
569    # due to the presence of '.' in @INC
570    #
571    # Given load_compiled never wants to traverse @INC, forcing
572    # an absolute path for the loaded file and the INC key is
573    # sensible.
574    #
575    # NB: %INC Keys are always identical to their respective
576    # "require" invocations regardless of OS, and the only time
577    # one needs to care about slash direction is when dealing
578    # with Module::Name -> Module/Name.pm translation.
579    my $fpath = File::Spec->rel2abs( $file );
580
581    return $self->error("compiled template missing path") unless defined $fpath;
582
583    ($fpath) = $fpath =~ /^(.*)$/s;
584
585    my $compiled;
586
587    # load compiled template via require();  we zap any
588    # %INC entry to ensure it is reloaded (we don't
589    # want 1 returned by require() to say it's in memory)
590    delete $INC{ $fpath };
591    eval { $compiled = require $fpath; };
592    return $@
593        ? $self->error("compiled template $compiled: $@")
594        : $compiled;
595}
596
597#------------------------------------------------------------------------
598# _load($name, $alias)
599#
600# Load template text from a string ($name = scalar ref), GLOB or file
601# handle ($name = ref), or from an absolute filename ($name = scalar).
602# Returns a hash array containing the following items:
603#   name    filename or $alias, if provided, or 'input text', etc.
604#   text    template text
605#   time    modification time of file, or current time for handles/strings
606#   load    time file was loaded (now!)
607#
608# On error, returns ($error, STATUS_ERROR), or (undef, STATUS_DECLINED)
609# if TOLERANT is set.
610#------------------------------------------------------------------------
611
612sub _load {
613    my ($self, $name, $alias) = @_;
614    my ($data, $error);
615    my $tolerant = $self->{ TOLERANT };
616    my $now = time;
617
618    $alias = $name unless defined $alias or ref $name;
619
620    $self->debug("_load($name, ", defined $alias ? $alias : '<no alias>',
621                 ')') if $self->{ DEBUG };
622
623    # SCALAR ref is the template text
624    if (ref $name eq 'SCALAR') {
625        # $name can be a SCALAR reference to the input text...
626        return {
627            name => defined $alias ? $alias : 'input text',
628            path => defined $alias ? $alias : 'input text',
629            text => $$name,
630            time => $now,
631            load => 0,
632        };
633    }
634
635    # Otherwise, assume GLOB as a file handle
636    if (ref $name) {
637        local $/;
638        my $text = <$name>;
639        $text = $self->_decode_unicode($text) if $self->{ UNICODE };
640        return {
641            name => defined $alias ? $alias : 'input file handle',
642            path => defined $alias ? $alias : 'input file handle',
643            text => $text,
644            time => $now,
645            load => 0,
646        };
647    }
648
649    # Otherwise, it's the name of the template
650    if ( defined $self->_template_modified( $name ) ) {  # does template exist?
651        my ($text, $error, $mtime ) = $self->_template_content( $name );
652        unless ( $error )  {
653            $text = $self->_decode_unicode($text) if $self->{ UNICODE };
654            return {
655                name => $alias,
656                path => $name,
657                text => $text,
658                time => $mtime,
659                load => $now,
660            };
661        }
662
663        return ( $error, Template::Constants::STATUS_ERROR )
664            unless $tolerant;
665    }
666
667    # Unable to process template, pass onto the next Provider.
668    return (undef, Template::Constants::STATUS_DECLINED);
669}
670
671
672#------------------------------------------------------------------------
673# _refresh(\@slot)
674#
675# Private method called to mark a cache slot as most recently used.
676# A reference to the slot array should be passed by parameter.  The
677# slot is relocated to the head of the linked list.  If the file from
678# which the data was loaded has been updated since it was compiled, then
679# it is re-loaded from disk and re-compiled.
680#------------------------------------------------------------------------
681
682sub _refresh {
683    my ($self, $slot) = @_;
684    my $stat_ttl = $self->{ STAT_TTL };
685    my ($head, $file, $data, $error);
686
687    $self->debug("_refresh([ ",
688                 join(', ', map { defined $_ ? $_ : '<undef>' } @$slot),
689                 '])') if $self->{ DEBUG };
690
691    # if it's more than $STAT_TTL seconds since we last performed a
692    # stat() on the file then we need to do it again and see if the file
693    # time has changed
694    my $now = time;
695    my $expires_in_sec = $slot->[ STAT ] + $stat_ttl - $now;
696
697    if ( $expires_in_sec <= 0 ) {  # Time to check!
698        $slot->[ STAT ] = $now;
699
700        # Grab mtime of template.
701        # Seems like this should be abstracted to compare to
702        # just ask for a newer compiled template (if it's newer)
703        # and let that check for a newer template source.
704        my $template_mtime = $self->_template_modified( $slot->[ NAME ] );
705        if ( ! defined $template_mtime || ( $template_mtime != $slot->[ LOAD ] )) {
706            $self->debug("refreshing cache file ", $slot->[ NAME ])
707                if $self->{ DEBUG };
708
709            ($data, $error) = $self->_load($slot->[ NAME ], $slot->[ DATA ]->{ name });
710            ($data, $error) = $self->_compile($data)
711                unless $error;
712
713            if ($error) {
714                # if the template failed to load/compile then we wipe out the
715                # STAT entry.  This forces the provider to try and reload it
716                # each time instead of using the previously cached version
717                # until $STAT_TTL is next up
718                $slot->[ STAT ] = 0;
719            }
720            else {
721                $slot->[ DATA ] = $data->{ data };
722                $slot->[ LOAD ] = $data->{ time };
723            }
724        }
725
726    } elsif ( $self->{ DEBUG } ) {
727        $self->debug( sprintf('STAT_TTL not met for file [%s].  Expires in %d seconds',
728                        $slot->[ NAME ], $expires_in_sec ) );
729    }
730
731    # Move this slot to the head of the list
732    unless( $self->{ HEAD } == $slot ) {
733        # remove existing slot from usage chain...
734        if ($slot->[ PREV ]) {
735            $slot->[ PREV ]->[ NEXT ] = $slot->[ NEXT ];
736        }
737        else {
738            $self->{ HEAD } = $slot->[ NEXT ];
739        }
740        if ($slot->[ NEXT ]) {
741            $slot->[ NEXT ]->[ PREV ] = $slot->[ PREV ];
742        }
743        else {
744            $self->{ TAIL } = $slot->[ PREV ];
745        }
746
747        # ..and add to start of list
748        $head = $self->{ HEAD };
749        $head->[ PREV ] = $slot if $head;
750        $slot->[ PREV ] = undef;
751        $slot->[ NEXT ] = $head;
752        $self->{ HEAD } = $slot;
753    }
754
755    return ($data, $error);
756}
757
758
759
760#------------------------------------------------------------------------
761# _store($name, $data)
762#
763# Private method called to add a data item to the cache.  If the cache
764# size limit has been reached then the oldest entry at the tail of the
765# list is removed and its slot relocated to the head of the list and
766# reused for the new data item.  If the cache is under the size limit,
767# or if no size limit is defined, then the item is added to the head
768# of the list.
769# Returns compiled template
770#------------------------------------------------------------------------
771
772sub _store {
773    my ($self, $name, $data, $compfile) = @_;
774    my $size = $self->{ SIZE };
775    my ($slot, $head);
776
777    # Return if memory cache disabled.  (overriding code should also check)
778    # $$$ What's the expected behaviour of store()?  Can't tell from the
779    # docs if you can call store() when SIZE = 0.
780    return $data->{data} if defined $size and !$size;
781
782    # check the modification time -- extra stat here
783    my $load = $data->{ mtime } || $self->_modified($name);
784
785    # extract the compiled template from the data hash
786    $data = $data->{ data };
787    $self->debug("_store($name, $data)") if $self->{ DEBUG };
788
789    if (defined $size && $self->{ SLOTS } >= $size) {
790        # cache has reached size limit, so reuse oldest entry
791        $self->debug("reusing oldest cache entry (size limit reached: $size)\nslots: $self->{ SLOTS }") if $self->{ DEBUG };
792
793        # remove entry from tail of list
794        $slot = $self->{ TAIL };
795        $slot->[ PREV ]->[ NEXT ] = undef;
796        $self->{ TAIL } = $slot->[ PREV ];
797
798        # remove name lookup for old node
799        delete $self->{ LOOKUP }->{ $slot->[ NAME ] };
800
801        # add modified node to head of list
802        $head = $self->{ HEAD };
803        $head->[ PREV ] = $slot if $head;
804        @$slot = ( undef, $name, $data, $load, $head, time );
805        $self->{ HEAD } = $slot;
806
807        # add name lookup for new node
808        $self->{ LOOKUP }->{ $name } = $slot;
809    }
810    else {
811        # cache is under size limit, or none is defined
812
813        $self->debug("adding new cache entry") if $self->{ DEBUG };
814
815        # add new node to head of list
816        $head = $self->{ HEAD };
817        $slot = [ undef, $name, $data, $load, $head, time ];
818        $head->[ PREV ] = $slot if $head;
819        $self->{ HEAD } = $slot;
820        $self->{ TAIL } = $slot unless $self->{ TAIL };
821
822        # add lookup from name to slot and increment nslots
823        $self->{ LOOKUP }->{ $name } = $slot;
824        $self->{ SLOTS }++;
825    }
826
827    return $data;
828}
829
830
831#------------------------------------------------------------------------
832# _compile($data)
833#
834# Private method called to parse the template text and compile it into
835# a runtime form.  Creates and delegates a Template::Parser object to
836# handle the compilation, or uses a reference passed in PARSER.  On
837# success, the compiled template is stored in the 'data' item of the
838# $data hash and returned.  On error, ($error, STATUS_ERROR) is returned,
839# or (undef, STATUS_DECLINED) if the TOLERANT flag is set.
840# The optional $compiled parameter may be passed to specify
841# the name of a compiled template file to which the generated Perl
842# code should be written.  Errors are (for now...) silently
843# ignored, assuming that failures to open a file for writing are
844# intentional (e.g directory write permission).
845#------------------------------------------------------------------------
846
847sub _compile {
848    my ($self, $data, $compfile) = @_;
849    my $text = $data->{ text };
850    my ($parsedoc, $error);
851
852    $self->debug("_compile($data, ",
853                 defined $compfile ? $compfile : '<no compfile>', ')')
854        if $self->{ DEBUG };
855
856    my $parser = $self->{ PARSER }
857        ||= Template::Config->parser($self->{ PARAMS })
858        ||  return (Template::Config->error(), Template::Constants::STATUS_ERROR);
859
860    # discard the template text - we don't need it any more
861    delete $data->{ text };
862
863    # call parser to compile template into Perl code
864    if ($parsedoc = $parser->parse($text, $data)) {
865
866        $parsedoc->{ METADATA } = {
867            'name'    => $data->{ name },
868            'modtime' => $data->{ 'time' },
869            %{ $parsedoc->{ METADATA } },
870        };
871
872        # write the Perl code to the file $compfile, if defined
873        if ($compfile) {
874            my $basedir = &File::Basename::dirname($compfile);
875            {
876                no warnings 'syntax';
877                $basedir = each %{ { $basedir => undef } } if ${^TAINT};    #untaint
878            }
879
880            unless (-d $basedir) {
881                eval { File::Path::mkpath($basedir) };
882                $error = "failed to create compiled templates directory: $basedir ($@)"
883                    if ($@);
884            }
885
886            unless ($error) {
887                my $docclass = $self->{ DOCUMENT };
888                $error = 'cache failed to write '
889                    . &File::Basename::basename($compfile)
890                    . ': ' . $docclass->error()
891                    unless $docclass->write_perl_file($compfile, $parsedoc);
892            }
893
894            # set atime and mtime of newly compiled file, don't bother
895            # if time is undef
896            if (!defined($error) && defined $data->{ 'time' }) {
897                my $cfile = do {
898                    no warnings 'syntax';
899                    each %{ { $compfile => undef } };
900                };
901                if (!length $cfile) {
902                    return("invalid filename: $compfile",
903                           Template::Constants::STATUS_ERROR);
904                };
905
906                my $ctime = $data->{ time };
907                if (!length $ctime || $ctime =~ tr{0-9}{}c) {
908                    return("invalid time: $ctime",
909                           Template::Constants::STATUS_ERROR);
910                }
911                utime($ctime, $ctime, $cfile);
912
913                $self->debug(" cached compiled template to file [$compfile]")
914                    if $self->{ DEBUG };
915            }
916        }
917
918        unless ($error) {
919            return $data                                        ## RETURN ##
920                if $data->{ data } = $DOCUMENT->new($parsedoc);
921            $error = $Template::Document::ERROR;
922        }
923    }
924    else {
925        $error = Template::Exception->new( 'parse', "$data->{ name } " .
926                                           $parser->error() );
927    }
928
929    # return STATUS_ERROR, or STATUS_DECLINED if we're being tolerant
930    return $self->{ TOLERANT }
931        ? (undef, Template::Constants::STATUS_DECLINED)
932        : ($error,  Template::Constants::STATUS_ERROR)
933}
934
935#------------------------------------------------------------------------
936# _compiled_is_current( $template_name )
937#
938# Returns true if $template_name and its compiled name
939# exist and they have the same mtime.
940#------------------------------------------------------------------------
941
942sub _compiled_is_current {
943    my ( $self, $template_name, $uncompiled_template_mtime ) = @_;
944
945    my $compiled_name   = $self->_compiled_filename($template_name);
946    return unless defined $compiled_name;
947
948    my $compiled_mtime  = (stat($compiled_name))[9];
949    return unless defined $compiled_mtime;
950
951    my $template_mtime  = $uncompiled_template_mtime || $self->_template_modified( $template_name )  or return;
952    return unless defined $template_mtime;
953
954    # This was >= in the 2.15, but meant that downgrading
955    # a source template would not get picked up.
956    return $compiled_mtime == $template_mtime ?  $template_mtime : 0;
957}
958
959
960#------------------------------------------------------------------------
961# _template_modified($path)
962#
963# Returns the last modified time of the $path.
964# Returns undef if the path does not exist.
965# Override if templates are not on disk, for example
966#------------------------------------------------------------------------
967
968sub _template_modified {
969    my $self = shift;
970    my $template = shift || return;
971    return (stat( $template ))[9];
972}
973
974#------------------------------------------------------------------------
975# _template_content($path)
976#
977# Fetches content pointed to by $path.
978# Returns the content in scalar context.
979# Returns ($data, $error, $mtime) in list context where
980#   $data       - content
981#   $error      - error string if there was an error, otherwise undef
982#   $mtime      - last modified time from calling stat() on the path
983#------------------------------------------------------------------------
984
985sub _template_content {
986    my ($self, $path) = @_;
987
988    return (undef, "No path specified to fetch content from ")
989        unless $path;
990
991    my $data;
992    my $mod_date;
993    my $error;
994
995    local *FH;
996    if(-d $path) {
997        $error = "$path: not a file";
998    }
999    elsif (open(FH, "<", $path)) {
1000        local $/;
1001        binmode(FH);
1002        $data = <FH>;
1003        $mod_date = (stat($path))[9];
1004        close(FH);
1005    }
1006    else {
1007        $error = "$path: $!";
1008    }
1009
1010    return wantarray
1011        ? ( $data, $error, $mod_date )
1012        : $data;
1013}
1014
1015
1016#------------------------------------------------------------------------
1017# _modified($name)
1018# _modified($name, $time)
1019#
1020# When called with a single argument, it returns the modification time
1021# of the named template.  When called with a second argument it returns
1022# true if $name has been modified since $time.
1023#------------------------------------------------------------------------
1024
1025sub _modified {
1026    my ($self, $name, $time) = @_;
1027    my $load = $self->_template_modified($name);
1028    return $time ? 1 : 0 unless defined $load;
1029
1030    return $time
1031         ? $load > $time
1032         : $load;
1033}
1034
1035#------------------------------------------------------------------------
1036# _decode_unicode
1037#
1038# Decodes encoded unicode text that starts with a BOM and
1039# turns it into perl's internal representation
1040#------------------------------------------------------------------------
1041
1042sub _decode_unicode {
1043    my $self   = shift;
1044    my $string = shift;
1045    return undef unless defined $string;
1046
1047    use bytes;
1048    require Encode;
1049
1050    return $string if Encode::is_utf8( $string );
1051
1052    # try all the BOMs in order looking for one (order is important
1053    # 32bit BOMs look like 16bit BOMs)
1054
1055    my $count  = 0;
1056
1057    while ($count < @{ $boms }) {
1058        my $enc = $boms->[$count++];
1059        my $bom = $boms->[$count++];
1060
1061        # does the string start with the bom?
1062        if ($bom eq substr($string, 0, length($bom))) {
1063            # decode it and hand it back
1064            return Encode::decode($enc, substr($string, length($bom)), 1);
1065        }
1066    }
1067
1068    return $self->{ ENCODING }
1069        ? Encode::decode( $self->{ ENCODING }, $string )
1070        : $string;
1071}
1072
1073
10741;
1075
1076__END__
1077
1078=head1 NAME
1079
1080Template::Provider - Provider module for loading/compiling templates
1081
1082=head1 SYNOPSIS
1083
1084    $provider = Template::Provider->new(\%options);
1085
1086    ($template, $error) = $provider->fetch($name);
1087
1088=head1 DESCRIPTION
1089
1090The L<Template::Provider> is used to load, parse, compile and cache template
1091documents. This object may be sub-classed to provide more specific facilities
1092for loading, or otherwise providing access to templates.
1093
1094The L<Template::Context> objects maintain a list of L<Template::Provider>
1095objects which are polled in turn (via L<fetch()|Template::Context#fetch()>) to
1096return a requested template. Each may return a compiled template, raise an
1097error, or decline to serve the request, giving subsequent providers a chance
1098to do so.
1099
1100The L<Template::Provider> can also be subclassed to provide templates from
1101a different source, e.g. a database. See L<SUBCLASSING> below.
1102
1103This documentation needs work.
1104
1105=head1 PUBLIC METHODS
1106
1107=head2 new(\%options)
1108
1109Constructor method which instantiates and returns a new C<Template::Provider>
1110object.  A reference to a hash array of configuration options may be passed.
1111
1112See L<CONFIGURATION OPTIONS> below for a summary of configuration options
1113and L<Template::Manual::Config> for full details.
1114
1115=head2 fetch($name)
1116
1117Returns a compiled template for the name specified. If the template cannot be
1118found then C<(undef, STATUS_DECLINED)> is returned. If an error occurs (e.g.
1119read error, parse error) then C<($error, STATUS_ERROR)> is returned, where
1120C<$error> is the error message generated. If the L<TOLERANT> option is set the
1121the method returns C<(undef, STATUS_DECLINED)> instead of returning an error.
1122
1123=head2 load($name)
1124
1125Loads a template without parsing or compiling it.  This is used by the
1126the L<INSERT|Template::Manual::Directives#INSERT> directive.
1127
1128=head2 store($name, $template)
1129
1130Stores the compiled template, C<$template>, in the cache under the name,
1131C<$name>.  Susbequent calls to C<fetch($name)> will return this template in
1132preference to any disk-based file.
1133
1134=head2 include_path(\@newpath)
1135
1136Accessor method for the C<INCLUDE_PATH> setting.  If called with an
1137argument, this method will replace the existing C<INCLUDE_PATH> with
1138the new value.
1139
1140=head2 paths()
1141
1142This method generates a copy of the C<INCLUDE_PATH> list.  Any elements in the
1143list which are dynamic generators (e.g. references to subroutines or objects
1144implementing a C<paths()> method) will be called and the list of directories
1145returned merged into the output list.
1146
1147It is possible to provide a generator which returns itself, thus sending
1148this method into an infinite loop.  To detect and prevent this from happening,
1149the C<$MAX_DIRS> package variable, set to C<64> by default, limits the maximum
1150number of paths that can be added to, or generated for the output list.  If
1151this number is exceeded then the method will immediately return an error
1152reporting as much.
1153
1154=head1 CONFIGURATION OPTIONS
1155
1156The following list summarises the configuration options that can be provided
1157to the C<Template::Provider> L<new()> constructor. Please consult
1158L<Template::Manual::Config> for further details and examples of each
1159configuration option in use.
1160
1161=head2 INCLUDE_PATH
1162
1163The L<INCLUDE_PATH|Template::Manual::Config#INCLUDE_PATH> option is used to
1164specify one or more directories in which template files are located.
1165
1166    # single path
1167    my $provider = Template::Provider->new({
1168        INCLUDE_PATH => '/usr/local/templates',
1169    });
1170
1171    # multiple paths
1172    my $provider = Template::Provider->new({
1173        INCLUDE_PATH => [ '/usr/local/templates',
1174                          '/tmp/my/templates' ],
1175    });
1176
1177=head2 ABSOLUTE
1178
1179The L<ABSOLUTE|Template::Manual::Config#ABSOLUTE> flag is used to indicate if
1180templates specified with absolute filenames (e.g. 'C</foo/bar>') should be
1181processed. It is disabled by default and any attempt to load a template by
1182such a name will cause a 'C<file>' exception to be raised.
1183
1184    my $provider = Template::Provider->new({
1185        ABSOLUTE => 1,
1186    });
1187
1188=head2 RELATIVE
1189
1190The L<RELATIVE|Template::Manual::Config#RELATIVE> flag is used to indicate if
1191templates specified with filenames relative to the current directory (e.g.
1192C<./foo/bar> or C<../../some/where/else>) should be loaded. It is also disabled
1193by default, and will raise a C<file> error if such template names are
1194encountered.
1195
1196    my $provider = Template::Provider->new({
1197        RELATIVE => 1,
1198    });
1199
1200=head2 DEFAULT
1201
1202The L<DEFAULT|Template::Manual::Config#DEFAULT> option can be used to specify
1203a default template which should be used whenever a specified template can't be
1204found in the L<INCLUDE_PATH>.
1205
1206    my $provider = Template::Provider->new({
1207        DEFAULT => 'notfound.html',
1208    });
1209
1210If a non-existant template is requested through the L<Template>
1211L<process()|Template#process()> method, or by an C<INCLUDE>, C<PROCESS> or
1212C<WRAPPER> directive, then the C<DEFAULT> template will instead be processed, if
1213defined. Note that the C<DEFAULT> template is not used when templates are
1214specified with absolute or relative filenames, or as a reference to a input
1215file handle or text string.
1216
1217=head2 ENCODING
1218
1219The Template Toolkit will automatically decode Unicode templates that
1220have a Byte Order Marker (BOM) at the start of the file.  This option
1221can be used to set the default encoding for templates that don't define
1222a BOM.
1223
1224    my $provider = Template::Provider->new({
1225        ENCODING => 'utf8',
1226    });
1227
1228See L<Encode> for further information.
1229
1230=head2 CACHE_SIZE
1231
1232The L<CACHE_SIZE|Template::Manual::Config#CACHE_SIZE> option can be used to
1233limit the number of compiled templates that the module should cache. By
1234default, the L<CACHE_SIZE|Template::Manual::Config#CACHE_SIZE> is undefined
1235and all compiled templates are cached.
1236
1237    my $provider = Template::Provider->new({
1238        CACHE_SIZE => 64,   # only cache 64 compiled templates
1239    });
1240
1241
1242=head2 STAT_TTL
1243
1244The L<STAT_TTL|Template::Manual::Config#STAT_TTL> value can be set to control
1245how long the C<Template::Provider> will keep a template cached in memory
1246before checking to see if the source template has changed.
1247
1248    my $provider = Template::Provider->new({
1249        STAT_TTL => 60,  # one minute
1250    });
1251
1252=head2 COMPILE_EXT
1253
1254The L<COMPILE_EXT|Template::Manual::Config#COMPILE_EXT> option can be
1255provided to specify a filename extension for compiled template files.
1256It is undefined by default and no attempt will be made to read or write
1257any compiled template files.
1258
1259    my $provider = Template::Provider->new({
1260        COMPILE_EXT => '.ttc',
1261    });
1262
1263=head2 COMPILE_DIR
1264
1265The L<COMPILE_DIR|Template::Manual::Config#COMPILE_DIR> option is used to
1266specify an alternate directory root under which compiled template files should
1267be saved.
1268
1269    my $provider = Template::Provider->new({
1270        COMPILE_DIR => '/tmp/ttc',
1271    });
1272
1273=head2 TOLERANT
1274
1275The L<TOLERANT|Template::Manual::Config#TOLERANT> flag can be set to indicate
1276that the C<Template::Provider> module should ignore any errors encountered while
1277loading a template and instead return C<STATUS_DECLINED>.
1278
1279=head2 PARSER
1280
1281The L<PARSER|Template::Manual::Config#PARSER> option can be used to define
1282a parser module other than the default of L<Template::Parser>.
1283
1284    my $provider = Template::Provider->new({
1285        PARSER => MyOrg::Template::Parser->new({ ... }),
1286    });
1287
1288=head2 DEBUG
1289
1290The L<DEBUG|Template::Manual::Config#DEBUG> option can be used to enable
1291debugging messages from the L<Template::Provider> module by setting it to include
1292the C<DEBUG_PROVIDER> value.
1293
1294    use Template::Constants qw( :debug );
1295
1296    my $template = Template->new({
1297        DEBUG => DEBUG_PROVIDER,
1298    });
1299
1300=head1 SUBCLASSING
1301
1302The C<Template::Provider> module can be subclassed to provide templates from a
1303different source (e.g. a database).  In most cases you'll just need to provide
1304custom implementations of the C<_template_modified()> and C<_template_content()>
1305methods.  If your provider requires and custom initialisation then you'll also
1306need to implement a new C<_init()> method.
1307
1308Caching in memory and on disk will still be applied (if enabled)
1309when overriding these methods.
1310
1311=head2 _template_modified($path)
1312
1313Returns a timestamp of the C<$path> passed in by calling C<stat()>.
1314This can be overridden, for example, to return a last modified value from
1315a database.  The value returned should be a timestamp value (as returned by C<time()>,
1316although a sequence number should work as well.
1317
1318=head2 _template_content($path)
1319
1320This method returns the content of the template for all C<INCLUDE>, C<PROCESS>,
1321and C<INSERT> directives.
1322
1323When called in scalar context, the method returns the content of the template
1324located at C<$path>, or C<undef> if C<$path> is not found.
1325
1326When called in list context it returns C<($content, $error, $mtime)>,
1327where C<$content> is the template content, C<$error> is an error string
1328(e.g. "C<$path: File not found>"), and C<$mtime> is the template modification
1329time.
1330
1331=head1 AUTHOR
1332
1333Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
1334
1335=head1 COPYRIGHT
1336
1337Copyright (C) 1996-2007 Andy Wardley.  All Rights Reserved.
1338
1339This module is free software; you can redistribute it and/or
1340modify it under the same terms as Perl itself.
1341
1342=head1 SEE ALSO
1343
1344L<Template>, L<Template::Parser>, L<Template::Context>
1345
1346=cut
1347
1348# Local Variables:
1349# mode: perl
1350# perl-indent-level: 4
1351# indent-tabs-mode: nil
1352# End:
1353#
1354# vim: expandtab shiftwidth=4:
1355