CSS   B
last analyzed

Complexity

Total Complexity 43

Size/Duplication

Total Lines 278
Duplicated Lines 6.83 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 85.71%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 4
dl 19
loc 278
ccs 138
cts 161
cp 0.8571
rs 8.3157
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
C export() 19 34 7
D process() 0 104 18
A removeCssComments() 0 15 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

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://processfast.com
6
 */
7
8
namespace processfast\yii\minify\components;
9
10
use yii\helpers\Html;
11
12
/**
13
 * Class CSS
14
 * @package processfast\yii\minify\components
15
 */
16
class CSS extends MinifyComponent
17
{
18
19 8
    public function export()
20
    {
21 8
        $cssFiles = $this->view->cssFiles;
22
23 8
        $this->view->cssFiles = [];
24
25 8
        $toMinify = [];
26
27 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...
28 7
            foreach ($cssFiles as $file => $html) {
29 7
                if ($this->thisFileNeedMinify($file, $html)) {
30 7
                    if ($this->view->concatCss) {
31 7
                        $toMinify[$file] = $html;
32 7
                    } else {
33
                        $this->process([$file => $html]);
34
                    }
35 7
                } else {
36 2
                    if (!empty($toMinify)) {
37 2
                        $this->process($toMinify);
38
39 2
                        $toMinify = [];
40 2
                    }
41
42 2
                    $this->view->cssFiles[$file] = $html;
43
                }
44 7
            }
45 7
        }
46
47 8
        if (!empty($toMinify)) {
48 5
            $this->process($toMinify);
49 5
        }
50
51 8
        unset($toMinify);
52 8
    }
53
54
    /**
55
     * @param array $files
56
     */
57 7
    protected function process(array $files)
58
    {
59 7
        $hash = $this->_getSummaryFilesHash($files) ;
60 7
        $resultFile = $this->view->minifyPath . DIRECTORY_SEPARATOR . $hash . '.css';
61
62 7
        if(  $this->view->S3Upload && $this->doesObjectExist( $resultFile , "CSS" , $hash ) )
63 7
        {
64
            // It exist on s3 just do not do any processing
65
            $resultFile = $this->getS3Path( $resultFile , "CSS" , $hash );
66
        }
67 7
        else if (!file_exists($resultFile))
68 7
        {
69 7
            $css = '';
70
71 7
            foreach ($files as $file => $html) {
72 7
                $path = dirname($file);
73
74 7
                if( $this->view->S3Upload )
75 7
                {
76
                    $assetsFolderPathPatch = $this->view->assetsFolderPathPatch ;
77
                    $pathArray = explode('assets', $path, 2);
78
                    $newPath = $pathArray[1] ;
79
                    $path = $assetsFolderPathPatch."assets".$newPath ;
80
                }
81
82 7
                $file = $this->getAbsoluteFilePath($file);
83
84 7
                $content = '';
85
86 7
                if (!file_exists($file)) {
87
                    \Yii::warning(sprintf('Asset file not found `%s`', $file), __METHOD__);
88 7
                } elseif (!is_readable($file)) {
89
                    \Yii::warning(sprintf('Asset file not readable `%s`', $file), __METHOD__);
90
                } else {
91 7
                    $content = file_get_contents($file);
92
                }
93
94 7
                $result = [];
95
96 7
                preg_match_all('|url\(([^)]+)\)|is', $content, $m);
97 7
                if (!empty($m[0])) {
98 7
                    foreach ($m[0] as $k => $v) {
99 7
                        if (in_array(strpos($m[1][$k], 'data:'), [0, 1], true)) {
100 7
                            continue;
101
                        }
102
103 7
                        $url = str_replace(['\'', '"'], '', $m[1][$k]);
104
105 7
                        if ($this->isUrl($url)) {
106 7
                            $result[$m[1][$k]] = $url;
107 7
                        } else {
108 7
                            $result[$m[1][$k]] = $path . '/' . $url;
109
                        }
110 7
                    }
111
112 7
                    $content = strtr($content, $result);
113 7
                }
114
115 7
                $css .= $content;
116 7
            }
117
118 7
            $this->expandImports($css);
119
120 7
            $this->removeCssComments($css);
121
122 7
            if ($this->view->minifyCss) {
123 7
                $css = (new \CSSmin())
124 7
                    ->run($css, $this->view->cssLinebreakPos);
125 7
            }
126
127 7
            $charsets = false !== $this->view->forceCharset
128 7
                ? ('@charset "' . (string)$this->view->forceCharset . '";' . "\n")
129 7
                : $this->collectCharsets($css);
130
131 7
            $imports = $this->collectImports($css);
132 7
            $fonts = $this->collectFonts($css);
133
134 7
            $content = $charsets . $imports . $fonts . $css ;
135 7
            if( $this->view->gzipEncodeCss ){
136
                $content = gzencode( $content ,  9);
137
            }
138 7
            file_put_contents($resultFile, $content );
139
140 7
            if (false !== $this->view->fileMode) {
141 7
                @chmod($resultFile, $this->view->fileMode);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
142 7
            }
143
144 7
            if( $this->view->S3Upload )
145 7
            {
146
                $resultFile = $this->uploadToS3( $resultFile , "CSS" , $hash );
147
            }
148 7
        }
149
        else
150
        {
151
            if( $this->view->S3Upload )
152
            {
153
                $resultFile = $this->uploadToS3( $resultFile , "CSS" , $hash );
154
            }
155
        }
156
157 7
        $file = $this->prepareResultFile($resultFile);
158
159 7
        $this->view->cssFiles[$file] = Html::cssFile($file);
160 7
    }
161
162
    /**
163
     * @param string $code
164
     */
165 7
    protected function removeCssComments(&$code)
166
    {
167 7
        if (true === $this->view->removeComments) {
168
169
            //https://stackoverflow.com/questions/1581049/preg-replace-out-css-comments
170
            $regex = array(
171 7
                "`^([\t\s]+)`ism"=>'',
172 7
                "`^\/\*(.+?)\*\/`ism"=>"",
173 7
                "`([\n\A;]+)\/\*(.+?)\*\/`ism"=>"$1",
174 7
                "`([\n\A;\s]+)//(.+?)[\n\r]`ism"=>"$1\n",
175
                "`(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+`ism"=>"\n"
176 7
            );
177 7
            $code = preg_replace(array_keys($regex),$regex,$code);
178 7
        }
179 7
    }
180
181
    /**
182
     * @param string $code
183
     */
184 7
    protected function expandImports(&$code)
185
    {
186 7
        if (true === $this->view->expandImports) {
187 7
            preg_match_all('|\@import\s([^;]+);|is', str_replace('&amp;', '&', $code), $m);
188
189 7
            if (!empty($m[0])) {
190 7
                foreach ($m[0] as $k => $v) {
191 7
                    $import_url = $m[1][$k];
192
193 7
                    if (!empty($import_url)) {
194 7
                        $import_content = $this->_getImportContent($import_url);
195
196 7
                        if (!empty($import_content)) {
197 7
                            $code = str_replace($m[0][$k], $import_content, $code);
198 7
                        }
199 7
                    }
200 7
                }
201 7
            }
202 7
        }
203 7
    }
204
205
    /**
206
     * @param string $code
207
     * @return string
208
     */
209
    protected function collectCharsets(&$code)
210
    {
211
        return $this->_collect($code, '|\@charset[^;]+|is', function ($string) {
212
            return $string . ';';
213
        });
214
    }
215
216
    /**
217
     * @param string $code
218
     * @return string
219
     */
220 7
    protected function collectImports(&$code)
221
    {
222
        return $this->_collect($code, '|\@import[^;]+|is', function ($string) {
223
            return $string . ';';
224 7
        });
225
    }
226
227
    /**
228
     * @param string $code
229
     * @return string
230
     */
231
    protected function collectFonts(&$code)
232
    {
233 7
        return $this->_collect($code, '|\@font-face\{[^}]+\}|is', function ($string) {
234 7
            return $string;
235 7
        });
236
    }
237
238
    /**
239
     * @param string $code
240
     * @param string $pattern
241
     * @param callable $handler
242
     * @return string
243
     */
244 7
    protected function _collect(&$code, $pattern, $handler)
245
    {
246 7
        $result = '';
247
248 7
        preg_match_all($pattern, $code, $m);
249
250 7
        foreach ($m[0] as $string) {
251 7
            $string = $handler($string);
252 7
            $code = str_replace($string, '', $code);
253
254 7
            $result .= $string . PHP_EOL;
255 7
        }
256
257 7
        return $result;
258
    }
259
260
    /**
261
     * @param string $url
262
     * @return null|string
263
     */
264 7
    protected function _getImportContent($url)
265
    {
266 7
        $result = null;
267
268 7
        if ('url(' === mb_substr($url, 0, 4)) {
269 7
            $url = str_replace(['url(\'', 'url("', 'url(', '\')', '")', ')'], '', $url);
270
271 7
            if (mb_substr($url, 0, 2) === '//') {
272 7
                $url = preg_replace('|^//|', 'http://', $url, 1);
273 7
            }
274
275 7
            if (!empty($url)) {
276 7
                if (!in_array(mb_substr($url, 0, 4), ['http', 'ftp:'], true)) {
277 7
                    $url = \Yii::getAlias($this->view->basePath . $url);
278 7
                }
279
280
                $context = [
281
                    'ssl' => [
282 7
                        'verify_peer' => false,
283 7
                        'verify_peer_name' => false,
284 7
                    ],
285 7
                ];
286
287 7
                $result = file_get_contents($url, null, stream_context_create($context));
288 7
            }
289 7
        }
290
291 7
        return $result;
292
    }
293
}
294