Completed
Push — master ( 8f3e48...b96168 )
by Daniel
01:37
created

AssetEngine::js()   A

Complexity

Conditions 6
Paths 12

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6.0493

Importance

Changes 0
Metric Value
cc 6
eloc 18
nc 12
nop 2
dl 0
loc 28
ccs 16
cts 18
cp 0.8889
crap 6.0493
rs 9.0444
c 0
b 0
f 0
1
<?php
2
3
namespace Odan\PlatesAsset;
4
5
use JSMin\JSMin;
6
use RuntimeException;
7
use Symfony\Component\Cache\Adapter\AdapterInterface;
8
use Symfony\Component\Cache\Adapter\ArrayAdapter;
9
use tubalmartin\CssMin\Minifier as CssMinifier;
10
11
/**
12
 * Extension that adds the ability to cache and minify assets.
13
 */
14
final class AssetEngine
15
{
16
    /**
17
     * @var AdapterInterface Cache
18
     */
19
    private $cache;
20
21
    /**
22
     * @var AssetCache Asset cache
23
     */
24
    private $publicCache;
25
26
    /**
27
     * Enables minify.
28
     *
29
     * @var array
30
     */
31
    private $options = [
32
        'minify' => true,
33
        'inline' => true,
34
        'public_dir' => null,
35
        'name' => 'file',
36
    ];
37
38
    /**
39
     * Create new instance.
40
     *
41
     * @param array $options The options
42
     */
43 3
    public function __construct(array $options)
44
    {
45 3
        if (!empty($options['cache']) && $options['cache'] instanceof AdapterInterface) {
46 3
            $this->cache = $options['cache'];
47
        } else {
48
            $this->cache = new ArrayAdapter();
49
        }
50 3
        $this->publicCache = new AssetCache($options['public_dir']);
51
52 3
        unset($options['public_cache'], $options['cache']);
53
54 3
        $this->options = (array)array_replace_recursive($this->options, $options);
55 3
    }
56
57
    /**
58
     * Render and compress JavaScript assets.
59
     *
60
     * @param string $asset The asset
61
     * @param array $options The options
62
     *
63
     * @return string The content
64
     */
65 2
    public function assetFile(string $asset, array $options = []): string
66
    {
67 2
        return $this->assetFiles((array)$asset, $options);
68
    }
69
70
    /**
71
     * Render and compress JavaScript assets.
72
     *
73
     * @param array $assets The assets
74
     * @param array $options The options
75
     *
76
     * @return string The content
77
     */
78 2
    public function assetFiles(array $assets, array $options = []): string
79
    {
80 2
        $assets = $this->prepareAssets($assets);
81 2
        $options = (array)array_replace_recursive($this->options, $options);
82
83 2
        $cacheKey = $this->getCacheKey($assets, $options);
84 2
        $cacheItem = $this->cache->getItem($cacheKey);
85 2
        if ($cacheItem->isHit()) {
86 2
            return $cacheItem->get();
87
        }
88
89 2
        $jsFiles = [];
90 2
        $cssFiles = [];
91 2
        foreach ($assets as $file) {
92 2
            $fileType = strtolower(pathinfo($file, PATHINFO_EXTENSION));
93 2
            if ($fileType === 'js') {
94 2
                $jsFiles[] = $file;
95
            }
96 2
            if ($fileType === 'css') {
97 2
                $cssFiles[] = $file;
98
            }
99
        }
100 2
        $cssContent = $this->css($cssFiles, $options);
101 2
        $jsContent = $this->js($jsFiles, $options);
102 2
        $result = $cssContent . $jsContent;
103
104 2
        $cacheItem->set($result);
105 2
        $this->cache->save($cacheItem);
106
107 2
        return $result;
108
    }
109
110
    /**
111
     * Resolve real asset filenames.
112
     *
113
     * @param array $assets The assets
114
     *
115
     * @return array The real filenames
116
     */
117 2
    protected function prepareAssets(array $assets): array
118
    {
119 2
        $result = [];
120 2
        foreach ($assets as $name) {
121 2
            $result[] = $this->getRealFilename($name);
122
        }
123
124 2
        return $result;
125
    }
126
127
    /**
128
     * Render and compress CSS assets.
129
     *
130
     * @param array $assets The assets
131
     * @param array $options The options
132
     *
133
     * @return string The content
134
     */
135 2
    public function js(array $assets, array $options): string
136
    {
137 2
        $contents = [];
138 2
        $public = '';
139 2
        foreach ($assets as $asset) {
140 2
            if ($this->isExternalUrl($asset)) {
141
                // External url
142
                $contents[] = sprintf('<script src="%s"></script>', $asset);
143
                continue;
144
            }
145 2
            $content = $this->getJsContent($asset, $options['minify']);
146
147 2
            if (!empty($options['inline'])) {
148 1
                $contents[] = sprintf('<script>%s</script>', $content);
149
            } else {
150 2
                $public .= $content . '';
151
            }
152
        }
153 2
        if ($public !== '') {
0 ignored issues
show
introduced by
The condition $public !== '' is always false.
Loading history...
154 1
            $name = $options['name'] ?? 'file.js';
155 1
            if (empty(pathinfo($name, PATHINFO_EXTENSION))) {
156 1
                $name .= '.js';
157
            }
158 1
            $url = $this->publicCache->createCacheBustedUrl($name, $public);
159 1
            $contents[] = sprintf('<script src="%s"></script>', $url);
160
        }
161
162 2
        return implode("\n", $contents);
163
    }
164
165
    /**
166
     * Minimize JavaScript content.
167
     *
168
     * @param string $filename Name of default JS file
169
     * @param bool $minify Minify js if true
170
     *
171
     * @throws RuntimeException
172
     *
173
     * @return string JavaScript code
174
     */
175 2
    protected function getJsContent(string $filename, bool $minify): string
176
    {
177 2
        $content = file_get_contents($filename);
178
179 2
        if ($content === false) {
180
            throw new RuntimeException(sprintf('File could be read: %s', $filename));
181
        }
182
183 2
        if ($minify) {
184 2
            $content = JSMin::minify($content);
185
        }
186
187 2
        return $content;
188
    }
189
190
    /**
191
     * Render and compress CSS assets.
192
     *
193
     * @param array $assets The assets
194
     * @param array $options The options
195
     *
196
     * @return string The content
197
     */
198 2
    public function css(array $assets, array $options): string
199
    {
200 2
        $contents = [];
201 2
        $public = '';
202
203 2
        foreach ($assets as $asset) {
204
            if ($this->isExternalUrl($asset)) {
205
                // External url
206
                $contents[] = sprintf('<link rel="stylesheet" type="text/css" href="%s" media="all" />', $asset);
207
                continue;
208
            }
209
            $content = $this->getCssContent($asset, $options['minify']);
210
211
            if (!empty($options['inline'])) {
212
                $contents[] = sprintf('<style>%s</style>', $content);
213
            } else {
214
                $public .= $content . '';
215
            }
216
        }
217
218 2
        if ($public !== '') {
0 ignored issues
show
introduced by
The condition $public !== '' is always false.
Loading history...
219
            $name = $options['name'] ?? 'file.css';
220
            if (empty(pathinfo($name, PATHINFO_EXTENSION))) {
221
                $name .= '.css';
222
            }
223
            $url = $this->publicCache->createCacheBustedUrl($name, $public);
224
            $contents[] = sprintf('<link rel="stylesheet" type="text/css" href="%s" media="all" />', $url);
225
        }
226
227 2
        return implode("\n", $contents);
228
    }
229
230
    /**
231
     * Minimize CSS.
232
     *
233
     * @param string $fileName Name of default CSS file
234
     * @param bool $minify Minify css if true
235
     *
236
     * @throws RuntimeException
237
     *
238
     * @return string CSS code
239
     */
240
    public function getCssContent(string $fileName, bool $minify): string
241
    {
242
        $content = file_get_contents($fileName);
243
244
        if ($content === false) {
245
            throw new RuntimeException(sprintf('File could be read: %s', $fileName));
246
        }
247
248
        if ($minify === true) {
249
            $compressor = new CssMinifier();
250
            $content = $compressor->run($content);
251
        }
252
253
        return $content;
254
    }
255
256
    /**
257
     * Get cache key.
258
     *
259
     * @param array $assets The assets
260
     * @param array $settings The settings
261
     *
262
     * @return string The cache key
263
     */
264 2
    protected function getCacheKey(array $assets, array $settings = null): string
265
    {
266 2
        $keys = [];
267 2
        foreach ($assets as $file) {
268 2
            $keys[] = sha1_file($file);
269
        }
270 2
        $keys[] = sha1(serialize($settings));
271
272 2
        return sha1(implode('', $keys));
273
    }
274
275
    /**
276
     * Check if url is valid.
277
     *
278
     * @param string $url The url
279
     *
280
     * @return bool The status
281
     */
282 2
    protected function isExternalUrl(string $url): bool
283
    {
284 2
        return (!filter_var($url, FILTER_VALIDATE_URL) === false) && (strpos($url, 'vfs://') === false);
285
    }
286
287
    /**
288
     * Returns full path and filename.
289
     *
290
     * @param string $filename The filename
291
     *
292
     * @return string The real filename
293
     */
294 2
    protected function getRealFilename(string $filename): string
295
    {
296 2
        return $filename;
297
    }
298
}
299