Test Setup Failed
Push — master ( 8d73bd...2022ae )
by Revin
01:56
created

CSS   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 255
Duplicated Lines 7.45 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 94.96%

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 4
dl 19
loc 255
ccs 132
cts 139
cp 0.9496
rs 8.2608
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
C export() 19 34 7
C process() 0 74 12
A removeCssComments() 0 6 2
B expandImports() 0 20 6
A collectCharsets() 0 6 1
A collectImports() 0 6 1
A collectFonts() 0 6 1
A _collect() 0 15 2
B _getImportContent() 0 29 5
A convertMediaTypeAttributeToMediaQuery() 0 9 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like CSS often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CSS, and based on these observations, apply Extract Interface, too.

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 tubalmartin\CssMin\Minifier as CSSmin;
11
use yii\helpers\Html;
12
13
/**
14
 * Class CSS
15
 * @package rmrevin\yii\minify\components
16
 */
17
class CSS extends MinifyComponent
18
{
19
20 8
    public function export()
21
    {
22 8
        $cssFiles = $this->view->cssFiles;
23
24 8
        $this->view->cssFiles = [];
25
26 8
        $toMinify = [];
27
28 8 View Code Duplication
        if (!empty($cssFiles)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
29 7
            foreach ($cssFiles as $file => $html) {
30 7
                if ($this->thisFileNeedMinify($file, $html)) {
31 7
                    if ($this->view->concatCss) {
32 7
                        $toMinify[$file] = $html;
33 7
                    } else {
34
                        $this->process([$file => $html]);
35
                    }
36 7
                } else {
37 2
                    if (!empty($toMinify)) {
38 2
                        $this->process($toMinify);
39
40 2
                        $toMinify = [];
41 2
                    }
42
43 2
                    $this->view->cssFiles[$file] = $html;
44
                }
45 7
            }
46 7
        }
47
48 8
        if (!empty($toMinify)) {
49 5
            $this->process($toMinify);
50 5
        }
51
52 8
        unset($toMinify);
53 8
    }
54
55
    /**
56
     * @param array $files
57
     */
58 7
    protected function process(array $files)
59
    {
60 7
        $resultFile = $this->view->minifyPath . DIRECTORY_SEPARATOR . $this->_getSummaryFilesHash($files) . '.css';
61
62 7
        if (!file_exists($resultFile)) {
63 7
            $css = '';
64
65 7
            foreach ($files as $file => $html) {
66 7
                $path = dirname($file);
67 7
                $file = $this->getAbsoluteFilePath($file);
68
69 7
                $content = '';
70
71 7
                if (!file_exists($file)) {
72
                    \Yii::warning(sprintf('Asset file not found `%s`', $file), __METHOD__);
73 7
                } elseif (!is_readable($file)) {
74
                    \Yii::warning(sprintf('Asset file not readable `%s`', $file), __METHOD__);
75 1
                } else {
76 7
                    $content = file_get_contents($file);
77
                }
78
79 7
                $result = [];
80
81 7
                preg_match_all('|url\(([^)]+)\)|is', $content, $m);
82 7
                if (!empty($m[0])) {
83 7
                    foreach ($m[0] as $k => $v) {
84 7
                        if (in_array(strpos($m[1][$k], 'data:'), [0, 1], true)) {
85 7
                            continue;
86
                        }
87
88 7
                        $url = str_replace(['\'', '"'], '', $m[1][$k]);
89
90 7
                        if ($this->isUrl($url)) {
91 7
                            $result[$m[1][$k]] = $url;
92 7
                        } else {
93 7
                            $result[$m[1][$k]] = $path . '/' . $url;
94
                        }
95 7
                    }
96
97 7
                    $content = strtr($content, $result);
98 7
                }
99
                
100 7
                $content = self::convertMediaTypeAttributeToMediaQuery($html, $content);
101
102 7
                $css .= $content;
103 7
            }
104
105 7
            $this->expandImports($css);
106
107 7
            $this->removeCssComments($css);
108
109 7
            if ($this->view->minifyCss) {
110 7
                $css = (new CSSmin())
111 7
                    ->run($css, $this->view->cssLinebreakPos);
112 7
            }
113
114 7
            $charsets = false !== $this->view->forceCharset
115 7
                ? ('@charset "' . (string)$this->view->forceCharset . '";' . "\n")
116 7
                : $this->collectCharsets($css);
117
118 7
            $imports = $this->collectImports($css);
119 7
            $fonts = $this->collectFonts($css);
120
121 7
            file_put_contents($resultFile, $charsets . $imports . $fonts . $css);
122
123 7
            if (false !== $this->view->fileMode) {
124 7
                @chmod($resultFile, $this->view->fileMode);
125 7
            }
126 7
        }
127
128 7
        $file = $this->prepareResultFile($resultFile);
129
130 7
        $this->view->cssFiles[$file] = Html::cssFile($file);
131 7
    }
132
133
    /**
134
     * @param string $code
135
     */
136 7
    protected function removeCssComments(&$code)
137
    {
138 7
        if (true === $this->view->removeComments) {
139 7
            $code = preg_replace('#/\*(?:[^*]*(?:\*(?!/))*)*\*/#', '', $code);
140 7
        }
141 7
    }
142
143
    /**
144
     * @param string $code
145
     */
146 7
    protected function expandImports(&$code)
147
    {
148 7
        if (true === $this->view->expandImports) {
149 7
            preg_match_all('|\@import\s([^;]+);|is', str_replace('&amp;', '&', $code), $m);
150
151 7
            if (!empty($m[0])) {
152 7
                foreach ($m[0] as $k => $v) {
153 7
                    $import_url = $m[1][$k];
154
155 7
                    if (!empty($import_url)) {
156 7
                        $import_content = $this->_getImportContent($import_url);
157
158 7
                        if (!empty($import_content)) {
159 7
                            $code = str_replace($m[0][$k], $import_content, $code);
160 7
                        }
161 7
                    }
162 7
                }
163 7
            }
164 7
        }
165 7
    }
166
167
    /**
168
     * @param string $code
169
     * @return string
170
     */
171
    protected function collectCharsets(&$code)
172
    {
173
        return $this->_collect($code, '|\@charset[^;]+|is', function ($string) {
174
            return $string . ';';
175
        });
176
    }
177
178
    /**
179
     * @param string $code
180
     * @return string
181
     */
182 7
    protected function collectImports(&$code)
183
    {
184
        return $this->_collect($code, '|\@import[^;]+|is', function ($string) {
185
            return $string . ';';
186 7
        });
187
    }
188
189
    /**
190
     * @param string $code
191
     * @return string
192
     */
193
    protected function collectFonts(&$code)
194
    {
195 7
        return $this->_collect($code, '|\@font-face\{[^}]+\}|is', function ($string) {
196 7
            return $string;
197 7
        });
198
    }
199
200
    /**
201
     * @param string $code
202
     * @param string $pattern
203
     * @param callable $handler
204
     * @return string
205
     */
206 7
    protected function _collect(&$code, $pattern, $handler)
207
    {
208 7
        $result = '';
209
210 7
        preg_match_all($pattern, $code, $m);
211
212 7
        foreach ($m[0] as $string) {
213 7
            $string = $handler($string);
214 7
            $code = str_replace($string, '', $code);
215
216 7
            $result .= $string . PHP_EOL;
217 7
        }
218
219 7
        return $result;
220
    }
221
222
    /**
223
     * @param string $url
224
     * @return null|string
225
     */
226 7
    protected function _getImportContent($url)
227
    {
228 7
        $result = null;
229
230 7
        if ('url(' === mb_substr($url, 0, 4)) {
231 7
            $url = str_replace(['url(\'', 'url("', 'url(', '\')', '")', ')'], '', $url);
232
233 7
            if (mb_substr($url, 0, 2) === '//') {
234 7
                $url = preg_replace('|^//|', 'http://', $url, 1);
235 7
            }
236
237 7
            if (!empty($url)) {
238 7
                if (!in_array(mb_substr($url, 0, 4), ['http', 'ftp:'], true)) {
239 7
                    $url = \Yii::getAlias($this->view->basePath . $url);
240 7
                }
241
242
                $context = [
243
                    'ssl' => [
244 7
                        'verify_peer'      => false,
245 7
                        'verify_peer_name' => false,
246 7
                    ],
247 7
                ];
248
249 7
                $result = file_get_contents($url, null, stream_context_create($context));
250 7
            }
251 7
        }
252
253 7
        return $result;
254
    }
255
    
256
    /**
257
     * If the <link> tag has a media="type" attribute, wrap the content in an equivalent media query
258
     * @param string $tag_html HTML of the link tag
0 ignored issues
show
Bug introduced by
There is no parameter named $tag_html. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
259
     * @param string $content CSS content
260
     * @return string $content CSS content wrapped with media query, if applicable
261
     */
262 8
    public static function convertMediaTypeAttributeToMediaQuery($html, $content)
263
    {
264 8
        if (preg_match('/\bmedia=(["\'])([^"\']+)\1/i', $html, $m)) {
265 1
            if ($m[2] !== 'all') {
266 1
                $content = '@media '.$m[2].'{' . $content . '}';
267 1
            }
268 1
        }        
269 8
        return $content;
270
    }
271
}
272