Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationLatte
      • ApplicationTracy
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsLatte
      • Framework
      • HttpTracy
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Diagnostics
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Latte
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Templating
    • Utils
  • NetteModule
  • none
  • Tracy

Classes

  • Cache
  • OutputHelper

Interfaces

  • IStorage
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Caching;
  9: 
 10: use Nette;
 11: use Nette\Utils\Callback;
 12: 
 13: 
 14: /**
 15:  * Implements the cache for a application.
 16:  *
 17:  * @author     David Grudl
 18:  */
 19: class Cache extends Nette\Object implements \ArrayAccess
 20: {
 21:     /** dependency */
 22:     const PRIORITY = 'priority',
 23:         EXPIRATION = 'expire',
 24:         EXPIRE = 'expire',
 25:         SLIDING = 'sliding',
 26:         TAGS = 'tags',
 27:         FILES = 'files',
 28:         ITEMS = 'items',
 29:         CONSTS = 'consts',
 30:         CALLBACKS = 'callbacks',
 31:         ALL = 'all';
 32: 
 33:     /** @internal */
 34:     const NAMESPACE_SEPARATOR = "\x00";
 35: 
 36:     /** @var IStorage */
 37:     private $storage;
 38: 
 39:     /** @var string */
 40:     private $namespace;
 41: 
 42:     /** @var string  last query cache used by offsetGet() */
 43:     private $key;
 44: 
 45:     /** @var mixed  last query cache used by offsetGet()  */
 46:     private $data;
 47: 
 48: 
 49:     public function __construct(IStorage $storage, $namespace = NULL)
 50:     {
 51:         $this->storage = $storage;
 52:         $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
 53:     }
 54: 
 55: 
 56:     /**
 57:      * Returns cache storage.
 58:      * @return IStorage
 59:      */
 60:     public function getStorage()
 61:     {
 62:         return $this->storage;
 63:     }
 64: 
 65: 
 66:     /**
 67:      * Returns cache namespace.
 68:      * @return string
 69:      */
 70:     public function getNamespace()
 71:     {
 72:         return (string) substr($this->namespace, 0, -1);
 73:     }
 74: 
 75: 
 76:     /**
 77:      * Returns new nested cache object.
 78:      * @param  string
 79:      * @return self
 80:      */
 81:     public function derive($namespace)
 82:     {
 83:         $derived = new static($this->storage, $this->namespace . $namespace);
 84:         return $derived;
 85:     }
 86: 
 87: 
 88:     /**
 89:      * Reads the specified item from the cache or generate it.
 90:      * @param  mixed key
 91:      * @param  callable
 92:      * @return mixed|NULL
 93:      */
 94:     public function load($key, $fallback = NULL)
 95:     {
 96:         $data = $this->storage->read($this->generateKey($key));
 97:         if ($data === NULL && $fallback) {
 98:             return $this->save($key, function (& $dependencies) use ($fallback) {
 99:                 return call_user_func_array($fallback, array(& $dependencies));
100:             });
101:         }
102:         return $data;
103:     }
104: 
105: 
106:     /**
107:      * Writes item into the cache.
108:      * Dependencies are:
109:      * - Cache::PRIORITY => (int) priority
110:      * - Cache::EXPIRATION => (timestamp) expiration
111:      * - Cache::SLIDING => (bool) use sliding expiration?
112:      * - Cache::TAGS => (array) tags
113:      * - Cache::FILES => (array|string) file names
114:      * - Cache::ITEMS => (array|string) cache items
115:      * - Cache::CONSTS => (array|string) cache items
116:      *
117:      * @param  mixed  key
118:      * @param  mixed  value
119:      * @param  array  dependencies
120:      * @return mixed  value itself
121:      * @throws Nette\InvalidArgumentException
122:      */
123:     public function save($key, $data, array $dependencies = NULL)
124:     {
125:         $this->release();
126:         $key = $this->generateKey($key);
127: 
128:         if ($data instanceof Nette\Callback || $data instanceof \Closure) {
129:             $this->storage->lock($key);
130:             try {
131:                 $data = call_user_func_array($data, array(& $dependencies));
132:             } catch (\Throwable $e) {
133:                 $this->storage->remove($key);
134:                 throw $e;
135:             } catch (\Exception $e) {
136:                 $this->storage->remove($key);
137:                 throw $e;
138:             }
139:         }
140: 
141:         if ($data === NULL) {
142:             $this->storage->remove($key);
143:         } else {
144:             $this->storage->write($key, $data, $this->completeDependencies($dependencies, $data));
145:             return $data;
146:         }
147:     }
148: 
149: 
150:     private function completeDependencies($dp, $data)
151:     {
152:         // convert expire into relative amount of seconds
153:         if (isset($dp[self::EXPIRATION])) {
154:             $dp[self::EXPIRATION] = Nette\Utils\DateTime::from($dp[self::EXPIRATION])->format('U') - time();
155:         }
156: 
157:         // convert FILES into CALLBACKS
158:         if (isset($dp[self::FILES])) {
159:             foreach (array_unique((array) $dp[self::FILES]) as $item) {
160:                 $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); // @ - stat may fail
161:             }
162:             unset($dp[self::FILES]);
163:         }
164: 
165:         // add namespaces to items
166:         if (isset($dp[self::ITEMS])) {
167:             $dp[self::ITEMS] = array_unique(array_map(array($this, 'generateKey'), (array) $dp[self::ITEMS]));
168:         }
169: 
170:         // convert CONSTS into CALLBACKS
171:         if (isset($dp[self::CONSTS])) {
172:             foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
173:                 $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
174:             }
175:             unset($dp[self::CONSTS]);
176:         }
177: 
178:         if (!is_array($dp)) {
179:             $dp = array();
180:         }
181:         return $dp;
182:     }
183: 
184: 
185:     /**
186:      * Removes item from the cache.
187:      * @param  mixed  key
188:      * @return void
189:      */
190:     public function remove($key)
191:     {
192:         $this->save($key, NULL);
193:     }
194: 
195: 
196:     /**
197:      * Removes items from the cache by conditions.
198:      * Conditions are:
199:      * - Cache::PRIORITY => (int) priority
200:      * - Cache::TAGS => (array) tags
201:      * - Cache::ALL => TRUE
202:      * @return void
203:      */
204:     public function clean(array $conditions = NULL)
205:     {
206:         $this->release();
207:         $this->storage->clean((array) $conditions);
208:     }
209: 
210: 
211:     /**
212:      * Caches results of function/method calls.
213:      * @param  mixed
214:      * @return mixed
215:      */
216:     public function call($function)
217:     {
218:         $key = func_get_args();
219:         if (is_array($function) && is_object($function[0])) {
220:             $key[0][0] = get_class($function[0]);
221:         }
222:         return $this->load($key, function () use ($function, $key) {
223:             return Callback::invokeArgs($function, array_slice($key, 1));
224:         });
225:     }
226: 
227: 
228:     /**
229:      * Caches results of function/method calls.
230:      * @param  mixed
231:      * @param  array  dependencies
232:      * @return \Closure
233:      */
234:     public function wrap($function, array $dependencies = NULL)
235:     {
236:         $cache = $this;
237:         return function () use ($cache, $function, $dependencies) {
238:             $key = array($function, func_get_args());
239:             if (is_array($function) && is_object($function[0])) {
240:                 $key[0][0] = get_class($function[0]);
241:             }
242:             $data = $cache->load($key);
243:             if ($data === NULL) {
244:                 $data = $cache->save($key, Callback::invokeArgs($function, $key[1]), $dependencies);
245:             }
246:             return $data;
247:         };
248:     }
249: 
250: 
251:     /**
252:      * Starts the output cache.
253:      * @param  mixed  key
254:      * @return OutputHelper|NULL
255:      */
256:     public function start($key)
257:     {
258:         $data = $this->load($key);
259:         if ($data === NULL) {
260:             return new OutputHelper($this, $key);
261:         }
262:         echo $data;
263:     }
264: 
265: 
266:     /**
267:      * Generates internal cache key.
268:      *
269:      * @param  string
270:      * @return string
271:      */
272:     protected function generateKey($key)
273:     {
274:         return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
275:     }
276: 
277: 
278:     /********************* interface ArrayAccess ****************d*g**/
279: 
280: 
281:     /**
282:      * @deprecated
283:      */
284:     public function offsetSet($key, $data)
285:     {
286:         $this->save($key, $data);
287:     }
288: 
289: 
290:     /**
291:      * @deprecated
292:      */
293:     public function offsetGet($key)
294:     {
295:         $key = is_scalar($key) ? (string) $key : serialize($key);
296:         if ($this->key !== $key) {
297:             $this->key = $key;
298:             $this->data = $this->load($key);
299:         }
300:         return $this->data;
301:     }
302: 
303: 
304:     /**
305:      * @deprecated
306:      */
307:     public function offsetExists($key)
308:     {
309:         $this->release();
310:         return $this->offsetGet($key) !== NULL;
311:     }
312: 
313: 
314:     /**
315:      * @deprecated
316:      */
317:     public function offsetUnset($key)
318:     {
319:         $this->save($key, NULL);
320:     }
321: 
322: 
323:     /**
324:      * @deprecated
325:      */
326:     public function release()
327:     {
328:         $this->key = $this->data = NULL;
329:     }
330: 
331: 
332:     /********************* dependency checkers ****************d*g**/
333: 
334: 
335:     /**
336:      * Checks CALLBACKS dependencies.
337:      * @param  array
338:      * @return bool
339:      */
340:     public static function checkCallbacks($callbacks)
341:     {
342:         foreach ($callbacks as $callback) {
343:             if (!call_user_func_array(array_shift($callback), $callback)) {
344:                 return FALSE;
345:             }
346:         }
347:         return TRUE;
348:     }
349: 
350: 
351:     /**
352:      * Checks CONSTS dependency.
353:      * @param  string
354:      * @param  mixed
355:      * @return bool
356:      */
357:     private static function checkConst($const, $value)
358:     {
359:         return defined($const) && constant($const) === $value;
360:     }
361: 
362: 
363:     /**
364:      * Checks FILES dependency.
365:      * @param  string
366:      * @param  int
367:      * @return bool
368:      */
369:     private static function checkFile($file, $time)
370:     {
371:         return @filemtime($file) == $time; // @ - stat may fail
372:     }
373: 
374: }
375: 
Nette 2.2 API documentation generated by ApiGen 2.8.0