1: | <?php
|
2: | |
3: | |
4: | |
5: | |
6: | |
7: |
|
8: |
|
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | |
21: | |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: |
|
31: | abstract class Smarty_CacheResource_KeyValueStore extends Smarty_CacheResource
|
32: | {
|
33: | |
34: | |
35: | |
36: | |
37: |
|
38: | protected $contents = array();
|
39: |
|
40: | |
41: | |
42: | |
43: | |
44: |
|
45: | protected $timestamps = array();
|
46: |
|
47: | |
48: | |
49: | |
50: | |
51: | |
52: | |
53: | |
54: |
|
55: | public function populate(Smarty_Template_Cached $cached, Smarty_Internal_Template $_template)
|
56: | {
|
57: | $cached->filepath = $_template->source->uid . '#' . $this->sanitize($cached->source->resource) . '#' .
|
58: | $this->sanitize($cached->cache_id) . '#' . $this->sanitize($cached->compile_id);
|
59: | $this->populateTimestamp($cached);
|
60: | }
|
61: |
|
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: |
|
69: | public function populateTimestamp(Smarty_Template_Cached $cached)
|
70: | {
|
71: | if (!$this->fetch(
|
72: | $cached->filepath,
|
73: | $cached->source->name,
|
74: | $cached->cache_id,
|
75: | $cached->compile_id,
|
76: | $content,
|
77: | $timestamp,
|
78: | $cached->source->uid
|
79: | )
|
80: | ) {
|
81: | return;
|
82: | }
|
83: | $cached->content = $content;
|
84: | $cached->timestamp = (int)$timestamp;
|
85: | $cached->exists = !!$cached->timestamp;
|
86: | }
|
87: |
|
88: | |
89: | |
90: | |
91: | |
92: | |
93: | |
94: | |
95: | |
96: |
|
97: | public function process(
|
98: | Smarty_Internal_Template $_smarty_tpl,
|
99: | Smarty_Template_Cached $cached = null,
|
100: | $update = false
|
101: | ) {
|
102: | if (!$cached) {
|
103: | $cached = $_smarty_tpl->cached;
|
104: | }
|
105: | $content = $cached->content ? $cached->content : null;
|
106: | $timestamp = $cached->timestamp ? $cached->timestamp : null;
|
107: | if ($content === null || !$timestamp) {
|
108: | if (!$this->fetch(
|
109: | $_smarty_tpl->cached->filepath,
|
110: | $_smarty_tpl->source->name,
|
111: | $_smarty_tpl->cache_id,
|
112: | $_smarty_tpl->compile_id,
|
113: | $content,
|
114: | $timestamp,
|
115: | $_smarty_tpl->source->uid
|
116: | )
|
117: | ) {
|
118: | return false;
|
119: | }
|
120: | }
|
121: | if (isset($content)) {
|
122: | eval('?>' . $content);
|
123: | return true;
|
124: | }
|
125: | return false;
|
126: | }
|
127: |
|
128: | |
129: | |
130: | |
131: | |
132: | |
133: | |
134: | |
135: |
|
136: | public function writeCachedContent(Smarty_Internal_Template $_template, $content)
|
137: | {
|
138: | $this->addMetaTimestamp($content);
|
139: | return $this->write(array($_template->cached->filepath => $content), $_template->cache_lifetime);
|
140: | }
|
141: |
|
142: | |
143: | |
144: | |
145: | |
146: | |
147: | |
148: |
|
149: | public function readCachedContent(Smarty_Internal_Template $_template)
|
150: | {
|
151: | $content = $_template->cached->content ? $_template->cached->content : null;
|
152: | $timestamp = null;
|
153: | if ($content === null) {
|
154: | if (!$this->fetch(
|
155: | $_template->cached->filepath,
|
156: | $_template->source->name,
|
157: | $_template->cache_id,
|
158: | $_template->compile_id,
|
159: | $content,
|
160: | $timestamp,
|
161: | $_template->source->uid
|
162: | )
|
163: | ) {
|
164: | return false;
|
165: | }
|
166: | }
|
167: | if (isset($content)) {
|
168: | return $content;
|
169: | }
|
170: | return false;
|
171: | }
|
172: |
|
173: | |
174: | |
175: | |
176: | |
177: | |
178: | |
179: | |
180: | |
181: | |
182: | |
183: |
|
184: | public function clearAll(Smarty $smarty, $exp_time = null)
|
185: | {
|
186: | if (!$this->purge()) {
|
187: | $this->invalidate(null);
|
188: | }
|
189: | return -1;
|
190: | }
|
191: |
|
192: | |
193: | |
194: | |
195: | |
196: | |
197: | |
198: | |
199: | |
200: | |
201: | |
202: | |
203: | |
204: | |
205: | |
206: | |
207: |
|
208: | public function clear(Smarty $smarty, $resource_name, $cache_id, $compile_id, $exp_time)
|
209: | {
|
210: | $uid = $this->getTemplateUid($smarty, $resource_name);
|
211: | $cid = $uid . '#' . $this->sanitize($resource_name) . '#' . $this->sanitize($cache_id) . '#' .
|
212: | $this->sanitize($compile_id);
|
213: | $this->delete(array($cid));
|
214: | $this->invalidate($cid, $resource_name, $cache_id, $compile_id, $uid);
|
215: | return -1;
|
216: | }
|
217: |
|
218: | |
219: | |
220: | |
221: | |
222: | |
223: | |
224: | |
225: | |
226: |
|
227: | protected function getTemplateUid(Smarty $smarty, $resource_name)
|
228: | {
|
229: | if (isset($resource_name)) {
|
230: | $source = Smarty_Template_Source::load(null, $smarty, $resource_name);
|
231: | if ($source->exists) {
|
232: | return $source->uid;
|
233: | }
|
234: | }
|
235: | return '';
|
236: | }
|
237: |
|
238: | |
239: | |
240: | |
241: | |
242: | |
243: | |
244: |
|
245: | protected function sanitize($string)
|
246: | {
|
247: | $string = trim($string, '|');
|
248: | if (!$string) {
|
249: | return '';
|
250: | }
|
251: | return preg_replace('#[^\w\|]+#S', '_', $string);
|
252: | }
|
253: |
|
254: | |
255: | |
256: | |
257: | |
258: | |
259: | |
260: | |
261: | |
262: | |
263: | |
264: | |
265: | |
266: |
|
267: | protected function fetch(
|
268: | $cid,
|
269: | $resource_name = null,
|
270: | $cache_id = null,
|
271: | $compile_id = null,
|
272: | &$content = null,
|
273: | &$timestamp = null,
|
274: | $resource_uid = null
|
275: | ) {
|
276: | $t = $this->read(array($cid));
|
277: | $content = !empty($t[ $cid ]) ? $t[ $cid ] : null;
|
278: | $timestamp = null;
|
279: | if ($content && ($timestamp = $this->getMetaTimestamp($content))) {
|
280: | $invalidated =
|
281: | $this->getLatestInvalidationTimestamp($cid, $resource_name, $cache_id, $compile_id, $resource_uid);
|
282: | if ($invalidated > $timestamp) {
|
283: | $timestamp = null;
|
284: | $content = null;
|
285: | }
|
286: | }
|
287: | return !!$content;
|
288: | }
|
289: |
|
290: | |
291: | |
292: | |
293: | |
294: | |
295: |
|
296: | protected function addMetaTimestamp(&$content)
|
297: | {
|
298: | $mt = explode(' ', microtime());
|
299: | $ts = pack('NN', $mt[ 1 ], (int)($mt[ 0 ] * 100000000));
|
300: | $content = $ts . $content;
|
301: | }
|
302: |
|
303: | |
304: | |
305: | |
306: | |
307: | |
308: | |
309: |
|
310: | protected function getMetaTimestamp(&$content)
|
311: | {
|
312: | extract(unpack('N1s/N1m/a*content', $content));
|
313: | |
314: | |
315: | |
316: |
|
317: | return $s + ($m / 100000000);
|
318: | }
|
319: |
|
320: | |
321: | |
322: | |
323: | |
324: | |
325: | |
326: | |
327: | |
328: | |
329: | |
330: |
|
331: | protected function invalidate(
|
332: | $cid = null,
|
333: | $resource_name = null,
|
334: | $cache_id = null,
|
335: | $compile_id = null,
|
336: | $resource_uid = null
|
337: | ) {
|
338: | $now = microtime(true);
|
339: | $key = null;
|
340: |
|
341: | if (!$resource_name && !$cache_id && !$compile_id) {
|
342: | $key = 'IVK#ALL';
|
343: | }
|
344: | else {
|
345: | if ($resource_name && !$cache_id && !$compile_id) {
|
346: | $key = 'IVK#TEMPLATE#' . $resource_uid . '#' . $this->sanitize($resource_name);
|
347: | }
|
348: | else {
|
349: | if (!$resource_name && $cache_id && !$compile_id) {
|
350: | $key = 'IVK#CACHE#' . $this->sanitize($cache_id);
|
351: | }
|
352: | else {
|
353: | if (!$resource_name && !$cache_id && $compile_id) {
|
354: | $key = 'IVK#COMPILE#' . $this->sanitize($compile_id);
|
355: | }
|
356: | else {
|
357: | $key = 'IVK#CID#' . $cid;
|
358: | }
|
359: | }
|
360: | }
|
361: | }
|
362: | $this->write(array($key => $now));
|
363: | }
|
364: |
|
365: | |
366: | |
367: | |
368: | |
369: | |
370: | |
371: | |
372: | |
373: | |
374: | |
375: |
|
376: | protected function getLatestInvalidationTimestamp(
|
377: | $cid,
|
378: | $resource_name = null,
|
379: | $cache_id = null,
|
380: | $compile_id = null,
|
381: | $resource_uid = null
|
382: | ) {
|
383: |
|
384: | if (false && !$cid) {
|
385: | return 0;
|
386: | }
|
387: |
|
388: | if (!($_cid = $this->listInvalidationKeys($cid, $resource_name, $cache_id, $compile_id, $resource_uid))) {
|
389: | return 0;
|
390: | }
|
391: |
|
392: | if (!($values = $this->read($_cid))) {
|
393: | return 0;
|
394: | }
|
395: |
|
396: | $values = array_map('floatval', $values);
|
397: | return max($values);
|
398: | }
|
399: |
|
400: | |
401: | |
402: | |
403: | |
404: | |
405: | |
406: | |
407: | |
408: | |
409: | |
410: | |
411: | |
412: |
|
413: | protected function listInvalidationKeys(
|
414: | $cid,
|
415: | $resource_name = null,
|
416: | $cache_id = null,
|
417: | $compile_id = null,
|
418: | $resource_uid = null
|
419: | ) {
|
420: | $t = array('IVK#ALL');
|
421: | $_name = $_compile = '#';
|
422: | if ($resource_name) {
|
423: | $_name .= $resource_uid . '#' . $this->sanitize($resource_name);
|
424: | $t[] = 'IVK#TEMPLATE' . $_name;
|
425: | }
|
426: | if ($compile_id) {
|
427: | $_compile .= $this->sanitize($compile_id);
|
428: | $t[] = 'IVK#COMPILE' . $_compile;
|
429: | }
|
430: | $_name .= '#';
|
431: | $cid = trim($cache_id, '|');
|
432: | if (!$cid) {
|
433: | return $t;
|
434: | }
|
435: | $i = 0;
|
436: | while (true) {
|
437: |
|
438: | $i = strpos($cid, '|', $i);
|
439: |
|
440: | if ($i === false) {
|
441: | $t[] = 'IVK#CACHE#' . $cid;
|
442: | $t[] = 'IVK#CID' . $_name . $cid . $_compile;
|
443: | $t[] = 'IVK#CID' . $_name . $_compile;
|
444: | break;
|
445: | }
|
446: | $part = substr($cid, 0, $i);
|
447: |
|
448: | $t[] = 'IVK#CACHE#' . $part;
|
449: | $t[] = 'IVK#CID' . $_name . $part . $_compile;
|
450: |
|
451: | $i++;
|
452: | }
|
453: | return $t;
|
454: | }
|
455: |
|
456: | |
457: | |
458: | |
459: | |
460: | |
461: | |
462: | |
463: |
|
464: | public function hasLock(Smarty $smarty, Smarty_Template_Cached $cached)
|
465: | {
|
466: | $key = 'LOCK#' . $cached->filepath;
|
467: | $data = $this->read(array($key));
|
468: | return $data && time() - $data[ $key ] < $smarty->locking_timeout;
|
469: | }
|
470: |
|
471: | |
472: | |
473: | |
474: | |
475: | |
476: | |
477: | |
478: |
|
479: | public function acquireLock(Smarty $smarty, Smarty_Template_Cached $cached)
|
480: | {
|
481: | $cached->is_locked = true;
|
482: | $key = 'LOCK#' . $cached->filepath;
|
483: | $this->write(array($key => time()), $smarty->locking_timeout);
|
484: | }
|
485: |
|
486: | |
487: | |
488: | |
489: | |
490: | |
491: | |
492: | |
493: |
|
494: | public function releaseLock(Smarty $smarty, Smarty_Template_Cached $cached)
|
495: | {
|
496: | $cached->is_locked = false;
|
497: | $key = 'LOCK#' . $cached->filepath;
|
498: | $this->delete(array($key));
|
499: | }
|
500: |
|
501: | |
502: | |
503: | |
504: | |
505: | |
506: | |
507: |
|
508: | abstract protected function read(array $keys);
|
509: |
|
510: | |
511: | |
512: | |
513: | |
514: | |
515: | |
516: | |
517: |
|
518: | abstract protected function write(array $keys, $expire = null);
|
519: |
|
520: | |
521: | |
522: | |
523: | |
524: | |
525: | |
526: |
|
527: | abstract protected function delete(array $keys);
|
528: |
|
529: | |
530: | |
531: | |
532: | |
533: |
|
534: | protected function purge()
|
535: | {
|
536: | return false;
|
537: | }
|
538: | }
|
539: | |