Test Setup Failed
Push — master ( a3536c...4068f0 )
by Revin
03:28
created

CSS::process()   D

Complexity

Conditions 13
Paths 5

Size

Total Lines 87
Code Lines 49

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 52
CRAP Score 13.1139

Importance

Changes 0
Metric Value
dl 0
loc 87
ccs 52
cts 57
cp 0.9123
rs 4.9922
c 0
b 0
f 0
cc 13
eloc 49
nc 5
nop 1
crap 13.1139

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * CSS.php
4
 * @author Revin Roman
5
 * @link https://rmrevin.com
6
 */
7
8
namespace rmrevin\yii\minify\components;
9
10
use yii\helpers\Html;
11
12
/**
13
 * Class CSS
14
 * @package rmrevin\yii\minify\components
15
 */
16
class CSS extends MinifyComponent
17
{
18
19 9
    public function export()
20
    {
21 9
        $cssFiles = $this->view->cssFiles;
22
23 9
        $this->view->cssFiles = [];
24
25 9
        $toMinify = [];
26
27 9
        foreach ($cssFiles as $file => $html) {
28 8
            if ($this->thisFileNeedMinify($file, $html)) {
29 8
                if ($this->view->concatCss) {
30 8
                    $toMinify[$file] = $html;
31 8
                } else {
32
                    $this->process([$file => $html]);
33
                }
34 8
            } else {
35 2
                if (!empty($toMinify)) {
36 2
                    $this->process($toMinify);
37
38 2
                    $toMinify = [];
39 2
                }
40
41 2
                $this->view->cssFiles[$file] = $html;
42
            }
43 9
        }
44
45 9
        if (!empty($toMinify)) {
46 6
            $this->process($toMinify);
47 6
        }
48
49 9
        unset($toMinify);
50 9
    }
51
52
    /**
53
     * @param array $files
54
     */
55 8
    protected function process(array $files)
56
    {
57 8
        $minifyPath = $this->view->minifyPath;
58 8
        $hash = $this->_getSummaryFilesHash($files);
59
60 8
        $resultFile = $minifyPath . DIRECTORY_SEPARATOR . $hash . '.css';
61
62 8
        if (!file_exists($resultFile)) {
63 8
            $css = '';
64
65 8
            foreach ($files as $file => $html) {
66 8
                $cacheKey = $this->buildCacheKey($file);
67
68 8
                $content = $this->getFromCache($cacheKey);
69
70 8
                if (false !== $content) {
71
                    $css .= $content;
72
                    continue;
73
                }
74
75 8
                $path = dirname($file);
76 8
                $file = $this->getAbsoluteFilePath($file);
77
78 8
                $content = '';
79
80 8
                if (!file_exists($file)) {
81
                    \Yii::warning(sprintf('Asset file not found `%s`', $file), __METHOD__);
82 8
                } elseif (!is_readable($file)) {
83
                    \Yii::warning(sprintf('Asset file not readable `%s`', $file), __METHOD__);
84
                } else {
85 8
                    $content = file_get_contents($file);
86
                }
87
88 8
                $result = [];
89
90 8
                preg_match_all('|url\(([^)]+)\)|i', $content, $m);
91
92 8
                if (isset($m[0])) {
93 8
                    foreach ((array)$m[0] as $k => $v) {
94 6
                        if (in_array(strpos($m[1][$k], 'data:'), [0, 1], true)) {
95 6
                            continue;
96
                        }
97
98 6
                        $url = str_replace(['\'', '"'], '', $m[1][$k]);
99
100 6
                        if ($this->isUrl($url)) {
101 6
                            $result[$m[1][$k]] = $url;
102 6
                        } else {
103 6
                            $result[$m[1][$k]] = $path . '/' . $url;
104
                        }
105 8
                    }
106
107 8
                    $content = strtr($content, $result);
108 8
                }
109
110 8
                $this->expandImports($content);
111
112 8
                $this->convertMediaTypeAttributeToMediaQuery($html, $content);
113
114 8
                if ($this->view->minifyCss) {
115 8
                    $content = \Minify_CSSmin::minify($content);
116 8
                }
117
118 8
                $this->saveToCache($cacheKey, $content);
119
120 8
                $css .= $content;
121 8
            }
122
123 8
            $charsets = $this->collectCharsets($css);
124 8
            $imports = $this->collectImports($css);
125 8
            $fonts = $this->collectFonts($css);
126
127 8
            if (false !== $this->view->forceCharset) {
128 7
                $charsets = '@charset "' . (string)$this->view->forceCharset . '";' . "\n";
129 7
            }
130
131 8
            file_put_contents($resultFile, $charsets . $imports . $fonts . $css);
132
133 8
            if (false !== $this->view->fileMode) {
134 8
                @chmod($resultFile, $this->view->fileMode);
135 8
            }
136 8
        }
137
138 8
        $file = $this->prepareResultFile($resultFile);
139
140 8
        $this->view->cssFiles[$file] = Html::cssFile($file);
141 8
    }
142
143
    /**
144
     * @param string $code
145
     */
146 8
    protected function expandImports(&$code)
147
    {
148 8
        if (true !== $this->view->expandImports) {
149 1
            return;
150
        }
151
152 7
        preg_match_all('|\@import\s([^;]+);|i', str_replace('&amp;', '&', $code), $m);
153
154 7
        if (!isset($m[0])) {
155
            return;
156
        }
157
158 7
        foreach ((array)$m[0] as $k => $v) {
159 5
            $importUrl = $m[1][$k];
160
161 5
            if (empty($importUrl)) {
162
                continue;
163
            }
164
165 5
            $importContent = $this->_getImportContent($importUrl);
166
167 5
            if (null === $importContent) {
168
                continue;
169
            }
170
171 5
            $code = str_replace($m[0][$k], $importContent, $code);
172 7
        }
173 7
    }
174
175
    /**
176
     * @param string $code
177
     * @return string
178
     */
179 8
    protected function collectCharsets(&$code)
180
    {
181
        return $this->_collect($code, '|\@charset[^;]+|is', function ($string) {
182
            return $string . ';';
183 8
        });
184
    }
185
186
    /**
187
     * @param string $code
188
     * @return string
189
     */
190 8
    protected function collectImports(&$code)
191
    {
192
        return $this->_collect($code, '|\@import[^;]+|is', function ($string) {
193 1
            return $string . ';';
194 8
        });
195
    }
196
197
    /**
198
     * @param string $code
199
     * @return string
200
     */
201
    protected function collectFonts(&$code)
202
    {
203 8
        return $this->_collect($code, '|\@font-face\{[^}]+\}|is', function ($string) {
204 5
            return $string;
205 8
        });
206
    }
207
208
    /**
209
     * @param string $code
210
     * @param string $pattern
211
     * @param callable $handler
212
     * @return string
213
     */
214 8
    protected function _collect(&$code, $pattern, $handler)
215
    {
216 8
        $result = '';
217
218 8
        preg_match_all($pattern, $code, $m);
219
220 8
        foreach ((array)$m[0] as $string) {
221 6
            $string = $handler($string);
222 6
            $code = str_replace($string, '', $code);
223
224 6
            $result .= $string . PHP_EOL;
225 8
        }
226
227 8
        return $result;
228
    }
229
230
    /**
231
     * @param string|null $url
232
     * @return null|string
233
     */
234 5
    protected function _getImportContent($url)
235
    {
236 5
        if (null === $url || '' === $url) {
237
            return null;
238
        }
239
240 5
        if (0 !== mb_strpos($url, 'url(')) {
241
            return null;
242
        }
243
244 5
        $currentUrl = str_replace(['url(\'', 'url("', 'url(', '\')', '")', ')'], '', $url);
245
246 5
        if (0 === mb_strpos($currentUrl, '//')) {
247 5
            $currentUrl = preg_replace('|^//|', 'http://', $currentUrl, 1);
248 5
        }
249
250 5
        if (null === $currentUrl || '' === $currentUrl) {
251
            return null;
252
        }
253
254 5
        if (!in_array(mb_substr($currentUrl, 0, 4), ['http', 'ftp:'], true)) {
255
            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
256 5
            $currentUrl = $this->view->basePath . $currentUrl;
257 5
        }
258
259 5
        if (false === $currentUrl) {
260
            return null;
261
        }
262
263 5
        return $this->_fetchImportFileContent($currentUrl);
264
    }
265
266
    /**
267
     * @param string $url
268
     * @return bool|string
269
     */
270 5
    protected function _fetchImportFileContent($url)
271
    {
272
        $context = [
273
            'ssl' => [
274 5
                'verify_peer'      => false,
275 5
                'verify_peer_name' => false,
276 5
            ],
277 5
        ];
278
279 5
        return file_get_contents($url, null, stream_context_create($context));
280
    }
281
282
    /**
283
     * If the <link> tag has a media="type" attribute, wrap the content in an equivalent media query
284
     * @param string $html HTML of the link tag
285
     * @param string $content CSS content
286
     * @return string $content CSS content wrapped with media query, if applicable
287
     */
288 8
    protected function convertMediaTypeAttributeToMediaQuery($html, &$content)
289
    {
290 8
        if (preg_match('/\bmedia=(["\'])([^"\']+)\1/i', $html, $m) && isset($m[2]) && $m[2] !== 'all') {
291 1
            $content = '@media ' . $m[2] . ' {' . $content . '}';
292 1
        }
293 8
    }
294
}
295