Namespaces

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

Classes

  • Cache
  • OutputHelper

Interfaces

  • IBulkReader
  • IStorage
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  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: class Cache
 18: {
 19:     use Nette\SmartObject;
 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: 
 43:     public function __construct(IStorage $storage, $namespace = NULL)
 44:     {
 45:         $this->storage = $storage;
 46:         $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
 47:     }
 48: 
 49: 
 50:     /**
 51:      * Returns cache storage.
 52:      * @return IStorage
 53:      */
 54:     public function getStorage()
 55:     {
 56:         return $this->storage;
 57:     }
 58: 
 59: 
 60:     /**
 61:      * Returns cache namespace.
 62:      * @return string
 63:      */
 64:     public function getNamespace()
 65:     {
 66:         return (string) substr($this->namespace, 0, -1);
 67:     }
 68: 
 69: 
 70:     /**
 71:      * Returns new nested cache object.
 72:      * @param  string
 73:      * @return static
 74:      */
 75:     public function derive($namespace)
 76:     {
 77:         $derived = new static($this->storage, $this->namespace . $namespace);
 78:         return $derived;
 79:     }
 80: 
 81: 
 82:     /**
 83:      * Reads the specified item from the cache or generate it.
 84:      * @param  mixed
 85:      * @param  callable
 86:      * @return mixed
 87:      */
 88:     public function load($key, $fallback = NULL)
 89:     {
 90:         $data = $this->storage->read($this->generateKey($key));
 91:         if ($data === NULL && $fallback) {
 92:             return $this->save($key, function (&$dependencies) use ($fallback) {
 93:                 return call_user_func_array($fallback, [&$dependencies]);
 94:             });
 95:         }
 96:         return $data;
 97:     }
 98: 
 99: 
100:     /**
101:      * Reads multiple items from the cache.
102:      * @param  array
103:      * @param  callable
104:      * @return array
105:      */
106:     public function bulkLoad(array $keys, $fallback = NULL)
107:     {
108:         if (count($keys) === 0) {
109:             return [];
110:         }
111:         foreach ($keys as $key) {
112:             if (!is_scalar($key)) {
113:                 throw new Nette\InvalidArgumentException('Only scalar keys are allowed in bulkLoad()');
114:             }
115:         }
116:         $storageKeys = array_map([$this, 'generateKey'], $keys);
117:         if (!$this->storage instanceof IBulkReader) {
118:             $result = array_combine($keys, array_map([$this->storage, 'read'], $storageKeys));
119:             if ($fallback !== NULL) {
120:                 foreach ($result as $key => $value) {
121:                     if ($value === NULL) {
122:                         $result[$key] = $this->save($key, function (&$dependencies) use ($key, $fallback) {
123:                             return call_user_func_array($fallback, [$key, &$dependencies]);
124:                         });
125:                     }
126:                 }
127:             }
128:             return $result;
129:         }
130: 
131:         $cacheData = $this->storage->bulkRead($storageKeys);
132:         $result = [];
133:         foreach ($keys as $i => $key) {
134:             $storageKey = $storageKeys[$i];
135:             if (isset($cacheData[$storageKey])) {
136:                 $result[$key] = $cacheData[$storageKey];
137:             } elseif ($fallback) {
138:                 $result[$key] = $this->save($key, function (&$dependencies) use ($key, $fallback) {
139:                     return call_user_func_array($fallback, [$key, &$dependencies]);
140:                 });
141:             } else {
142:                 $result[$key] = NULL;
143:             }
144:         }
145:         return $result;
146:     }
147: 
148: 
149:     /**
150:      * Writes item into the cache.
151:      * Dependencies are:
152:      * - Cache::PRIORITY => (int) priority
153:      * - Cache::EXPIRATION => (timestamp) expiration
154:      * - Cache::SLIDING => (bool) use sliding expiration?
155:      * - Cache::TAGS => (array) tags
156:      * - Cache::FILES => (array|string) file names
157:      * - Cache::ITEMS => (array|string) cache items
158:      * - Cache::CONSTS => (array|string) cache items
159:      *
160:      * @param  mixed
161:      * @param  mixed
162:      * @return mixed  value itself
163:      * @throws Nette\InvalidArgumentException
164:      */
165:     public function save($key, $data, array $dependencies = NULL)
166:     {
167:         $key = $this->generateKey($key);
168: 
169:         if ($data instanceof Nette\Callback || $data instanceof \Closure) {
170:             if ($data instanceof Nette\Callback) {
171:                 trigger_error('Nette\Callback is deprecated, use closure or Nette\Utils\Callback::toClosure().', E_USER_DEPRECATED);
172:             }
173:             $this->storage->lock($key);
174:             try {
175:                 $data = call_user_func_array($data, [&$dependencies]);
176:             } catch (\Throwable $e) {
177:                 $this->storage->remove($key);
178:                 throw $e;
179:             } catch (\Exception $e) {
180:                 $this->storage->remove($key);
181:                 throw $e;
182:             }
183:         }
184: 
185:         if ($data === NULL) {
186:             $this->storage->remove($key);
187:         } else {
188:             $dependencies = $this->completeDependencies($dependencies);
189:             if (isset($dependencies[Cache::EXPIRATION]) && $dependencies[Cache::EXPIRATION] <= 0) {
190:                 $this->storage->remove($key);
191:             } else {
192:                 $this->storage->write($key, $data, $dependencies);
193:             }
194:             return $data;
195:         }
196:     }
197: 
198: 
199:     private function completeDependencies($dp)
200:     {
201:         // convert expire into relative amount of seconds
202:         if (isset($dp[self::EXPIRATION])) {
203:             $dp[self::EXPIRATION] = Nette\Utils\DateTime::from($dp[self::EXPIRATION])->format('U') - time();
204:         }
205: 
206:         // make list from TAGS
207:         if (isset($dp[self::TAGS])) {
208:             $dp[self::TAGS] = array_values((array) $dp[self::TAGS]);
209:         }
210: 
211:         // convert FILES into CALLBACKS
212:         if (isset($dp[self::FILES])) {
213:             foreach (array_unique((array) $dp[self::FILES]) as $item) {
214:                 $dp[self::CALLBACKS][] = [[__CLASS__, 'checkFile'], $item, @filemtime($item) ?: NULL]; // @ - stat may fail
215:             }
216:             unset($dp[self::FILES]);
217:         }
218: 
219:         // add namespaces to items
220:         if (isset($dp[self::ITEMS])) {
221:             $dp[self::ITEMS] = array_unique(array_map([$this, 'generateKey'], (array) $dp[self::ITEMS]));
222:         }
223: 
224:         // convert CONSTS into CALLBACKS
225:         if (isset($dp[self::CONSTS])) {
226:             foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
227:                 $dp[self::CALLBACKS][] = [[__CLASS__, 'checkConst'], $item, constant($item)];
228:             }
229:             unset($dp[self::CONSTS]);
230:         }
231: 
232:         if (!is_array($dp)) {
233:             $dp = [];
234:         }
235:         return $dp;
236:     }
237: 
238: 
239:     /**
240:      * Removes item from the cache.
241:      * @param  mixed
242:      * @return void
243:      */
244:     public function remove($key)
245:     {
246:         $this->save($key, NULL);
247:     }
248: 
249: 
250:     /**
251:      * Removes items from the cache by conditions.
252:      * Conditions are:
253:      * - Cache::PRIORITY => (int) priority
254:      * - Cache::TAGS => (array) tags
255:      * - Cache::ALL => TRUE
256:      * @return void
257:      */
258:     public function clean(array $conditions = NULL)
259:     {
260:         $conditions = (array) $conditions;
261:         if (isset($conditions[self::TAGS])) {
262:             $conditions[self::TAGS] = array_values((array) $conditions[self::TAGS]);
263:         }
264:         $this->storage->clean($conditions);
265:     }
266: 
267: 
268:     /**
269:      * Caches results of function/method calls.
270:      * @param  mixed
271:      * @return mixed
272:      */
273:     public function call($function)
274:     {
275:         $key = func_get_args();
276:         if (is_array($function) && is_object($function[0])) {
277:             $key[0][0] = get_class($function[0]);
278:         }
279:         return $this->load($key, function () use ($function, $key) {
280:             return Callback::invokeArgs($function, array_slice($key, 1));
281:         });
282:     }
283: 
284: 
285:     /**
286:      * Caches results of function/method calls.
287:      * @param  mixed
288:      * @return \Closure
289:      */
290:     public function wrap($function, array $dependencies = NULL)
291:     {
292:         return function () use ($function, $dependencies) {
293:             $key = [$function, func_get_args()];
294:             if (is_array($function) && is_object($function[0])) {
295:                 $key[0][0] = get_class($function[0]);
296:             }
297:             $data = $this->load($key);
298:             if ($data === NULL) {
299:                 $data = $this->save($key, Callback::invokeArgs($function, $key[1]), $dependencies);
300:             }
301:             return $data;
302:         };
303:     }
304: 
305: 
306:     /**
307:      * Starts the output cache.
308:      * @param  mixed
309:      * @return OutputHelper|NULL
310:      */
311:     public function start($key)
312:     {
313:         $data = $this->load($key);
314:         if ($data === NULL) {
315:             return new OutputHelper($this, $key);
316:         }
317:         echo $data;
318:     }
319: 
320: 
321:     /**
322:      * Generates internal cache key.
323:      * @param  mixed
324:      * @return string
325:      */
326:     protected function generateKey($key)
327:     {
328:         return $this->namespace . md5(is_scalar($key) ? (string) $key : serialize($key));
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|NULL
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.4-20170221 API API documentation generated by ApiGen 2.8.0