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