Make use of Template names for usage profiling.
[hoplite.git] / views / template_loader.php
1 <?php
2 // Hoplite
3 // Copyright (c) 2011 Blue Static
4 //
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.
8 //
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
12 // more details.
13 //
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/>.
16
17 namespace hoplite\views;
18
19 use \hoplite\base\Profiling;
20
21 require_once HOPLITE_ROOT . '/base/profiling.php';
22 require_once HOPLITE_ROOT . '/views/template.php';
23
24 /*!
25 This class knows how to load and cache templates to the file system.
26 */
27 class TemplateLoader
28 {
29 /*! @var TemplateLoader Singleton instance */
30 static private $instance = NULL;
31
32 /*! @var string Base path for loading the template file. Use %s to indicate
33 where the name (passed to the constructor) should be
34 substituted.
35 */
36 protected $template_path = '%s.tpl';
37
38 /*! @var string The cache path for templates. Unlike |$template_path|, this
39 should only be a path, to which the cached template name will
40 be appended. This should not end with a trailing slash.
41 */
42 protected $cache_path = '/tmp/phalanx_views';
43
44 /*! @var string A header to put at the beginning of each cached template file,
45 common for setting include paths or cache debug information.
46 */
47 protected $cache_prefix = '';
48
49 /*! @var array An array of Template objects, keyed by the template name. */
50 protected $cache = array();
51
52 /*! @var array Array of template usage counts, keyed by name. Only used
53 when profiling.
54 */
55 protected $usage = array();
56
57 /*! Gets the singleton instance. */
58 static public function GetInstance()
59 {
60 if (!self::$instance) {
61 $class = get_called_class();
62 self::$instance = new $class();
63 }
64 return self::$instance;
65 }
66 /*! Sets the singleton instance. */
67 static public function SetInstance($instance) { self::$instance = $instance; }
68
69 /*! Accessors */
70 public function set_template_path($path) { $this->template_path = $path; }
71 function template_path() { return $this->template_path; }
72
73 public function set_cache_path($path) { $this->cache_path = $path; }
74 public function cache_path() { return $this->cache_path; }
75
76 /*!
77 Loads a template from a file, creates a Template object, and returns a copy
78 of that object.
79
80 @param string Template name, with wich the template plath is formatted.
81
82 @return Template Clone of the cached template.
83 */
84 public function Load($name)
85 {
86 if (Profiling::IsProfilingEnabled() && !isset($this->usage[$name]))
87 $this->usage[$name] = 0;
88
89 // First check the memory cache.
90 if (isset($this->cache[$name]))
91 return clone $this->cache[$name];
92
93 // Then check the filesystem cache.
94 $template = $this->_LoadIfCached($name);
95 if ($template) {
96 $this->cache[$name] = $template;
97 return clone $template;
98 }
99
100 // Finally, parse and cache the template.
101 $template = $this->_Cache($name);
102 $this->cache[$name] = $template;
103 return clone $template;
104 }
105
106 /*! Convenience function for loading templates. */
107 static public function Fetch($name)
108 {
109 return self::GetInstance()->Load($name);
110 }
111
112 /*! Marks a template as having been used. */
113 public function MarkTemplateRendered($name)
114 {
115 if (!isset($this->usage[$name]))
116 throw new \InvalidArgumentException("Template $name has not been loaded through this instance");
117
118 $this->usage[$name]++;
119 }
120
121 /*!
122 Loads a cached filesystem template if it is up-to-date.
123
124 @param string Template name
125
126 @return Template|NULL
127 */
128 protected function _LoadIfCached($name)
129 {
130 $cache_path = $this->_CachePath($name);
131 $tpl_path = $this->_TemplatePath($name);
132
133 // Make sure the cached file exists and hasn't gotten out-of-date.
134 if (!file_exists($cache_path) || filemtime($cache_path) < filemtime($tpl_path))
135 return NULL;
136
137 // Load the contents of the cache.
138 $data = @file_get_contents($cache_path);
139 if ($data === FALSE)
140 return NULL;
141
142 return Template::NewWithCompiledData($name, $data);
143 }
144
145 /*!
146 Loads a raw template from the file system, stores the compiled template in
147 the file system, and returns a new template object with that data.
148
149 @param string Template name.
150
151 @return Template
152 */
153 protected function _Cache($name)
154 {
155 $cache_path = $this->_CachePath($name);
156 $tpl_path = $this->_TemplatePath($name);
157
158 $data = @file_get_contents($tpl_path);
159 if ($data === FALSE)
160 throw new TemplateLoaderException('Could not load template ' . $name);
161
162 $template = Template::NewWithData($name, $data);
163
164 // Cache the file.
165 if (file_put_contents($cache_path, $this->cache_prefix . $template->template()) === FALSE)
166 throw new TemplateLoaderException('Could not cache ' . $name . ' to ' . $cache_path);
167
168 return $template;
169 }
170
171 /*! Returns the template path for a given template name. */
172 protected function _TemplatePath($name)
173 {
174 return sprintf($this->template_path, $name);
175 }
176
177 /*! Returns the cache path for a given template name. */
178 protected function _CachePath($name)
179 {
180 return $this->cache_path . $name . '.phpi';
181 }
182 }
183
184 class TemplateLoaderException extends \Exception {}