3 // Copyright (c) 2011 Blue Static
5 // This program is free software: you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation, either version 3 of the License, or any later version.
9 // This program is distributed in the hope that it will be useful, but WITHOUT
10 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 // You should have received a copy of the GNU General Public License along with
15 // this program. If not, see <http://www.gnu.org/licenses/>.
17 namespace hoplite
\views
;
19 use \hoplite
\base\Profiling
;
21 require_once HOPLITE_ROOT
. '/base/profiling.php';
22 require_once HOPLITE_ROOT
. '/views/template.php';
25 The TemplateLoader manages the reading of templates from the file system.
27 It can also work with a CacheBackend to store the compiled template data to
30 Internally it also maintains a cache that it will consult after a template is
31 loaded for the first time.
35 /*! @var TemplateLoader Singleton instance */
36 static private $instance = NULL
;
38 /*! @var string Base path for loading the template file. Use %s to indicate
39 where the name (passed to the constructor) should be
42 protected $template_path = '%s.tpl';
44 /*! @var CacheBackend A cache for compiled template data. */
45 protected $cache_backend = NULL
;
47 /*! @var array An array of Template objects, keyed by the template name. */
48 protected $cache = array();
50 /*! @var array Array of template usage counts, keyed by name. Only used
53 protected $usage = array();
55 /*! Gets the singleton instance. */
56 static public function GetInstance()
58 if (!self
::$instance) {
59 $class = get_called_class();
60 self
::$instance = new $class();
62 return self
::$instance;
64 /*! Sets the singleton instance. */
65 static public function SetInstance($instance) { self
::$instance = $instance; }
68 public function set_template_path($path) { $this->template_path
= $path; }
69 public function template_path() { return $this->template_path
; }
71 public function set_cache_backend(CacheBackend
$backend) { $this->cache_backend
= $backend; }
72 public function cache_backend() { return $this->cache_backend
; }
75 Loads a template from a file, creates a Template object, and returns a copy
78 @param string Template name, with wich the template plath is formatted.
80 @return Template Clone of the cached template.
82 public function Load($name)
84 if (Profiling
::IsProfilingEnabled() && !isset($this->usage
[$name]))
85 $this->usage
[$name] = 0;
87 $tpl_path = $this->_TemplatePath($name);
88 if (!file_exists($tpl_path))
89 throw new TemplateException("Template $name does not exist at path $tpl_path");
91 // First check the memory cache.
92 if (isset($this->cache
[$name]))
93 return clone $this->cache
[$name];
95 // Then check if the cache backend has it.
96 $template = $this->_QueryCache($name, $tpl_path);
98 $this->cache
[$name] = $template;
99 return clone $template;
102 // Finally, parse and cache the template.
103 $template = $this->_Load($name);
104 $this->cache
[$name] = $template;
105 return clone $template;
109 Warms up the template cache with a set of templates. If the cache backend
110 supports multiple simultaneous fetches, this can greatly improve performance.
112 public function PreCache(Array $templates)
114 if (!$this->cache_backend
)
117 $fetch_templates = [];
118 $paths_to_names = [];
119 foreach ($templates AS $name) {
120 // Do not re-cache templates that have already been cached.
121 if (isset($this->cache
[$name]))
124 $tpl_path = $this->_TemplatePath($name);
125 $fetch_templates[$tpl_path] = filemtime($tpl_path);
126 $paths_to_names[$tpl_path] = $name;
129 $profile = Profiling
::IsProfilingEnabled();
131 $cached_templates = $this->cache_backend
->GetMultipleTemplates($fetch_templates);
132 foreach ($cached_templates AS $tpl_path => $data) {
133 $name = $paths_to_names[$tpl_path];
134 $this->cache
[$name] = Template
::NewWithCompiledData($name, $data);
136 $this->usage
[$name] = 0;
140 /*! Convenience function for loading templates. */
141 static public function Fetch($name)
143 return self
::GetInstance()->Load($name);
146 /*! Marks a template as having been used. */
147 public function MarkTemplateRendered($name)
149 if (!isset($this->usage
[$name]))
150 throw new \
InvalidArgumentException("Template $name has not been loaded through this instance");
152 $this->usage
[$name]++
;
156 Queries the optional CacheBackend for a template.
158 @param string Template name
159 @param string Template path
161 @return Template|NULL
163 protected function _QueryCache($name, $tpl_path)
165 if (!$this->cache_backend
)
168 $data = $this->cache_backend
->GetTemplateDataForName($tpl_path, filemtime($tpl_path));
172 return Template
::NewWithCompiledData($name, $data);
176 Loads a raw template from the file system and compiles it. If the optional
177 CacheBackend is present, it will cache the compiled data.
179 @param string Template name.
183 protected function _Load($name)
185 $tpl_path = $this->_TemplatePath($name);
187 $data = @file_get_contents($tpl_path);
189 throw new TemplateLoaderException('Could not load template ' . $name);
191 $template = Template
::NewWithData($name, $data);
193 if ($this->cache_backend
) {
194 $this->cache_backend
->StoreCompiledTemplate(
195 $tpl_path, filemtime($tpl_path), $template->template());
201 /*! Returns the template path for a given template name. */
202 protected function _TemplatePath($name)
204 return sprintf($this->template_path
, $name);
208 class TemplateLoaderException
extends \Exception
{}