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

CSS::convertMediaTypeAttributeToMediaQuery()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 2
crap 3
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