1 | <?php |
||
2 | /** |
||
3 | * This file is part of the SVN-Buddy library. |
||
4 | * For the full copyright and license information, please view |
||
5 | * the LICENSE file that was distributed with this source code. |
||
6 | * |
||
7 | * @copyright Alexander Obuhovich <[email protected]> |
||
8 | * @link https://github.com/console-helpers/svn-buddy |
||
9 | */ |
||
10 | |||
11 | namespace ConsoleHelpers\SVNBuddy\Cache; |
||
12 | |||
13 | |||
14 | use ConsoleHelpers\ConsoleKit\ConsoleIO; |
||
15 | use ConsoleHelpers\SVNBuddy\Helper\SizeHelper; |
||
16 | |||
17 | class CacheManager |
||
18 | { |
||
19 | |||
20 | /** |
||
21 | * Working directory. |
||
22 | * |
||
23 | * @var string |
||
24 | */ |
||
25 | private $_workingDirectory; |
||
26 | |||
27 | /** |
||
28 | * Console IO. |
||
29 | * |
||
30 | * @var ConsoleIO |
||
31 | */ |
||
32 | private $_io; |
||
33 | |||
34 | /** |
||
35 | * Size helper. |
||
36 | * |
||
37 | * @var SizeHelper |
||
38 | */ |
||
39 | private $_sizeHelper; |
||
40 | |||
41 | /** |
||
42 | * Statistics. |
||
43 | * |
||
44 | * @var array |
||
45 | */ |
||
46 | private $_statistics = array(); |
||
47 | |||
48 | /** |
||
49 | * Create cache manager. |
||
50 | * |
||
51 | * @param string $working_directory Working directory. |
||
52 | * @param SizeHelper $size_helper Size helper. |
||
53 | * @param ConsoleIO $io Console IO. |
||
54 | */ |
||
55 | 25 | public function __construct($working_directory, SizeHelper $size_helper, ConsoleIO $io = null) |
|
56 | { |
||
57 | 25 | $this->_workingDirectory = $working_directory; |
|
58 | 25 | $this->_sizeHelper = $size_helper; |
|
59 | 25 | $this->_io = $io; |
|
60 | } |
||
61 | |||
62 | /** |
||
63 | * Sets value in cache. |
||
64 | * |
||
65 | * @param string $name Name. |
||
66 | * @param mixed $value Value. |
||
67 | * @param mixed $invalidator Invalidator. |
||
68 | * @param integer $duration Duration in seconds. |
||
69 | * |
||
70 | * @return void |
||
71 | */ |
||
72 | 12 | public function setCache($name, $value, $invalidator = null, $duration = null) |
|
73 | { |
||
74 | 12 | $duration = $this->durationIntoSeconds($duration); |
|
75 | |||
76 | 12 | $storage = $this->_getStorage($name, $duration); |
|
77 | 11 | $storage->set(array( |
|
78 | 11 | 'name' => $name, |
|
79 | 11 | 'invalidator' => $invalidator, |
|
80 | 11 | 'duration' => $duration, |
|
81 | 11 | 'expiration' => $duration ? time() + $duration : null, |
|
82 | 11 | 'data' => $value, |
|
83 | 11 | )); |
|
84 | } |
||
85 | |||
86 | /** |
||
87 | * Gets value from cache. |
||
88 | * |
||
89 | * @param string $name Name. |
||
90 | * @param mixed $invalidator Invalidator. |
||
91 | * @param integer $duration Duration in seconds. |
||
92 | * |
||
93 | * @return mixed |
||
94 | */ |
||
95 | 10 | public function getCache($name, $invalidator = null, $duration = null) |
|
96 | { |
||
97 | 10 | $storage = $this->_getStorage($name, $this->durationIntoSeconds($duration)); |
|
98 | 10 | $cache = $storage->get(); |
|
99 | |||
100 | 10 | if ( !is_array($cache) || $cache['invalidator'] !== $invalidator ) { |
|
101 | 4 | $storage->invalidate(); |
|
102 | 4 | $this->registerMiss($storage, is_array($cache) ? 'invalidated' : 'absent'); |
|
0 ignored issues
–
show
introduced
by
![]() |
|||
103 | |||
104 | 4 | return null; |
|
105 | } |
||
106 | |||
107 | 7 | if ( $cache['expiration'] && $cache['expiration'] < time() ) { |
|
108 | 2 | $storage->invalidate(); |
|
109 | 2 | $this->registerMiss($storage, 'expired'); |
|
110 | |||
111 | 2 | return null; |
|
112 | } |
||
113 | |||
114 | 6 | $this->registerHit($storage); |
|
115 | |||
116 | 6 | return $cache['data']; |
|
117 | } |
||
118 | |||
119 | /** |
||
120 | * Registers hit. |
||
121 | * |
||
122 | * @param ICacheStorage $storage Storage. |
||
123 | * |
||
124 | * @return void |
||
125 | */ |
||
126 | 6 | protected function registerHit(ICacheStorage $storage) |
|
127 | { |
||
128 | 6 | if ( !isset($this->_io) || !$this->_io->isVerbose() ) { |
|
129 | 5 | return; |
|
130 | } |
||
131 | |||
132 | 1 | $unique_id = $storage->getUniqueId(); |
|
133 | 1 | $message = $unique_id; |
|
134 | |||
135 | 1 | $this->initStatistics($storage); |
|
136 | |||
137 | 1 | $this->_statistics[$unique_id]['hits']++; |
|
138 | 1 | $message .= sprintf( |
|
139 | 1 | ' (hit #%d: %s)', |
|
140 | 1 | $this->_statistics[$unique_id]['hits'], |
|
141 | 1 | $this->_sizeHelper->formatSize($storage->getSize()) |
|
142 | 1 | ); |
|
143 | |||
144 | 1 | $this->_io->writeln(array( |
|
145 | 1 | '', |
|
146 | 1 | '<debug>[cache]: ' . $message . '</debug>', |
|
147 | 1 | )); |
|
148 | } |
||
149 | |||
150 | /** |
||
151 | * Registers miss. |
||
152 | * |
||
153 | * @param ICacheStorage $storage Storage. |
||
154 | * @param string $reason Reason. |
||
155 | * |
||
156 | * @return void |
||
157 | */ |
||
158 | 5 | protected function registerMiss(ICacheStorage $storage, $reason) |
|
159 | { |
||
160 | 5 | if ( !isset($this->_io) || !$this->_io->isVerbose() ) { |
|
161 | 4 | return; |
|
162 | } |
||
163 | |||
164 | 1 | $unique_id = $storage->getUniqueId(); |
|
165 | 1 | $message = $unique_id; |
|
166 | |||
167 | 1 | $this->initStatistics($storage); |
|
168 | |||
169 | 1 | $this->_statistics[$unique_id]['misses']++; |
|
170 | 1 | $message .= sprintf( |
|
171 | 1 | ' (miss:' . $reason . ' #%d)', |
|
172 | 1 | $this->_statistics[$unique_id]['misses'] |
|
173 | 1 | ); |
|
174 | |||
175 | 1 | $this->_io->writeln(array( |
|
176 | 1 | '', |
|
177 | 1 | '<debug>[cache]: ' . $message . '</debug>', |
|
178 | 1 | )); |
|
179 | } |
||
180 | |||
181 | /** |
||
182 | * Initializes statistics. |
||
183 | * |
||
184 | * @param ICacheStorage $storage Storage. |
||
185 | * |
||
186 | * @return void |
||
187 | */ |
||
188 | 2 | protected function initStatistics(ICacheStorage $storage) |
|
189 | { |
||
190 | 2 | $unique_id = $storage->getUniqueId(); |
|
191 | |||
192 | 2 | if ( !array_key_exists($unique_id, $this->_statistics) ) { |
|
193 | 2 | $this->_statistics[$unique_id] = array('hits' => 0, 'misses' => 0); |
|
194 | } |
||
195 | } |
||
196 | |||
197 | /** |
||
198 | * Deletes value from cache. |
||
199 | * |
||
200 | * @param string $name Name. |
||
201 | * @param integer $duration Duration in seconds. |
||
202 | * |
||
203 | * @return void |
||
204 | */ |
||
205 | 3 | public function deleteCache($name, $duration = null) |
|
206 | { |
||
207 | 3 | $storage = $this->_getStorage($name, $this->durationIntoSeconds($duration)); |
|
208 | 3 | $storage->invalidate(); |
|
209 | } |
||
210 | |||
211 | /** |
||
212 | * Converts duration into seconds. |
||
213 | * |
||
214 | * @param integer $duration Duration in seconds. |
||
215 | * |
||
216 | * @return integer|null |
||
217 | */ |
||
218 | 15 | protected function durationIntoSeconds($duration = null) |
|
219 | { |
||
220 | 15 | if ( !isset($duration) ) { |
|
221 | 14 | return null; |
|
222 | } |
||
223 | |||
224 | 3 | if ( is_numeric($duration) ) { |
|
0 ignored issues
–
show
|
|||
225 | 2 | $duration .= ' seconds'; |
|
226 | } |
||
227 | |||
228 | 3 | $now = time(); |
|
229 | |||
230 | 3 | return strtotime('+' . $duration, $now) - $now; |
|
231 | } |
||
232 | |||
233 | /** |
||
234 | * Returns file-based cache storage. |
||
235 | * |
||
236 | * @param string $name Cache name. |
||
237 | * @param integer $duration Duration in seconds. |
||
238 | * |
||
239 | * @return ICacheStorage |
||
240 | * @throws \InvalidArgumentException When namespace is missing in the name. |
||
241 | */ |
||
242 | 15 | private function _getStorage($name, $duration = null) |
|
243 | { |
||
244 | 15 | $name_parts = explode(':', $name, 2); |
|
245 | |||
246 | 15 | if ( count($name_parts) != 2 ) { |
|
247 | 1 | throw new \InvalidArgumentException('The $name parameter must be in "namespace:name" format.'); |
|
248 | } |
||
249 | |||
250 | 14 | $filename_parts = array( |
|
251 | 14 | $name_parts[0], |
|
252 | 14 | substr(hash_hmac('sha1', $name_parts[1], 'svn-buddy'), 0, 8), |
|
253 | 14 | 'D' . (isset($duration) ? $duration : 'INF'), |
|
254 | 14 | ); |
|
255 | |||
256 | 14 | return new FileCacheStorage( |
|
257 | 14 | $this->_workingDirectory . DIRECTORY_SEPARATOR . implode('_', $filename_parts) . '.cache' |
|
258 | 14 | ); |
|
259 | } |
||
260 | |||
261 | } |
||
262 |