1 |
<? |
2 |
|
3 |
/* |
4 |
/* |
5 |
Class cacheFastTemplate |
6 |
|
7 |
PHP extension for managing cached templates |
8 |
|
9 |
This program is free software; you can redistribute it and/or modify it under |
10 |
the GNU Library General Public License, with the following stipulations; |
11 |
|
12 |
Changes or modifications must retain these Copyright statements. |
13 |
Changes or modifications must be submitted to both AUTHORS. |
14 |
|
15 |
This program is released under the GNU Library General Public License. |
16 |
( http://www.gnu.org/copyleft/lgpl.html ) |
17 |
|
18 |
This program is distributed in the hope that it will be useful, |
19 |
but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
20 |
FOR A PARTICULAR PURPOSE. See the Artistic License for more details. |
21 |
This software is distributed AS-IS. |
22 |
|
23 |
Some caching functions by Benjamin Kahn <xkahn@cybersites.com> |
24 |
http://www.zoned.net/~xkahn/php/fasttemplate |
25 |
Portions written by Benjamin Kahn Copyright (c) 2000 CyberSites, Inc; xkahn@cybersites.com, All Rights Reserved |
26 |
|
27 |
originally by: "JP" <jprins@dds.nl> |
28 |
http://www.phpbuilder.com/columns/jprins20000201.php3 |
29 |
|
30 |
with code by: Spencer D. Mindlin <smindlin@beaconeast.com> |
31 |
http://www.phpbuilder.com/columns/spencer20000208.php3 |
32 |
|
33 |
Portions of this code are based on code from FastTemplate for PHP |
34 |
Copyright (c) 1999 CDI, cdi@thewebmasters.net, All Rights Reserved. |
35 |
|
36 |
*/ |
37 |
|
38 |
class cachedFastTemplate extends FastTemplate { |
39 |
|
40 |
|
41 |
var $CACHE = array(); // An array of arrays which set |
42 |
// caching on a block |
43 |
|
44 |
// ************************************************************ |
45 |
|
46 |
function cachedFastTemplate ($pathToTemplates = "") { |
47 |
$this->FastTemplate ($pathToTemplates); |
48 |
|
49 |
} // end (new) FastTemplate () |
50 |
|
51 |
|
52 |
// ********************* |
53 |
// Function: is_cached |
54 |
// Input: RETURN, FileHandle (FIXME: Plural FileHandles are not supported.) |
55 |
// Return: true or false depending on whether a cache file is available. |
56 |
// Side Effects: assigns variables as if a parse call had been made |
57 |
// |
58 |
// Notes: |
59 |
// Find the cache name, and see if the file exists |
60 |
// If it doesn't, return FALSE |
61 |
// If it does, check is the file is current (see ~/src/php-cache.php) |
62 |
// If is isn't, return FALSE |
63 |
// check if the section is dynamic. If so, use clear_dynamic to insert contents of file |
64 |
// If not, assign file contents to ReturnVar |
65 |
// return TRUE |
66 |
function is_cached ( $ReturnVar, $FileTags ) { |
67 |
|
68 |
// Fixme: We should do something smart if $FileTags is an array |
69 |
|
70 |
$str_cache_file = $this->cache_file_name( $ReturnVar, $FileTags ); |
71 |
|
72 |
// This function will wait until a cache has been created. (Lock file cleared.) |
73 |
clearstatcache($str_cache_file); |
74 |
// Here we perform some error correction. If a lock file |
75 |
// is left over from a previous request, this ensures that |
76 |
// it is deleted. |
77 |
// FIXME: If this takes too long, we should turn caching off for this file, and return FALSE; |
78 |
while (file_exists($str_cache_file . '.lock')) { |
79 |
sleep(2); |
80 |
clearstatcache(); |
81 |
continue; |
82 |
} |
83 |
clearstatcache(); |
84 |
|
85 |
// If the cache file doesn't exist, it isn't cached. QED. |
86 |
if (!file_exists($str_cache_file)) { |
87 |
return FALSE; |
88 |
} |
89 |
|
90 |
// Now we need to find out if we are caching. |
91 |
$refresh = $this->CACHE[$FileTags]["refresh"]; |
92 |
$expires = $this->CACHE[$FileTags]["expire"]; |
93 |
$cache_read = 0; // Default to no read |
94 |
if (!refresh && !expires) // Do we have any set time? |
95 |
$expires = 600; |
96 |
|
97 |
if (isset ($expires)) { |
98 |
if ((filectime ($str_cache_file) + $expires) > date ( "U")) { |
99 |
$cache_read = 1; |
100 |
} |
101 |
} |
102 |
|
103 |
if (isset ($refresh)) { |
104 |
switch (strtoupper($refresh)) { |
105 |
case 'MINUTE': |
106 |
if (checkRefreshMinute(mktime(), filemtime($str_cache_file))) { |
107 |
$cache_read = 1; |
108 |
} |
109 |
break; |
110 |
case 'QUARTERHOUR': |
111 |
if (checkRefreshQuarterHour(mktime(), filemtime($str_cache_file))) { |
112 |
$cache_read = 1; |
113 |
} |
114 |
break; |
115 |
case 'HALFHOUR': |
116 |
if (checkRefreshHalfHour(mktime(), filemtime($str_cache_file))) { |
117 |
$cache_read = 1; |
118 |
} |
119 |
break; |
120 |
case 'HOUR': |
121 |
if (checkRefreshHour(mktime(), filemtime($str_cache_file))) { |
122 |
$cache_read = 1; |
123 |
} |
124 |
break; |
125 |
case 'HALFDAY': |
126 |
if (checkExpiredHalfDay(mktime(), filemtime($str_cache_file))) { |
127 |
$cache_read = 1; |
128 |
} |
129 |
break; |
130 |
case 'DAY': |
131 |
if (checkRefreshDay(mktime(), filemtime($str_cache_file))) { |
132 |
$cache_read = 1; |
133 |
} |
134 |
break; |
135 |
case 'MONTH': |
136 |
if (checkRefreshMonth(mktime(), filemtime($str_cache_file))) { |
137 |
$cache_read = 1; |
138 |
} |
139 |
} |
140 |
} |
141 |
|
142 |
if ($cache_read == 1) { |
143 |
|
144 |
// We have a cache file and we should read from it! |
145 |
if ($f = fopen ($str_cache_file, "r")) { |
146 |
$buf = ""; |
147 |
while ($str = fgets ($f, 4096)) { |
148 |
$buf .= $str; |
149 |
} |
150 |
fclose ($f); |
151 |
|
152 |
$this->LAST = $ReturnVar; |
153 |
|
154 |
// This is the part that actually does all the work. |
155 |
// The template and the parsed version are the same when caching |
156 |
$this->assign( array( $ReturnVar => $buf ) ); |
157 |
$this->$ReturnVar = $buf; |
158 |
$this->$FileTags = $buf; |
159 |
$this->LOADED[$FileTags] = "1"; // Make sure the template doesn't get loaded. |
160 |
|
161 |
// If we were supposed to be appending, maybe we should assume a dynamic block |
162 |
if ( (substr ($FileTags, 0, 1)) == '.' ) { |
163 |
$this->add_cache_dynamic ($ReturnVar, $buf); |
164 |
} |
165 |
|
166 |
return TRUE; |
167 |
} |
168 |
$this->ROOT = $root; |
169 |
} |
170 |
|
171 |
} // End set_root() |
172 |
|
173 |
// ********************* |
174 |
// Function: write_cache |
175 |
// Input: RETURN, FileHandle (FIXME: Plural FileHandles are not supported.) |
176 |
// Return: true or false depending on whether a cache file could be created |
177 |
// Side Effects: None |
178 |
// |
179 |
// Notes: |
180 |
// Find the cache name. |
181 |
// Write out the ReturnVar contents to the file |
182 |
// Return |
183 |
function write_cache ( $ReturnVar, $FileTags ) { |
184 |
|
185 |
// FIXME: I should do something clever if $FileTags is an array. |
186 |
|
187 |
$str_cache_file = $this->cache_file_name ( $ReturnVar, $FileTags ); |
188 |
|
189 |
$new = $this->parse_template_messy ($this->$FileTags, $this->PARSEVARS); |
190 |
|
191 |
// Time to write the cache -- but we need to look out for lock files |
192 |
// FIXME: I'm SURE there is a race condition here. |
193 |
while (file_exists($str_cache_file . '.lock')) { |
194 |
sleep(2); |
195 |
clearstatcache(); |
196 |
continue; |
197 |
} |
198 |
clearstatcache(); |
199 |
|
200 |
// We pray that nothing bad happens after this point. |
201 |
touch ($str_cache_file . '.lock'); |
202 |
|
203 |
$f = fopen ($str_cache_file, "w"); |
204 |
if (!$f) |
205 |
return FALSE; |
206 |
|
207 |
fputs ($f, $new); |
208 |
fclose ($f); |
209 |
|
210 |
// And remove the lock file. |
211 |
$this->remove_cache_lock ($str_cache_file); |
212 |
|
213 |
return TRUE; |
214 |
|
215 |
} |
216 |
|
217 |
// ********************* |
218 |
// Function: cache_expire |
219 |
// Input: RETURN, number_of_seconds |
220 |
// Return: None |
221 |
// Side Effects: Stores time to expire |
222 |
// |
223 |
// Notes: |
224 |
// tells in how many seconds the cached data will expire |
225 |
|
226 |
function cache_expire ( $ReturnVar, $seconds = 3000) { |
227 |
$this->CACHE[$ReturnVar]["expire"] = $seconds; |
228 |
} |
229 |
|
230 |
// ********************* |
231 |
// Function: cache_refresh |
232 |
// Input: RETURN, frequency |
233 |
// Return: None |
234 |
// Side Effects: Stores time to refresh |
235 |
// |
236 |
// Notes: |
237 |
// sets an event (turn of day) at which cache will expire |
238 |
function cache_refresh ( $ReturnVar, $freq = 'HOUR') { |
239 |
$this->CACHE[$ReturnVar]["refresh"] = $freq; |
240 |
} |
241 |
|
242 |
// ************************************************************ |
243 |
// All templates will be loaded from this "root" directory |
244 |
// Can be changed in mid-process by re-calling with a new |
245 |
// value. |
246 |
|
247 |
function set_root ($root) { |
248 |
|
249 |
$trailer = substr($root,-1); |
250 |
|
251 |
if(!$this->WIN32) { |
252 |
|
253 |
if( (ord($trailer)) != 47 ) { |
254 |
$root = "$root". chr(47); |
255 |
} |
256 |
|
257 |
if(is_dir($root)) { |
258 |
$this->ROOT = $root; |
259 |
} else { |
260 |
$this->ROOT = ""; |
261 |
$this->error("Specified ROOT dir [$root] is not a directory"); |
262 |
} |
263 |
} else { |
264 |
// WIN32 box - no testing |
265 |
if( (ord($trailer)) != 92 ) { |
266 |
$root = "$root" . chr(92); |
267 |
} |
268 |
$this->ROOT = $root; |
269 |
} |
270 |
|
271 |
} // End set_root() |
272 |
|
273 |
|
274 |
function parse_template_messy ($template, $tpl_array) { |
275 |
$a = strtok($template,"{"); |
276 |
$oldtemp = $template; |
277 |
while($a || $t) { |
278 |
$t = strtok("}"); |
279 |
|
280 |
if($t){ |
281 |
settype($tpl_array[$t],"string"); |
282 |
|
283 |
if(empty($tpl_array[$t])){ |
284 |
$toprint = $toprint . $a . "{". $t . "}"; // Recreate the substitution. |
285 |
} else { |
286 |
$toprint = $toprint . $a . $tpl_array[$t]; |
287 |
} |
288 |
|
289 |
} else { |
290 |
$toprint = $toprint . $a; |
291 |
} |
292 |
//print('T-' . $t . '<BR>'); |
293 |
//print('value' . htmlentities($tpl_array[$t]) . '<BR>'); |
294 |
//print('A-' . htmlentities($a) . '<BR>'); |
295 |
$a = strtok("{"); |
296 |
|
297 |
} |
298 |
|
299 |
$template = $toprint; |
300 |
|
301 |
return $template; |
302 |
|
303 |
} // end parse_template_messy(); |
304 |
|
305 |
// ************************************************************ |
306 |
// Adds cached data to a DYNAMIC BLOCK from a template. |
307 |
|
308 |
function add_cache_dynamic ($Macro="", $cache_data="" ) { |
309 |
if(empty($Macro)) { return false; } |
310 |
|
311 |
// The file must already be in memory. |
312 |
|
313 |
$ParentTag = $this->DYNAMIC["$Macro"]; |
314 |
|
315 |
if( (!$this->$ParentTag) or (empty($this->$ParentTag)) ) { |
316 |
$fileName = $this->FILELIST[$ParentTag]; |
317 |
$this->$ParentTag = $this->get_template($fileName); |
318 |
$this->LOADED[$ParentTag] = 1; |
319 |
} |
320 |
|
321 |
if($this->$ParentTag) { |
322 |
$template = $this->$ParentTag; |
323 |
$DataArray = split("\n",$template); |
324 |
$newParent = ""; |
325 |
$outside = true; |
326 |
$start = false; |
327 |
$end = false; |
328 |
while ( list ($lineNum,$lineData) = each ($DataArray) ) { |
329 |
$lineTest = trim($lineData); |
330 |
if("<!-- BEGIN DYNAMIC BLOCK: $Macro -->" == "$lineTest" ) { |
331 |
$start = true; |
332 |
$end = false; |
333 |
$outside = false; |
334 |
} |
335 |
if("<!-- END DYNAMIC BLOCK: $Macro -->" == "$lineTest" ) { |
336 |
$start = false; |
337 |
$end = true; |
338 |
$outside = true; |
339 |
} |
340 |
if( ($outside) and (!$start) and (!$end) ) { |
341 |
$newParent .= "$lineData\n"; // Restore linebreaks |
342 |
} |
343 |
if ($end) { |
344 |
$newParent .= $cache_data; |
345 |
} |
346 |
// Next line please |
347 |
if($end) { $end = false; } |
348 |
if($start) { $start = false; } |
349 |
} // end While |
350 |
|
351 |
$this->$ParentTag = $newParent; |
352 |
return true; |
353 |
|
354 |
} else {// $ParentTag NOT loaded - MAJOR oopsie |
355 |
@error_log("ParentTag: [$ParentTag] not loaded!",0); |
356 |
$this->error("ParentTag: [$ParentTag] not loaded!",0); |
357 |
} |
358 |
return false; |
359 |
} |
360 |
|
361 |
// ************************************************************ |
362 |
// Caching system |
363 |
|
364 |
//*********************************/ |
365 |
// Some other functions |
366 |
function remove_cache_lock($str_cache_file) { |
367 |
clearstatcache(); |
368 |
if (file_exists($str_cache_file . '.lock')) { |
369 |
@unlink($str_cache_file . '.lock'); |
370 |
} |
371 |
} |
372 |
|
373 |
// Create the cache file name |
374 |
function cache_file_name ( $ReturnVar, $FileTags ) { |
375 |
|
376 |
// What are we going to do? |
377 |
$str_cache_root = "/tmp/"; |
378 |
$str_cache_dir = strtolower(getenv ("HTTP_HOST")) . getenv ("SCRIPT_URL"); |
379 |
if ( (substr ($FileTags, 0, 1)) == '.' ) { |
380 |
$str_cache_file = "/" . substr($FileTags,1); |
381 |
} else { |
382 |
$str_cache_file = "/" . $FileTags; |
383 |
} |
384 |
|
385 |
// First we need to make the directory the cache file is in if it doesn't already exist. |
386 |
if ($this->rmkdir ($str_cache_root . $str_cache_dir)) |
387 |
return $str_cache_root . $str_cache_dir . $str_cache_file; |
388 |
|
389 |
return; |
390 |
} |
391 |
|
392 |
// Create a directory tree |
393 |
function rmkdir ( $directory ) { |
394 |
|
395 |
// If we encounter this as a file, we can't make it a directory |
396 |
$type = filetype ( $directory ); |
397 |
if ($type != 'dir' && $type != 'FALSE' && !empty($type)) { |
398 |
return FALSE; |
399 |
} |
400 |
if ($type == 'dir') |
401 |
return TRUE; |
402 |
|
403 |
$pieces = explode ("/", $directory); |
404 |
|
405 |
for ($num = 0; $num < count($pieces); $num += 1) { |
406 |
$so_far = $so_far . "/" . $pieces[$num]; |
407 |
|
408 |
$type = filetype ( $so_far ); |
409 |
if ($type != 'dir' && $type != 'FALSE' && !empty($type)) { |
410 |
return FALSE; |
411 |
} |
412 |
|
413 |
if ($type == 'dir') { |
414 |
continue; |
415 |
} |
416 |
|
417 |
if (!mkdir ($so_far, 0700)) { |
418 |
return FALSE; |
419 |
} |
420 |
} |
421 |
|
422 |
return TRUE; |
423 |
|
424 |
} |
425 |
|
426 |
//*********************************/ |
427 |
// CACHE REFRESH CHECKING FUNCTIONS |
428 |
// Each function checks the next interval higher for verification |
429 |
|
430 |
function checkRefreshYear($systime, $filetime) { |
431 |
$sysYear = date( "y", $systime); |
432 |
$fileYear = date( "y", $filetime); |
433 |
if ($sysYear != $fileYear) { return false; } |
434 |
return true; |
435 |
} |
436 |
|
437 |
function checkRefreshMonth($systime, $filetime) { |
438 |
$sysMonth = date( "M", $systime); |
439 |
$fileMonth = date( "M", $filetime); |
440 |
if ($sysMonth != $fileMonth) { return false; } |
441 |
if (!(checkRefreshYear($systime, $filetime))) { return false; } |
442 |
return true; |
443 |
} |
444 |
|
445 |
function checkRefreshDay($systime, $filetime) { |
446 |
$sysDay = date( "j", $systime); |
447 |
$fileDay = date( "j", $filetime); |
448 |
if ($sysDay != $fileDay) { return false; } |
449 |
if (!(checkRefreshMonth($systime, $filetime))) { return false; } |
450 |
return true; |
451 |
} |
452 |
|
453 |
function checkRefreshDayHalf($systime, $filetime) { |
454 |
$sysHour = date( "H", $systime); |
455 |
$fileHour = date( "H", $filetime); |
456 |
if (($sysHour % 12) < ($fileHour % 12)) { return false; } |
457 |
if (!(checkRefreshDay($systime, $filetime))) { return false; } |
458 |
return true; |
459 |
} |
460 |
|
461 |
function checkRefreshHour($systime, $filetime) { |
462 |
$sysHour = date( "H", $systime); |
463 |
$fileHour = date( "H", $filetime); |
464 |
if ($sysHour != $fileHour) { return false; } |
465 |
if (!(checkRefreshDayHalf($systime, $filetime))) { return false; } |
466 |
return true; |
467 |
} |
468 |
|
469 |
function checkRefreshHalfHour($systime, $filetime) { |
470 |
$sysMin = date( "i", $systime); |
471 |
$fileMin = date( "i", $filetime); |
472 |
if (($sysMin % 30) < ($fileMin % 30)) { return false; } |
473 |
if (!(checkRefreshHour($systime, $filetime))) { return false; } |
474 |
return true; |
475 |
} |
476 |
|
477 |
function checkRefreshQuarterHour($systime, $filetime) { |
478 |
$sysMin = date( "i", $systime); |
479 |
$fileMin = date( "i", $filetime); |
480 |
if (($sysMin % 15) < ($fileMin % 15)) { return false; } |
481 |
if (!(checkRefreshHalfHour($systime, $filetime))) { return false; } |
482 |
return true; |
483 |
} |
484 |
|
485 |
function checkRefreshMinute($systime, $filetime) { |
486 |
$sysMin = date( "i", $systime); |
487 |
$fileMin = date( "i", $filetime); |
488 |
if ($sysMin != $fileMin) { return false; } |
489 |
if (!(checkRefreshQuarterHour($systime, $filetime))) { return false; } |
490 |
return true; |
491 |
} |
492 |
|
493 |
|
494 |
|
495 |
} // End class.cacheFastTemplate |
496 |
|
497 |
?> |