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