]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1352264 - Preload all templates (re-apply)
authorDylan William Hardison <dylan@hardison.net>
Fri, 26 May 2017 23:48:49 +0000 (19:48 -0400)
committerDylan William Hardison <dylan@hardison.net>
Fri, 26 May 2017 23:48:49 +0000 (19:48 -0400)
Bugzilla/Template.pm
Bugzilla/Template/PreloadProvider.pm [new file with mode: 0644]
mod_perl.pl

index 29b1d4a1310c98e1592167e2db275e1463207a32..87ab2929e990b448eacc2c3cde3119ac2eb95411 100644 (file)
@@ -12,6 +12,7 @@ use 5.10.1;
 use strict;
 use warnings;
 
+use Bugzilla::Template::PreloadProvider;
 use Bugzilla::Bug;
 use Bugzilla::Constants;
 use Bugzilla::Hook;
@@ -47,6 +48,8 @@ use constant FORMAT_3_SIZE => [19,28,28];
 use constant FORMAT_DOUBLE => '%19s %-55s';
 use constant FORMAT_2_SIZE => [19,55];
 
+my %SHARED_PROVIDERS;
+
 # Pseudo-constant.
 sub SAFE_URL_REGEXP {
     my $safe_protocols = join('|', SAFE_PROTOCOLS);
@@ -968,10 +971,12 @@ sub create {
 
         PLUGIN_BASE => 'Bugzilla::Template::Plugin',
 
-        CONSTANTS => _load_constants(),
-
         # Default variables for all templates
         VARIABLES => {
+            # Some of these are not really constants, and doing this messes up preloading.
+            # they are now fake constants.
+            constants => _load_constants(),
+
             # Function for retrieving global parameters.
             'Param' => sub { return Bugzilla->params->{$_[0]}; },
 
@@ -1110,12 +1115,18 @@ sub create {
             'is_mobile_browser' => sub { return Bugzilla->cgi->user_agent =~ /Mobi/ },
         },
     };
+
+    # under mod_perl, use a provider (template loader) that preloads all templates into memory
+    my $provider_class
+        = $ENV{MOD_PERL}
+        ? 'Bugzilla::Template::PreloadProvider'
+        : 'Template::Provider';
+
     # Use a per-process provider to cache compiled templates in memory across
     # requests.
     my $provider_key = join(':', @{ $config->{INCLUDE_PATH} });
-    my $shared_providers = Bugzilla->process_cache->{shared_providers} ||= {};
-    $shared_providers->{$provider_key} ||= Template::Provider->new($config);
-    $config->{LOAD_TEMPLATES} = [ $shared_providers->{$provider_key} ];
+    $SHARED_PROVIDERS{$provider_key} ||= $provider_class->new($config);
+    $config->{LOAD_TEMPLATES} = [ $SHARED_PROVIDERS{$provider_key} ];
 
     # BMO - use metrics subclass
     local $Template::Config::CONTEXT = Bugzilla->metrics_enabled()
@@ -1204,7 +1215,7 @@ sub precompile_templates {
     delete Bugzilla->request_cache->{template};
 
     # Clear out the cached Provider object
-    Bugzilla->process_cache->{shared_providers} = undef;
+    %SHARED_PROVIDERS = ();
 
     print install_string('done') . "\n" if $output;
 }
diff --git a/Bugzilla/Template/PreloadProvider.pm b/Bugzilla/Template/PreloadProvider.pm
new file mode 100644 (file)
index 0000000..2588b1a
--- /dev/null
@@ -0,0 +1,110 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+# This exists to implement the template-before_process hook.
+package Bugzilla::Template::PreloadProvider;
+
+use 5.10.1;
+use strict;
+use warnings;
+
+use base qw(Template::Provider);
+
+use File::Find ();
+use Cwd ();
+use File::Spec;
+use Template::Constants qw( STATUS_ERROR );
+use Template::Document;
+use Template::Config;
+
+use Bugzilla::Util qw(trick_taint);
+
+sub _init {
+    my $self = shift;
+    $self->SUPER::_init(@_);
+
+    my $path   = $self->{INCLUDE_PATH};
+    my $cache  = $self->{_BZ_CACHE} = {};
+    my $search = $self->{_BZ_SEARCH} = {};
+
+    foreach my $template_dir (@$path) {
+        $template_dir = Cwd::realpath($template_dir);
+        my $wanted = sub {
+            my ( $name, $dir ) = ($File::Find::name, $File::Find::dir);
+            if ( $name =~ /\.tmpl$/ ) {
+                my $key = $name;
+                $key =~ s/^\Q$template_dir\///;
+                unless ($search->{$key}) {
+                    $search->{$key} = $name;
+                }
+                trick_taint($name);
+                my $data = {
+                    name => $key,
+                    text => do {
+                        open my $fh, '<:utf8', $name or die "cannot open $name";
+                        local $/ = undef;
+                        scalar <$fh>; # $fh is closed it goes out of scope
+                    },
+                    time => (stat($name))[9],
+                };
+                trick_taint($data->{text}) if $data->{text};
+                $cache->{$name} = $self->_bz_compile($data) or die "compile error: $name";
+            }
+        };
+        File::Find::find( { wanted => $wanted, no_chdir => 1 }, $template_dir );
+    }
+
+    return $self;
+}
+
+sub fetch {
+    my ($self, $name, $prefix) = @_;
+    my $file;
+    if (File::Spec->file_name_is_absolute($name)) {
+        $file = $name;
+    }
+    elsif ($name =~ m#^\./#) {
+        $file = File::Spec->rel2abs($name);
+    }
+    else {
+        $file = $self->{_BZ_SEARCH}{$name};
+    }
+
+    if (not $file) {
+        return ("cannot find file - $name ($file)", STATUS_ERROR);
+    }
+
+    if ($self->{_BZ_CACHE}{$file}) {
+        return ($self->{_BZ_CACHE}{$file}, undef);
+    }
+    else {
+        return ("unknown file - $file", STATUS_ERROR);
+    }
+}
+
+sub _bz_compile {
+    my ($self, $data) = @_;
+
+    my $parser = $self->{PARSER} ||= Template::Config->parser( $self->{PARAMS} )
+        || return ( Template::Config->error(), STATUS_ERROR );
+
+    # discard the template text - we don't need it any more
+    my $text = delete $data->{text};
+
+    # call parser to compile template into Perl code
+    if (my $parsedoc = $parser->parse($text, $data)) {
+        $parsedoc->{METADATA} = {
+            'name' => $data->{name},
+            'modtime' => $data->{time},
+            %{ $parsedoc->{METADATA} },
+        };
+
+        return Template::Document->new($parsedoc);
+    }
+}
+
+1;
index 0a0a0df6a8ce929d812d58aafdce74f4e6f4e27a..f3beb88db8f39190cb87d58a6b57d621f98acab8 100644 (file)
@@ -79,6 +79,9 @@ $Bugzilla::extension_packages = Bugzilla::Extension->load_all();
 
 Bugzilla->preload_features();
 
+# Force instantiation of template so Bugzilla::Template::PreloadProvider can do its magic.
+Bugzilla->template;
+
 # Have ModPerl::RegistryLoader pre-compile all CGI scripts.
 my $rl = new ModPerl::RegistryLoader();
 # If we try to do this in "new" it fails because it looks for a