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: /**
33: * The key or function to generate a key.
34: *
35: * @var string|callable
36: */
37: protected $_key;
38:
39: /**
40: * Config for cache engine.
41: *
42: * @var string|\Cake\Cache\CacheEngine
43: */
44: protected $_config;
45:
46: /**
47: * Constructor.
48: *
49: * @param string|\Closure $key The key or function to generate a key.
50: * @param string|\Cake\Cache\CacheEngine $config The cache config name or cache engine instance.
51: * @throws \RuntimeException
52: */
53: public function __construct($key, $config)
54: {
55: if (!is_string($key) && !is_callable($key)) {
56: throw new RuntimeException('Cache keys must be strings or callables.');
57: }
58: $this->_key = $key;
59:
60: if (!is_string($config) && !($config instanceof CacheEngine)) {
61: throw new RuntimeException('Cache configs must be strings or CacheEngine instances.');
62: }
63: $this->_config = $config;
64: }
65:
66: /**
67: * Load the cached results from the cache or run the query.
68: *
69: * @param object $query The query the cache read is for.
70: * @return \Cake\Datasource\ResultSetInterface|null Either the cached results or null.
71: */
72: public function fetch($query)
73: {
74: $key = $this->_resolveKey($query);
75: $storage = $this->_resolveCacher();
76: $result = $storage->read($key);
77: if (empty($result)) {
78: return null;
79: }
80:
81: return $result;
82: }
83:
84: /**
85: * Store the result set into the cache.
86: *
87: * @param object $query The query the cache read is for.
88: * @param \Traversable $results The result set to store.
89: * @return bool True if the data was successfully cached, false on failure
90: */
91: public function store($query, Traversable $results)
92: {
93: $key = $this->_resolveKey($query);
94: $storage = $this->_resolveCacher();
95:
96: return $storage->write($key, $results);
97: }
98:
99: /**
100: * Get/generate the cache key.
101: *
102: * @param object $query The query to generate a key for.
103: * @return string
104: * @throws \RuntimeException
105: */
106: protected function _resolveKey($query)
107: {
108: if (is_string($this->_key)) {
109: return $this->_key;
110: }
111: $func = $this->_key;
112: $key = $func($query);
113: if (!is_string($key)) {
114: $msg = sprintf('Cache key functions must return a string. Got %s.', var_export($key, true));
115: throw new RuntimeException($msg);
116: }
117:
118: return $key;
119: }
120:
121: /**
122: * Get the cache engine.
123: *
124: * @return \Cake\Cache\CacheEngine
125: */
126: protected function _resolveCacher()
127: {
128: if (is_string($this->_config)) {
129: return Cache::engine($this->_config);
130: }
131:
132: return $this->_config;
133: }
134: }
135: