1 : <?php
2 :
3 : /**
4 : * represents a Dwoo template contained in a string
5 : *
6 : * This software is provided 'as-is', without any express or implied warranty.
7 : * In no event will the authors be held liable for any damages arising from the use of this software.
8 : *
9 : * @author Jordi Boggiano <j.boggiano@seld.be>
10 : * @copyright Copyright (c) 2008, Jordi Boggiano
11 : * @license http://dwoo.org/LICENSE Modified BSD License
12 : * @link http://dwoo.org/
13 : * @version 1.1.0
14 : * @date 2009-07-18
15 : * @package Dwoo
16 : */
17 : class Dwoo_Template_String implements Dwoo_ITemplate
18 : {
19 : /**
20 : * template name
21 : *
22 : * @var string
23 : */
24 : protected $name;
25 :
26 : /**
27 : * template compilation id
28 : *
29 : * @var string
30 : */
31 : protected $compileId;
32 :
33 : /**
34 : * template cache id, if not provided in the constructor, it is set to
35 : * the md4 hash of the request_uri. it is however highly recommended to
36 : * provide one that will fit your needs.
37 : *
38 : * in all cases, the compilation id is prepended to the cache id to separate
39 : * templates with similar cache ids from one another
40 : *
41 : * @var string
42 : */
43 : protected $cacheId;
44 :
45 : /**
46 : * validity duration of the generated cache file (in seconds)
47 : *
48 : * set to -1 for infinite cache, 0 to disable and null to inherit the Dwoo instance's cache time
49 : *
50 : * @var int
51 : */
52 : protected $cacheTime;
53 :
54 : /**
55 : * boolean flag that defines whether the compilation should be enforced (once) or
56 : * not use this if you have issues with the compiled templates not being updated
57 : * but if you do need this it's most likely that you should file a bug report
58 : *
59 : * @var bool
60 : */
61 : protected $compilationEnforced;
62 :
63 : /**
64 : * caches the results of the file checks to save some time when the same
65 : * templates is rendered several times
66 : *
67 : * @var array
68 : */
69 : protected static $cache = array('cached'=>array(), 'compiled'=>array());
70 :
71 : /**
72 : * holds the compiler that built this template
73 : *
74 : * @var Dwoo_ICompiler
75 : */
76 : protected $compiler;
77 :
78 : /**
79 : * chmod value for all files written (cached or compiled ones)
80 : *
81 : * set to null if you don't want any chmod operation to happen
82 : *
83 : * @var int
84 : */
85 : protected $chmod = 0777;
86 :
87 : /**
88 : * creates a template from a string
89 : *
90 : * @param string $templateString the template to use
91 : * @param int $cacheTime duration of the cache validity for this template,
92 : * if null it defaults to the Dwoo instance that will
93 : * render this template, set to -1 for infinite cache or 0 to disable
94 : * @param string $cacheId the unique cache identifier of this page or anything else that
95 : * makes this template's content unique, if null it defaults
96 : * to the current url
97 : * @param string $compileId the unique compiled identifier, which is used to distinguish this
98 : * template from others, if null it defaults to the md4 hash of the template
99 : */
100 : public function __construct($templateString, $cacheTime = null, $cacheId = null, $compileId = null)
101 : {
102 214 : $this->template = $templateString;
103 214 : if (function_exists('hash')) {
104 214 : $this->name = hash('md4', $templateString);
105 214 : } else {
106 0 : $this->name = md5($templateString);
107 : }
108 214 : $this->cacheTime = $cacheTime;
109 :
110 214 : if ($compileId !== null) {
111 0 : $this->compileId = str_replace('../', '__', strtr($compileId, '\\%?=!:;'.PATH_SEPARATOR, '/-------'));
112 0 : }
113 :
114 214 : if ($cacheId !== null) {
115 5 : $this->cacheId = str_replace('../', '__', strtr($cacheId, '\\%?=!:;'.PATH_SEPARATOR, '/-------'));
116 5 : }
117 214 : }
118 :
119 : /**
120 : * returns the cache duration for this template
121 : *
122 : * defaults to null if it was not provided
123 : *
124 : * @return int|null
125 : */
126 : public function getCacheTime()
127 : {
128 1 : return $this->cacheTime;
129 : }
130 :
131 : /**
132 : * sets the cache duration for this template
133 : *
134 : * can be used to set it after the object is created if you did not provide
135 : * it in the constructor
136 : *
137 : * @param int $seconds duration of the cache validity for this template, if
138 : * null it defaults to the Dwoo instance's cache time. 0 = disable and
139 : * -1 = infinite cache
140 : */
141 : public function setCacheTime($seconds = null)
142 : {
143 0 : $this->cacheTime = $seconds;
144 0 : }
145 :
146 : /**
147 : * returns the chmod value for all files written (cached or compiled ones)
148 : *
149 : * defaults to 0777
150 : *
151 : * @return int|null
152 : */
153 : public function getChmod()
154 : {
155 0 : return $this->chmod;
156 : }
157 :
158 : /**
159 : * set the chmod value for all files written (cached or compiled ones)
160 : *
161 : * set to null if you don't want to do any chmod() operation
162 : *
163 : * @param int $mask new bitmask to use for all files
164 : */
165 : public function setChmod($mask = null)
166 : {
167 0 : $this->chmod = $mask;
168 0 : }
169 :
170 : /**
171 : * returns the template name
172 : *
173 : * @return string
174 : */
175 : public function getName()
176 : {
177 219 : return $this->name;
178 : }
179 :
180 : /**
181 : * returns the resource name for this template class
182 : *
183 : * @return string
184 : */
185 : public function getResourceName()
186 : {
187 34 : return 'string';
188 : }
189 :
190 : /**
191 : * returns the resource identifier for this template, false here as strings don't have identifiers
192 : *
193 : * @return false
194 : */
195 : public function getResourceIdentifier()
196 : {
197 10 : return false;
198 : }
199 :
200 : /**
201 : * returns the template source of this template
202 : *
203 : * @return string
204 : */
205 : public function getSource()
206 : {
207 211 : return $this->template;
208 : }
209 :
210 : /**
211 : * returns an unique value identifying the current version of this template,
212 : * in this case it's the md4 hash of the content
213 : *
214 : * @return string
215 : */
216 : public function getUid()
217 : {
218 1 : return $this->name;
219 : }
220 :
221 : /**
222 : * returns the compiler used by this template, if it was just compiled, or null
223 : *
224 : * @return Dwoo_ICompiler
225 : */
226 : public function getCompiler()
227 : {
228 1 : return $this->compiler;
229 : }
230 :
231 : /**
232 : * marks this template as compile-forced, which means it will be recompiled even if it
233 : * was already saved and wasn't modified since the last compilation. do not use this in production,
234 : * it's only meant to be used in development (and the development of dwoo particularly)
235 : */
236 : public function forceCompilation()
237 : {
238 218 : $this->compilationEnforced = true;
239 218 : }
240 :
241 : /**
242 : * returns the cached template output file name, true if it's cache-able but not cached
243 : * or false if it's not cached
244 : *
245 : * @param Dwoo $dwoo the dwoo instance that requests it
246 : * @return string|bool
247 : */
248 : public function getCachedTemplate(Dwoo $dwoo)
249 : {
250 220 : if ($this->cacheTime !== null) {
251 3 : $cacheLength = $this->cacheTime;
252 3 : } else {
253 217 : $cacheLength = $dwoo->getCacheTime();
254 : }
255 :
256 : // file is not cacheable
257 220 : if ($cacheLength === 0) {
258 215 : return false;
259 : }
260 :
261 5 : $cachedFile = $this->getCacheFilename($dwoo);
262 :
263 5 : if (isset(self::$cache['cached'][$this->cacheId]) === true && file_exists($cachedFile)) {
264 : // already checked, return cache file
265 5 : return $cachedFile;
266 5 : } elseif ($this->compilationEnforced !== true && file_exists($cachedFile) && ($cacheLength === -1 || filemtime($cachedFile) > ($_SERVER['REQUEST_TIME'] - $cacheLength)) && $this->isValidCompiledFile($this->getCompiledFilename($dwoo))) {
267 : // cache is still valid and can be loaded
268 0 : self::$cache['cached'][$this->cacheId] = true;
269 0 : return $cachedFile;
270 : } else {
271 : // file is cacheable
272 5 : return true;
273 : }
274 : }
275 :
276 : /**
277 : * caches the provided output into the cache file
278 : *
279 : * @param Dwoo $dwoo the dwoo instance that requests it
280 : * @param string $output the template output
281 : * @return mixed full path of the cached file or false upon failure
282 : */
283 : public function cache(Dwoo $dwoo, $output)
284 : {
285 5 : $cacheDir = $dwoo->getCacheDir();
286 5 : $cachedFile = $this->getCacheFilename($dwoo);
287 :
288 : // the code below is courtesy of Rasmus Schultz,
289 : // thanks for his help on avoiding concurency issues
290 5 : $temp = tempnam($cacheDir, 'temp');
291 5 : if (!($file = @fopen($temp, 'wb'))) {
292 0 : $temp = $cacheDir . uniqid('temp');
293 0 : if (!($file = @fopen($temp, 'wb'))) {
294 0 : trigger_error('Error writing temporary file \''.$temp.'\'', E_USER_WARNING);
295 0 : return false;
296 : }
297 0 : }
298 :
299 5 : fwrite($file, $output);
300 5 : fclose($file);
301 :
302 5 : $this->makeDirectory(dirname($cachedFile), $cacheDir);
303 5 : if (!@rename($temp, $cachedFile)) {
304 0 : @unlink($cachedFile);
305 0 : @rename($temp, $cachedFile);
306 0 : }
307 :
308 5 : if ($this->chmod !== null) {
309 5 : chmod($cachedFile, $this->chmod);
310 5 : }
311 :
312 5 : self::$cache['cached'][$this->cacheId] = true;
313 :
314 5 : return $cachedFile;
315 : }
316 :
317 : /**
318 : * clears the cached template if it's older than the given time
319 : *
320 : * @param Dwoo $dwoo the dwoo instance that was used to cache that template
321 : * @param int $olderThan minimum time (in seconds) required for the cache to be cleared
322 : * @return bool true if the cache was not present or if it was deleted, false if it remains there
323 : */
324 : public function clearCache(Dwoo $dwoo, $olderThan = -1)
325 : {
326 1 : $cachedFile = $this->getCacheFilename($dwoo);
327 :
328 1 : return !file_exists($cachedFile) || (filectime($cachedFile) < (time() - $olderThan) && unlink($cachedFile));
329 : }
330 :
331 : /**
332 : * returns the compiled template file name
333 : *
334 : * @param Dwoo $dwoo the dwoo instance that requests it
335 : * @param Dwoo_ICompiler $compiler the compiler that must be used
336 : * @return string
337 : */
338 : public function getCompiledTemplate(Dwoo $dwoo, Dwoo_ICompiler $compiler = null)
339 : {
340 219 : $compiledFile = $this->getCompiledFilename($dwoo);
341 :
342 219 : if ($this->compilationEnforced !== true && isset(self::$cache['compiled'][$this->compileId]) === true) {
343 : // already checked, return compiled file
344 219 : } elseif ($this->compilationEnforced !== true && $this->isValidCompiledFile($compiledFile)) {
345 : // template is compiled
346 0 : self::$cache['compiled'][$this->compileId] = true;
347 0 : } else {
348 : // compiles the template
349 219 : $this->compilationEnforced = false;
350 :
351 219 : if ($compiler === null) {
352 28 : $compiler = $dwoo->getDefaultCompilerFactory($this->getResourceName());
353 :
354 28 : if ($compiler === null || $compiler === array('Dwoo_Compiler', 'compilerFactory')) {
355 28 : if (class_exists('Dwoo_Compiler', false) === false) {
356 0 : include DWOO_DIRECTORY . 'Dwoo/Compiler.php';
357 0 : }
358 28 : $compiler = Dwoo_Compiler::compilerFactory();
359 28 : } else {
360 0 : $compiler = call_user_func($compiler);
361 : }
362 28 : }
363 :
364 219 : $this->compiler = $compiler;
365 :
366 219 : $compiler->setCustomPlugins($dwoo->getCustomPlugins());
367 219 : $compiler->setSecurityPolicy($dwoo->getSecurityPolicy());
368 219 : $this->makeDirectory(dirname($compiledFile), $dwoo->getCompileDir());
369 219 : file_put_contents($compiledFile, $compiler->compile($dwoo, $this));
370 208 : if ($this->chmod !== null) {
371 208 : chmod($compiledFile, $this->chmod);
372 208 : }
373 :
374 208 : self::$cache['compiled'][$this->compileId] = true;
375 : }
376 :
377 208 : return $compiledFile;
378 : }
379 :
380 : /**
381 : * Checks if compiled file is valid (it exists)
382 : *
383 : * @param string file
384 : * @return boolean True cache file existance
385 : */
386 : protected function isValidCompiledFile($file) {
387 5 : return file_exists($file);
388 : }
389 :
390 : /**
391 : * returns a new template string object with the resource id being the template source code
392 : *
393 : * @param Dwoo $dwoo the dwoo instance requiring it
394 : * @param mixed $resourceId the filename (relative to this template's dir) of the template to include
395 : * @param int $cacheTime duration of the cache validity for this template,
396 : * if null it defaults to the Dwoo instance that will
397 : * render this template
398 : * @param string $cacheId the unique cache identifier of this page or anything else that
399 : * makes this template's content unique, if null it defaults
400 : * to the current url
401 : * @param string $compileId the unique compiled identifier, which is used to distinguish this
402 : * template from others, if null it defaults to the filename+bits of the path
403 : * @param Dwoo_ITemplate $parentTemplate the template that is requesting a new template object (through
404 : * an include, extends or any other plugin)
405 : * @return Dwoo_Template_String
406 : */
407 : public static function templateFactory(Dwoo $dwoo, $resourceId, $cacheTime = null, $cacheId = null, $compileId = null, Dwoo_ITemplate $parentTemplate = null)
408 : {
409 1 : return new self($resourceId, $cacheTime, $cacheId, $compileId);
410 : }
411 :
412 : /**
413 : * returns the full compiled file name and assigns a default value to it if
414 : * required
415 : *
416 : * @param Dwoo $dwoo the dwoo instance that requests the file name
417 : * @return string the full path to the compiled file
418 : */
419 : protected function getCompiledFilename(Dwoo $dwoo)
420 : {
421 : // no compile id was provided, set default
422 209 : if ($this->compileId===null) {
423 209 : $this->compileId = $this->name;
424 209 : }
425 209 : return $dwoo->getCompileDir() . $this->compileId.'.d'.Dwoo::RELEASE_TAG.'.php';
426 : }
427 :
428 : /**
429 : * returns the full cached file name and assigns a default value to it if
430 : * required
431 : *
432 : * @param Dwoo $dwoo the dwoo instance that requests the file name
433 : * @return string the full path to the cached file
434 : */
435 : protected function getCacheFilename(Dwoo $dwoo)
436 : {
437 : // no cache id provided, use request_uri as default
438 5 : if ($this->cacheId === null) {
439 0 : if (isset($_SERVER['REQUEST_URI']) === true) {
440 0 : $cacheId = $_SERVER['REQUEST_URI'];
441 0 : } elseif (isset($_SERVER['SCRIPT_FILENAME']) && isset($_SERVER['argv'])) {
442 0 : $cacheId = $_SERVER['SCRIPT_FILENAME'].'-'.implode('-', $_SERVER['argv']);
443 0 : } else {
444 0 : $cacheId = '';
445 : }
446 : // force compiled id generation
447 0 : $this->getCompiledFilename($dwoo);
448 :
449 0 : $this->cacheId = str_replace('../', '__', $this->compileId . strtr($cacheId, '\\%?=!:;'.PATH_SEPARATOR, '/-------'));
450 0 : }
451 5 : return $dwoo->getCacheDir() . $this->cacheId.'.html';
452 : }
453 :
454 : /**
455 : * returns some php code that will check if this template has been modified or not
456 : *
457 : * if the function returns null, the template will be instanciated and then the Uid checked
458 : *
459 : * @return string
460 : */
461 : public function getIsModifiedCode()
462 : {
463 0 : return null;
464 : }
465 :
466 : /**
467 : * ensures the given path exists
468 : *
469 : * @param string $path any path
470 : * @param string $baseDir the base directory where the directory is created
471 : * ($path must still contain the full path, $baseDir
472 : * is only used for unix permissions)
473 : */
474 : protected function makeDirectory($path, $baseDir = null)
475 : {
476 219 : if (is_dir($path) === true) {
477 217 : return;
478 : }
479 :
480 3 : if ($this->chmod === null) {
481 0 : $chmod = 0777;
482 0 : } else {
483 3 : $chmod = $this->chmod;
484 : }
485 3 : mkdir($path, $chmod, true);
486 :
487 : // enforce the correct mode for all directories created
488 3 : if (strpos(PHP_OS, 'WIN') !== 0 && $baseDir !== null) {
489 0 : $path = strtr(str_replace($baseDir, '', $path), '\\', '/');
490 0 : $folders = explode('/', trim($path, '/'));
491 0 : foreach ($folders as $folder) {
492 0 : $baseDir .= $folder . DIRECTORY_SEPARATOR;
493 0 : chmod($baseDir, $chmod);
494 0 : }
495 0 : }
496 3 : }
497 : }
|