1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Liip\ImagineBundle\Imagine\Cache; |
4
|
|
|
|
5
|
|
|
use Liip\ImagineBundle\Imagine\Cache\Warmer\WarmerInterface; |
6
|
|
|
use Liip\ImagineBundle\Imagine\Data\DataManager; |
7
|
|
|
use Liip\ImagineBundle\Imagine\Filter\FilterManager; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Class CacheWarmer |
11
|
|
|
* |
12
|
|
|
* @author Konstantin Tjuterev <[email protected]> |
13
|
|
|
*/ |
14
|
|
|
class CacheWarmer |
15
|
|
|
{ |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* @var WarmerInterface[] |
19
|
|
|
*/ |
20
|
|
|
protected $warmers = array(); |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Chunk size to query warmer in one step |
24
|
|
|
* |
25
|
|
|
* @var int |
26
|
|
|
*/ |
27
|
|
|
protected $chunkSize = 100; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var CacheManager |
31
|
|
|
*/ |
32
|
|
|
protected $cacheManager; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var DataManager |
36
|
|
|
*/ |
37
|
|
|
protected $dataManager; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var FilterManager |
41
|
|
|
*/ |
42
|
|
|
protected $filterManager; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var callable |
46
|
|
|
*/ |
47
|
|
|
protected $loggerClosure; |
48
|
|
|
|
49
|
|
|
public function __construct(DataManager $dataManager, FilterManager $filterManager) |
50
|
|
|
{ |
51
|
|
|
$this->dataManager = $dataManager; |
52
|
|
|
$this->filterManager = $filterManager; |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @param int $chunkSize |
57
|
|
|
* |
58
|
|
|
* @return CacheWarmer |
59
|
|
|
*/ |
60
|
|
|
public function setChunkSize($chunkSize) |
61
|
|
|
{ |
62
|
|
|
$this->chunkSize = $chunkSize; |
63
|
|
|
|
64
|
|
|
return $this; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Sets logger closure - a callable which will be passed verbose messages |
69
|
|
|
* |
70
|
|
|
* @param callable $loggerClosure |
71
|
|
|
* |
72
|
|
|
* @return CacheWarmer |
73
|
|
|
*/ |
74
|
|
|
public function setLoggerClosure($loggerClosure) |
75
|
|
|
{ |
76
|
|
|
$this->loggerClosure = $loggerClosure; |
77
|
|
|
|
78
|
|
|
return $this; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @param \Liip\ImagineBundle\Imagine\Cache\CacheManager $cacheManager |
83
|
|
|
* |
84
|
|
|
* @return CacheWarmer |
85
|
|
|
*/ |
86
|
|
|
public function setCacheManager($cacheManager) |
87
|
|
|
{ |
88
|
|
|
$this->cacheManager = $cacheManager; |
89
|
|
|
|
90
|
|
|
return $this; |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
public function addWarmer($name, WarmerInterface $warmer) |
94
|
|
|
{ |
95
|
|
|
$this->warmers[$name] = $warmer; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param bool $force If set to true, cache is warmed up for paths already stored in cached (regenerate thumbs) |
100
|
|
|
* @param null|array $selectedWarmers An optional array of warmers to process, if null - all warmers will be processed |
101
|
|
|
* |
102
|
|
|
* @return void |
103
|
|
|
* @throws \InvalidArgumentException |
104
|
|
|
*/ |
105
|
|
|
public function warm($force = false, $selectedWarmers = null) |
106
|
|
|
{ |
107
|
|
|
$filtersByWarmer = $this->getFiltersByWarmers(); |
108
|
|
|
if (!$filtersByWarmer) { |
|
|
|
|
109
|
|
|
$this->log(sprintf('No warmes are configured - add some as `warmers` param in your filter sets')); |
110
|
|
|
return; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
foreach ($filtersByWarmer as $warmerName => $filters) { |
114
|
|
|
if (isset($selectedWarmers) && !empty($selectedWarmers) && !in_array($warmerName, $selectedWarmers)) { |
115
|
|
|
$this->log( |
116
|
|
|
sprintf( |
117
|
|
|
'Skipping warmer %s as it\'s not listed in selected warmers: [%s]', |
118
|
|
|
$warmerName, |
119
|
|
|
join(', ', $selectedWarmers) |
120
|
|
|
) |
121
|
|
|
); |
122
|
|
|
continue; |
123
|
|
|
} |
124
|
|
|
if (!isset($this->warmers[$warmerName])) { |
125
|
|
|
throw new \InvalidArgumentException(sprintf( |
126
|
|
|
'Could not find warmer "%s"', $warmerName |
127
|
|
|
)); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
$this->log(sprintf('Processing warmer "%s"', $warmerName)); |
131
|
|
|
$start = 0; |
132
|
|
|
$warmer = $this->warmers[$warmerName]; |
133
|
|
|
while ($paths = $warmer->getPaths($start, $this->chunkSize)) { |
134
|
|
|
$this->log( |
135
|
|
|
sprintf( |
136
|
|
|
'Processing chunk %d - %d for warmer "%s"', |
137
|
|
|
$start, |
138
|
|
|
$start + $this->chunkSize, |
139
|
|
|
$warmerName |
140
|
|
|
) |
141
|
|
|
); |
142
|
|
|
$warmedPaths = $this->warmPaths($paths, $filters, $force); |
143
|
|
|
$warmer->setWarmed($warmedPaths); |
144
|
|
|
$start += count($paths) - count($warmedPaths); |
145
|
|
|
} |
146
|
|
|
$this->log(sprintf('Finished processing warmer "%s"', $warmerName)); |
147
|
|
|
} |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
public function clearWarmed($paths, $filters) |
151
|
|
|
{ |
152
|
|
|
$filtersByWarmer = $this->getFiltersByWarmers(); |
153
|
|
|
foreach ($filtersByWarmer as $warmerName => $warmerFilters) { |
154
|
|
|
if (array_intersect($filters, $warmerFilters)) { |
155
|
|
|
if (!isset($this->warmers[$warmerName])) { |
156
|
|
|
throw new \InvalidArgumentException(sprintf( |
157
|
|
|
'Could not find warmer "%s"', $warmerName |
158
|
|
|
)); |
159
|
|
|
} |
160
|
|
|
$warmer = $this->warmers[$warmerName]; |
161
|
|
|
$warmer->clearWarmed($paths); |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
protected function getFiltersByWarmers() |
167
|
|
|
{ |
168
|
|
|
$all = $this->filterManager->getFilterConfiguration()->all(); |
169
|
|
|
$warmers = array(); |
170
|
|
|
foreach ($all as $filterSet => $config) { |
171
|
|
|
if (isset($config['warmers']) && $config['warmers']) { |
172
|
|
|
foreach ($config['warmers'] as $warmer) { |
173
|
|
|
if (!isset($warmers[$warmer])) { |
174
|
|
|
$warmers[$warmer] = array($filterSet); |
175
|
|
|
} else { |
176
|
|
|
$warmers[$warmer][] = $filterSet; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $warmers; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* @param array $paths |
187
|
|
|
* @param array $filters |
188
|
|
|
* @param bool $force |
189
|
|
|
* |
190
|
|
|
* @return array |
191
|
|
|
*/ |
192
|
|
|
protected function warmPaths($paths, $filters, $force) |
193
|
|
|
{ |
194
|
|
|
$successfulWarmedPaths = []; |
195
|
|
|
foreach ($paths as $pathData) { |
196
|
|
|
$aPath = $pathData['path']; |
197
|
|
|
$binaries = array(); |
198
|
|
|
foreach ($filters as $filter) { |
199
|
|
|
$this->log(sprintf('Warming up path "%s" for filter "%s"', $aPath, $filter)); |
200
|
|
|
|
201
|
|
|
$isStored = $this->cacheManager->isStored($aPath, $filter); |
202
|
|
|
if ($force || !$isStored) { |
203
|
|
|
// this is to avoid loading binary with the same loader for multiple filters |
204
|
|
|
$loader = $this->dataManager->getLoader($filter); |
205
|
|
|
$isStored = false; |
206
|
|
|
|
207
|
|
|
try { |
208
|
|
|
$hash = spl_object_hash($loader); |
209
|
|
|
if (!isset($binaries[$hash])) { |
210
|
|
|
// if NotLoadable is thrown - it will just bubble up |
211
|
|
|
// everything returned by Warmer should be loadable |
212
|
|
|
$binaries[$hash] = $this->dataManager->find($filter, $aPath); |
213
|
|
|
} |
214
|
|
|
$this->cacheManager->store( |
215
|
|
|
$this->filterManager->applyFilter($binaries[$hash], $filter), |
216
|
|
|
$aPath, |
217
|
|
|
$filter |
218
|
|
|
); |
219
|
|
|
|
220
|
|
|
$isStored = true; |
221
|
|
|
} catch (\RuntimeException $e) { |
222
|
|
|
$message = sprintf('Unable to warm cache for filter "%s", due to - "%s"', |
223
|
|
|
$filter, $e->getMessage()); |
224
|
|
|
$this->log($message, 'error'); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
if ($isStored) { |
229
|
|
|
$successfulWarmedPaths[] = $pathData; |
230
|
|
|
} |
231
|
|
|
} |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return $successfulWarmedPaths; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
protected function log($message, $type = 'info') |
238
|
|
|
{ |
239
|
|
|
if (is_callable($this->loggerClosure)) { |
240
|
|
|
$loggerClosure = $this->loggerClosure; |
241
|
|
|
$loggerClosure($message, $type); |
242
|
|
|
} |
243
|
|
|
} |
244
|
|
|
} |
245
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.