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 self
 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 key
 85:      * @param  callable
 86:      * @return mixed|NULL
 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  key
161:      * @param  mixed  value
162:      * @param  array  dependencies
163:      * @return mixed  value itself
164:      * @throws Nette\InvalidArgumentException
165:      */
166:     public function save($key, $data, array $dependencies = NULL)
167:     {
168:         $key = $this->generateKey($key);
169: 
170:         if ($data instanceof Nette\Callback || $data instanceof \Closure) {
171:             if ($data instanceof Nette\Callback) {
172:                 trigger_error('Nette\Callback is deprecated, use closure or Nette\Utils\Callback::toClosure().', E_USER_DEPRECATED);
173:             }
174:             $this->storage->lock($key);
175:             try {
176:                 $data = call_user_func_array($data, [& $dependencies]);
177:             } catch (\Throwable $e) {
178:                 $this->storage->remove($key);
179:                 throw $e;
180:             } catch (\Exception $e) {
181:                 $this->storage->remove($key);
182:                 throw $e;
183:             }
184:         }
185: 
186:         if ($data === NULL) {
187:             $this->storage->remove($key);
188:         } else {
189:             $this->storage->write($key, $data, $this->completeDependencies($dependencies));
190:             return $data;
191:         }
192:     }
193: 
194: 
195:     private function completeDependencies($dp)
196:     {
197:         // convert expire into relative amount of seconds
198:         if (isset($dp[self::EXPIRATION])) {
199:             $dp[self::EXPIRATION] = Nette\Utils\DateTime::from($dp[self::EXPIRATION])->format('U') - time();
200:         }
201: 
202:         // convert FILES into CALLBACKS
203:         if (isset($dp[self::FILES])) {
204:             foreach (array_unique((array) $dp[self::FILES]) as $item) {
205:                 $dp[self::CALLBACKS][] = [[__CLASS__, 'checkFile'], $item, @filemtime($item)]; // @ - stat may fail
206:             }
207:             unset($dp[self::FILES]);
208:         }
209: 
210:         // add namespaces to items
211:         if (isset($dp[self::ITEMS])) {
212:             $dp[self::ITEMS] = array_unique(array_map([$this, 'generateKey'], (array) $dp[self::ITEMS]));
213:         }
214: 
215:         // convert CONSTS into CALLBACKS
216:         if (isset($dp[self::CONSTS])) {
217:             foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
218:                 $dp[self::CALLBACKS][] = [[__CLASS__, 'checkConst'], $item, constant($item)];
219:             }
220:             unset($dp[self::CONSTS]);
221:         }
222: 
223:         if (!is_array($dp)) {
224:             $dp = [];
225:         }
226:         return $dp;
227:     }
228: 
229: 
230:     /**
231:      * Removes item from the cache.
232:      * @param  mixed  key
233:      * @return void
234:      */
235:     public function remove($key)
236:     {
237:         $this->save($key, NULL);
238:     }
239: 
240: 
241:     /**
242:      * Removes items from the cache by conditions.
243:      * Conditions are:
244:      * - Cache::PRIORITY => (int) priority
245:      * - Cache::TAGS => (array) tags
246:      * - Cache::ALL => TRUE
247:      * @return void
248:      */
249:     public function clean(array $conditions = NULL)
250:     {
251:         $this->storage->clean((array) $conditions);
252:     }
253: 
254: 
255:     /**
256:      * Caches results of function/method calls.
257:      * @param  mixed
258:      * @return mixed
259:      */
260:     public function call($function)
261:     {
262:         $key = func_get_args();
263:         if (is_array($function) && is_object($function[0])) {
264:             $key[0][0] = get_class($function[0]);
265:         }
266:         return $this->load($key, function () use ($function, $key) {
267:             return Callback::invokeArgs($function, array_slice($key, 1));
268:         });
269:     }
270: 
271: 
272:     /**
273:      * Caches results of function/method calls.
274:      * @param  mixed
275:      * @param  array  dependencies
276:      * @return \Closure
277:      */
278:     public function wrap($function, array $dependencies = NULL)
279:     {
280:         return function () use ($function, $dependencies) {
281:             $key = [$function, func_get_args()];
282:             if (is_array($function) && is_object($function[0])) {
283:                 $key[0][0] = get_class($function[0]);
284:             }
285:             $data = $this->load($key);
286:             if ($data === NULL) {
287:                 $data = $this->save($key, Callback::invokeArgs($function, $key[1]), $dependencies);
288:             }
289:             return $data;
290:         };
291:     }
292: 
293: 
294:     /**
295:      * Starts the output cache.
296:      * @param  mixed  key
297:      * @return OutputHelper|NULL
298:      */
299:     public function start($key)
300:     {
301:         $data = $this->load($key);
302:         if ($data === NULL) {
303:             return new OutputHelper($this, $key);
304:         }
305:         echo $data;
306:     }
307: 
308: 
309:     /**
310:      * Generates internal cache key.
311:      *
312:      * @param  string
313:      * @return string
314:      */
315:     protected function generateKey($key)
316:     {
317:         return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
318:     }
319: 
320: 
321:     /********************* dependency checkers ****************d*g**/
322: 
323: 
324:     /**
325:      * Checks CALLBACKS dependencies.
326:      * @param  array
327:      * @return bool
328:      */
329:     public static function checkCallbacks($callbacks)
330:     {
331:         foreach ($callbacks as $callback) {
332:             if (!call_user_func_array(array_shift($callback), $callback)) {
333:                 return FALSE;
334:             }
335:         }
336:         return TRUE;
337:     }
338: 
339: 
340:     /**
341:      * Checks CONSTS dependency.
342:      * @param  string
343:      * @param  mixed
344:      * @return bool
345:      */
346:     private static function checkConst($const, $value)
347:     {
348:         return defined($const) && constant($const) === $value;
349:     }
350: 
351: 
352:     /**
353:      * Checks FILES dependency.
354:      * @param  string
355:      * @param  int
356:      * @return bool
357:      */
358:     private static function checkFile($file, $time)
359:     {
360:         return @filemtime($file) == $time; // @ - stat may fail
361:     }
362: 
363: }
364: 
Nette 2.4-20160930 API API documentation generated by ApiGen 2.8.0