- Added new configuration function
[haanga.git] / lib / Haanga.php
blob5eb8e19053a26954e745ef48655c59f15a221fa6
1 <?php
2 /*
3 +---------------------------------------------------------------------------------+
4 | Copyright (c) 2010 Haanga |
5 +---------------------------------------------------------------------------------+
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met: |
8 | 1. Redistributions of source code must retain the above copyright |
9 | notice, this list of conditions and the following disclaimer. |
10 | |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | 3. All advertising materials mentioning features or use of this software |
16 | must display the following acknowledgement: |
17 | This product includes software developed by César D. Rodas. |
18 | |
19 | 4. Neither the name of the César D. Rodas nor the |
20 | names of its contributors may be used to endorse or promote products |
21 | derived from this software without specific prior written permission. |
22 | |
23 | THIS SOFTWARE IS PROVIDED BY CÉSAR D. RODAS ''AS IS'' AND ANY |
24 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
25 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
26 | DISCLAIMED. IN NO EVENT SHALL CÉSAR D. RODAS BE LIABLE FOR ANY |
27 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
28 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
29 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
30 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
32 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE |
33 +---------------------------------------------------------------------------------+
34 | Authors: César Rodas <crodas@php.net> |
35 +---------------------------------------------------------------------------------+
39 /**
40 * Haanga Runtime class
42 * Simple class to call templates efficiently. This class aims
43 * to reduce the compilation of a template as less a possible. Also
44 * it will not load in memory the compiler, except when there is not
45 * cache (compiled template) or it is out-dated.
48 class Haanga
50 protected static $cache_dir;
51 protected static $templates_dir='.';
52 protected static $debug;
53 protected static $onCompile = NULL;
54 protected static $check_ttl;
55 protected static $check_get;
56 protected static $check_set;
57 protected static $use_autoload = TRUE;
58 protected static $hash_filename = TRUE;
60 public static $has_compiled;
62 private function __construct()
64 /* The class can't be instanced */
67 final public static function AutoLoad($class)
69 static $loaded = array();
70 static $path;
72 if (!isset($loaded[$class]) && substr($class, 0, 6) === 'Haanga' && !class_exists($class, false)) {
73 if ($path === NULL) {
74 $path = dirname(__FILE__);
76 $file = $path.DIRECTORY_SEPARATOR.str_replace('_', DIRECTORY_SEPARATOR, $class).'.php';
77 if (is_file($file)) {
78 require $file;
80 $loaded[$class] = TRUE;
81 return;
84 return FALSE;
87 // configure(Array $opts) {{{
88 /**
89 * Configuration to load Haanga
91 * Options:
93 * - (string) cache_dir
94 * - (string) tempalte_dir
95 * - (callback) on_compile
96 * - (boolean) debug
97 * - (int) check_ttl
98 * - (callback) check_get
99 * - (callback) check_set
100 * - (boolean) autoload
101 * - (boolean) use_hash_filename
103 * @return void
105 final public static function configure(Array $opts)
107 foreach ($opts as $option => $value) {
108 switch (strtolower($option)) {
109 case 'cache_dir':
110 self::setCacheDir($value);
111 break;
112 case 'template_dir':
113 self::setTemplateDir($value);
114 break;
115 case 'on_compile':
116 if (is_callable($value)) {
117 self::$onCompile = $value;
119 break;
120 case 'debug':
121 self::enableDebug((bool)$value);
122 break;
123 case 'check_ttl':
124 self::$check_ttl = (int)$value;
125 break;
126 case 'check_get':
127 if (is_callable($value)) {
128 self::$check_get = $value;
130 break;
131 case 'check_set':
132 if (is_callable($value)) {
133 self::$check_set = $value;
135 break;
136 case 'autoload':
137 self::$use_autoload = (bool)$value;
138 break;
139 case 'use_hash_filename':
140 self::$hash_filename = (bool)$value;
141 default:
142 continue;
146 // }}}
148 // setCacheDir(string $dir) {{{
150 * Set the directory where the compiled templates
151 * are stored.
153 * @param string $dir
155 * @return void
157 public static function setCacheDir($dir)
159 if (!is_dir($dir)) {
160 throw new Haanga_Exception("{$dir} is not a valid directory");
162 if (!is_writable($dir)) {
163 throw new Haanga_Exception("{$dir} can't be written");
165 self::$cache_dir = $dir;
167 // }}}
169 // setTemplateDir(string $dir) {{{
171 * Set the directory where the templates are located.
173 * @param string $dir
175 * @return void
177 public static function setTemplateDir($dir)
179 if (!is_dir($dir)) {
180 throw new Haanga_Exception("{$dir} is not a valid directory");
182 self::$templates_dir = $dir;
184 // }}}
186 // enableDebug($bool) {{{
187 public static function enableDebug($bool)
189 self::$debug = $bool;
191 // }}}
193 // load(string $file, array $vars, bool $return, array $blocks) {{{
195 * Load
197 * Load template. If the template is already compiled, just the compiled
198 * PHP file will be included an used. If the template is new, or it
199 * had changed, the Haanga compiler is loaded in memory, and the template
200 * is compiled.
203 * @param string $file
204 * @param array $vars
205 * @param bool $return
206 * @param array $blocks
208 * @return string|NULL
210 public static function Load($file, $vars = array(), $return=FALSE, $blocks=array())
212 static $compiler;
213 if (empty(self::$cache_dir)) {
214 throw new Haanga_Exception("Cache dir or template dir is missing");
217 self::$has_compiled = FALSE;
219 $tpl = self::$templates_dir.'/'.$file;
220 $fnc = sha1($tpl);
221 $callback = "haanga_".$fnc;
222 $php = self::$cache_dir.'/'.(self::$hash_filename ? $fnc : str_replace(DIRECTORY_SEPARATOR, '_', $file)).'.php';
224 if (is_callable($callback)) {
225 return $callback($vars, $return, $blocks);
228 $check = TRUE;
230 if (self::$check_ttl && self::$check_get && self::$check_set) {
231 /* */
232 if (call_user_func(self::$check_get, $callback)) {
233 /* disable checking for the next $check_ttl seconds */
234 $check = FALSE;
235 } else {
236 $result = call_user_func(self::$check_set, $callback, TRUE, self::$check_ttl);
240 if (!is_file($php) || ($check && filemtime($tpl) > filemtime($php))) {
242 if (!is_file($tpl)) {
243 /* There is no template nor compiled file */
244 throw new Exception("View {$file} doesn't exists");
247 /* recompile */
248 if (!$compiler) {
249 /* load compiler (done just once) */
250 if (self::$use_autoload) {
251 spl_autoload_register(array(__CLASS__, 'AutoLoad'));
254 $compiler = new Haanga_Compiler_Runtime;
256 if (self::$onCompile) {
257 /* call onCompile hook, just the first time */
258 call_user_func(self::$onCompile);
262 $compiler->reset();
264 if (self::$debug) {
265 $compiler->setDebug($php.".dump");
268 $code = $compiler->compile_file($tpl, FALSE);
270 file_put_contents($php, "<?php".$code);
271 self::$has_compiled = TRUE;
274 if (!is_callable($callback)) {
275 require $php;
278 return $callback($vars, $return, $blocks);
280 // }}}
285 * Local variables:
286 * tab-width: 4
287 * c-basic-offset: 4
288 * End:
289 * vim600: sw=4 ts=4 fdm=marker
290 * vim<600: sw=4 ts=4