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