Completed
Pull Request — master (#1258)
by
unknown
01:33
created

CacheWarmer   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 5
dl 0
loc 231
rs 9.76
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setChunkSize() 0 6 1
A setLoggerClosure() 0 6 1
A setCacheManager() 0 6 1
A addWarmer() 0 4 1
B warm() 0 44 8
A clearWarmed() 0 15 4
A getFiltersByWarmers() 0 18 6
B warmPaths() 0 44 8
A log() 0 7 2
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) {
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...
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