1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.0.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Datasource;
16:
17: use Cake\Cache\Cache;
18: use Cake\Cache\CacheEngine;
19: use RuntimeException;
20: use Traversable;
21:
22: /**
23: * Handles caching queries and loading results from the cache.
24: *
25: * Used by Cake\Datasource\QueryTrait internally.
26: *
27: * @see \Cake\Datasource\QueryTrait::cache() for the public interface.
28: */
29: class QueryCacher
30: {
31: /**
32: * The key or function to generate a key.
33: *
34: * @var string|callable
35: */
36: protected $_key;
37:
38: /**
39: * Config for cache engine.
40: *
41: * @var string|\Cake\Cache\CacheEngine
42: */
43: protected $_config;
44:
45: /**
46: * Constructor.
47: *
48: * @param string|\Closure $key The key or function to generate a key.
49: * @param string|\Cake\Cache\CacheEngine $config The cache config name or cache engine instance.
50: * @throws \RuntimeException
51: */
52: public function __construct($key, $config)
53: {
54: if (!is_string($key) && !is_callable($key)) {
55: throw new RuntimeException('Cache keys must be strings or callables.');
56: }
57: $this->_key = $key;
58:
59: if (!is_string($config) && !($config instanceof CacheEngine)) {
60: throw new RuntimeException('Cache configs must be strings or CacheEngine instances.');
61: }
62: $this->_config = $config;
63: }
64:
65: /**
66: * Load the cached results from the cache or run the query.
67: *
68: * @param object $query The query the cache read is for.
69: * @return \Cake\Datasource\ResultSetInterface|null Either the cached results or null.
70: */
71: public function fetch($query)
72: {
73: $key = $this->_resolveKey($query);
74: $storage = $this->_resolveCacher();
75: $result = $storage->read($key);
76: if (empty($result)) {
77: return null;
78: }
79:
80: return $result;
81: }
82:
83: /**
84: * Store the result set into the cache.
85: *
86: * @param object $query The query the cache read is for.
87: * @param \Traversable $results The result set to store.
88: * @return bool True if the data was successfully cached, false on failure
89: */
90: public function store($query, Traversable $results)
91: {
92: $key = $this->_resolveKey($query);
93: $storage = $this->_resolveCacher();
94:
95: return $storage->write($key, $results);
96: }
97:
98: /**
99: * Get/generate the cache key.
100: *
101: * @param object $query The query to generate a key for.
102: * @return string
103: * @throws \RuntimeException
104: */
105: protected function _resolveKey($query)
106: {
107: if (is_string($this->_key)) {
108: return $this->_key;
109: }
110: $func = $this->_key;
111: $key = $func($query);
112: if (!is_string($key)) {
113: $msg = sprintf('Cache key functions must return a string. Got %s.', var_export($key, true));
114: throw new RuntimeException($msg);
115: }
116:
117: return $key;
118: }
119:
120: /**
121: * Get the cache engine.
122: *
123: * @return \Cake\Cache\CacheEngine
124: */
125: protected function _resolveCacher()
126: {
127: if (is_string($this->_config)) {
128: return Cache::engine($this->_config);
129: }
130:
131: return $this->_config;
132: }
133: }
134: