Completed
Pull Request — 2.x (#1258)
by
unknown
01:50
created

CacheWarmer::warmPaths()   B

Complexity

Conditions 8
Paths 16

Size

Total Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 44
rs 7.9715
c 0
b 0
f 0
cc 8
nc 16
nop 3
1
<?php
2
3
namespace Liip\ImagineBundle\Imagine\Cache;
4
5
use InvalidArgumentException;
6
use Liip\ImagineBundle\Imagine\Cache\Warmer\WarmerInterface;
7
use Liip\ImagineBundle\Imagine\Data\DataManager;
8
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
9
10
/**
11
 * Class CacheWarmer
12
 *
13
 * @author Konstantin Tjuterev <[email protected]>
14
 */
15
class CacheWarmer
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
    /**
50
     * CacheWarmer constructor.
51
     *
52
     * @param DataManager   $dataManager
53
     * @param FilterManager $filterManager
54
     */
55
    public function __construct(DataManager $dataManager, FilterManager $filterManager)
56
    {
57
        $this->dataManager = $dataManager;
58
        $this->filterManager = $filterManager;
59
    }
60
61
    /**
62
     * @param int $chunkSize
63
     *
64
     * @return CacheWarmer
65
     */
66
    public function setChunkSize($chunkSize)
67
    {
68
        $this->chunkSize = $chunkSize;
69
70
        return $this;
71
    }
72
73
    /**
74
     * Sets logger closure - a callable which will be passed verbose messages
75
     *
76
     * @param callable $loggerClosure
77
     *
78
     * @return CacheWarmer
79
     */
80
    public function setLoggerClosure($loggerClosure)
81
    {
82
        $this->loggerClosure = $loggerClosure;
83
84
        return $this;
85
    }
86
87
    /**
88
     * @param CacheManager $cacheManager
89
     *
90
     * @return CacheWarmer
91
     */
92
    public function setCacheManager($cacheManager)
93
    {
94
        $this->cacheManager = $cacheManager;
95
96
        return $this;
97
    }
98
99
    /**
100
     * @param string          $name
101
     * @param WarmerInterface $warmer
102
     */
103
    public function addWarmer($name, WarmerInterface $warmer)
104
    {
105
        $this->warmers[$name] = $warmer;
106
    }
107
108
    /**
109
     * @param bool       $force           If set to true, cache is warmed up for paths already stored in cached (regenerate thumbs)
110
     * @param null|array $selectedWarmers An optional array of warmers to process, if null - all warmers will be processed
111
     *
112
     * @return void
113
     * @throws InvalidArgumentException
114
     */
115
    public function warm($force = false, $selectedWarmers = null)
116
    {
117
        $filtersByWarmer = $this->getFiltersByWarmers();
118
        if (!$filtersByWarmer) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filtersByWarmer of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
119
            $this->log('No warmers are configured - add some as `warmers` param in your filter sets');
120
            return;
121
        }
122
123
        foreach ($filtersByWarmer as $warmerName => $filters) {
124
            if (isset($selectedWarmers) && !empty($selectedWarmers) && !in_array($warmerName, $selectedWarmers)) {
125
                $this->log(
126
                    sprintf(
127
                        'Skipping warmer %s as it\'s not listed in selected warmers: [%s]',
128
                        $warmerName,
129
                        join(', ', $selectedWarmers)
130
                    )
131
                );
132
                continue;
133
            }
134
            if (!isset($this->warmers[$warmerName])) {
135
                throw new InvalidArgumentException(sprintf('Could not find warmer "%s"', $warmerName));
136
            }
137
138
            $this->log(sprintf('Processing warmer "%s"', $warmerName));
139
            $start = 0;
140
            $warmer = $this->warmers[$warmerName];
141
            while ($paths = $warmer->getPaths($start, $this->chunkSize)) {
142
                $this->log(
143
                    sprintf(
144
                        'Processing chunk %d - %d for warmer "%s"',
145
                        $start,
146
                        $start + $this->chunkSize,
147
                        $warmerName
148
                    )
149
                );
150
                $warmedPaths = $this->warmPaths($paths, $filters, $force);
151
                $warmer->setWarmed($warmedPaths);
152
                $start += count($paths) - count($warmedPaths);
153
            }
154
            $this->log(sprintf('Finished processing warmer "%s"', $warmerName));
155
        }
156
    }
157
158
    /**
159
     * @param array $paths
160
     * @param array $filters
161
     */
162
    public function clearWarmed($paths, $filters)
163
    {
164
        $filtersByWarmer = $this->getFiltersByWarmers();
165
        foreach ($filtersByWarmer as $warmerName => $warmerFilters) {
166
            if (array_intersect($filters, $warmerFilters)) {
167
                if (!isset($this->warmers[$warmerName])) {
168
                    throw new InvalidArgumentException(sprintf(
169
                        'Could not find warmer "%s"', $warmerName
170
                    ));
171
                }
172
                $warmer = $this->warmers[$warmerName];
173
                $warmer->clearWarmed($paths);
174
            }
175
        }
176
    }
177
178
    /**
179
     * @return array
180
     */
181
    protected function getFiltersByWarmers()
182
    {
183
        $all = $this->filterManager->getFilterConfiguration()->all();
184
        $warmers = array();
185
        foreach ($all as $filterSet => $config) {
186
            if (isset($config['warmers']) && $config['warmers']) {
187
                foreach ($config['warmers'] as $warmer) {
188
                    if (!isset($warmers[$warmer])) {
189
                        $warmers[$warmer] = array($filterSet);
190
                    } else {
191
                        $warmers[$warmer][] = $filterSet;
192
                    }
193
                }
194
            }
195
        }
196
197
        return $warmers;
198
    }
199
200
    /**
201
     * @param array $paths
202
     * @param array $filters
203
     * @param bool  $force
204
     *
205
     * @return array
206
     */
207
    protected function warmPaths($paths, $filters, $force)
208
    {
209
        $successfulWarmedPaths = [];
210
        foreach ($paths as $pathData) {
211
            $aPath = $pathData['path'];
212
            $binaries = array();
213
            foreach ($filters as $filter) {
214
                $this->log(sprintf('Warming up path "%s" for filter "%s"', $aPath, $filter));
215
216
                $isStored = $this->cacheManager->isStored($aPath, $filter);
217
                if ($force || !$isStored) {
218
                    // this is to avoid loading binary with the same loader for multiple filters
219
                    $loader   = $this->dataManager->getLoader($filter);
220
                    $isStored = false;
221
222
                    try {
223
                        $hash = spl_object_hash($loader);
224
                        if (!isset($binaries[$hash])) {
225
                            // if NotLoadable is thrown - it will just bubble up
226
                            // everything returned by Warmer should be loadable
227
                            $binaries[$hash] = $this->dataManager->find($filter, $aPath);
228
                        }
229
                        $this->cacheManager->store(
230
                            $this->filterManager->applyFilter($binaries[$hash], $filter),
231
                            $aPath,
232
                            $filter
233
                        );
234
235
                        $isStored = true;
236
                    } catch (\RuntimeException $e) {
237
                        $message = sprintf('Unable to warm cache for filter "%s", due to - "%s"',
238
                            $filter, $e->getMessage());
239
                        $this->log($message, 'error');
240
                    }
241
                }
242
243
                if ($isStored) {
244
                    $successfulWarmedPaths[] = $pathData;
245
                }
246
            }
247
        }
248
249
        return $successfulWarmedPaths;
250
    }
251
252
    /**
253
     * @param string $message
254
     * @param string $type
255
     */
256
    protected function log($message, $type = 'info')
257
    {
258
        if (is_callable($this->loggerClosure)) {
259
            $loggerClosure = $this->loggerClosure;
260
            $loggerClosure($message, $type);
261
        }
262
    }
263
}
264