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