Completed
Push — master ( d14c1f...3b84d0 )
by Vitaly
03:05
created

CSSMin::_commentCB()   C

Complexity

Conditions 11
Paths 14

Size

Total Lines 52
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 52
rs 5.9999
cc 11
eloc 26
nc 14
nop 1

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
namespace samsonphp\minify;
3
4
/**
5
 * Compress CSS
6
 *
7
 * This is a heavy regex-based removal of whitespace, unnecessary
8
 * comments and tokens, and some CSS value minimization, where practical.
9
 * Many steps have been taken to avoid breaking comment-based hacks, 
10
 * including the ie5/mac filter (and its inversion), but expect tricky
11
 * hacks involving comment tokens in 'content' value strings to break
12
 * minimization badly. A test suite is available.
13
 * 
14
 * @package Minify
15
 * @author Stephen Clay <[email protected]>
16
 * @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
17
 */
18
class CSSMin {
19
20
    /**
21
     * @var array options
22
     */
23
    protected $_options = null;
24
    /**
25
     * @var bool Are we "in" a hack?
26
     *
27
     * I.e. are some browsers targetted until the next comment?
28
     */
29
    protected $_inHack = FALSE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected false, but found FALSE.
Loading history...
30
    
31
    /**
32
     * Constructor
33
     *
34
     * @param array $options (currently ignored)
35
     *
36
     * @return null
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
37
     */
38
    private function __construct($options)
39
    {
40
        $this->_options = $options;
41
    }
42
    
43
    /**
44
     * Minify a CSS string
45
     *
46
     * @param string $css
47
     *
48
     * @param array $options (currently ignored)
49
     *
50
     * @return string
51
     */
52
    public static function process($css, $options = array())
53
    {
54
        $obj = new CSSMin($options);
55
        return $obj->_process($css);
56
    }
57
    
58
    /**
59
     * Minify a CSS string
60
     * 
61
     * @param string $css
62
     * 
63
     * @return string
64
     */
65
    protected function _process($css)
66
    {
67
        $css = str_replace("\r\n", "\n", $css);
68
        
69
        // preserve empty comment after '>'
70
        // http://www.webdevout.net/css-hacks#in_css-selectors
71
        $css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
72
        
73
        // preserve empty comment between property and value
74
        // http://css-discuss.incutio.com/?page=BoxModelHack
75
        $css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
76
        $css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
77
        
78
        // apply callback to all valid comments (and strip out surrounding ws
79
        $css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
80
            ,array($this, '_commentCB'), $css);
0 ignored issues
show
Coding Style introduced by
Space found before comma in function call
Loading history...
81
82
        // remove ws around { } and last semicolon in declaration block
83
        $css = preg_replace('/\\s*{\\s*/', '{', $css);
84
        $css = preg_replace('/;?\\s*}\\s*/', '}', $css);
85
        
86
        // remove ws surrounding semicolons
87
        $css = preg_replace('/\\s*;\\s*/', ';', $css);
88
        
89
        // remove ws around urls
90
        $css = preg_replace('/
91
                url\\(      # url(
92
                \\s*
93
                ([^\\)]+?)  # 1 = the URL (really just a bunch of non right parenthesis)
94
                \\s*
95
                \\)         # )
96
            /x', 'url($1)', $css);
97
        
98
        // remove ws between rules and colons
99
        $css = preg_replace('/
100
                \\s*
101
                ([{;])              # 1 = beginning of block or rule separator 
102
                \\s*
103
                ([\\*_]?[\\w\\-]+)  # 2 = property (and maybe IE filter)
104
                \\s*
105
                :
106
                \\s*
107
                (\\b|[#\'"-])        # 3 = first character of a value
108
            /x', '$1$2:$3', $css);
109
        
110
        // remove ws in selectors
111
        $css = preg_replace_callback('/
112
                (?:              # non-capture
113
                    \\s*
114
                    [^~>+,\\s]+  # selector part
115
                    \\s*
116
                    [,>+~]       # combinators
117
                )+
118
                \\s*
119
                [^~>+,\\s]+      # selector part
120
                {                # open declaration block
121
            /x'
122
            ,array($this, '_selectorsCB'), $css);
0 ignored issues
show
Coding Style introduced by
Space found before comma in function call
Loading history...
123
        
124
        // minimize hex colors
125
        $css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
126
            , '$1#$2$3$4$5', $css);
0 ignored issues
show
Coding Style introduced by
Space found before comma in function call
Loading history...
127
        
128
        // remove spaces between font families
129
        $css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
130
            ,array($this, '_fontFamilyCB'), $css);
0 ignored issues
show
Coding Style introduced by
Space found before comma in function call
Loading history...
131
        
132
        $css = preg_replace('/@import\\s+url/', '@import url', $css);
133
        
134
        // replace any ws involving newlines with a single newline
135
        $css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
136
        
137
        // separate common descendent selectors w/ newlines (to limit line lengths)
138
        $css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
139
        
140
        // Use newline after 1st numeric value (to limit line lengths).
141
        $css = preg_replace('/
142
            ((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
143
            \\s+
144
            /x'
145
            ,"$1\n", $css);
0 ignored issues
show
Coding Style introduced by
Space found before comma in function call
Loading history...
146
        
147
        // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
148
        $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
149
            
150
        return trim($css);
151
    }
152
    
153
    /**
154
     * Replace what looks like a set of selectors  
155
     *
156
     * @param array $m regex matches
157
     * 
158
     * @return string
159
     */
160
    protected function _selectorsCB($m)
161
    {
162
        // remove ws around the combinators
163
        return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
164
    }
165
    
166
    /**
167
     * Process a comment and return a replacement
168
     * 
169
     * @param array $m regex matches
170
     * 
171
     * @return string
172
     */
173
    protected function _commentCB($m)
174
    {
175
        $hasSurroundingWs = (trim($m[0]) !== $m[1]);
176
        $m = $m[1]; 
177
        // $m is the comment content w/o the surrounding tokens, 
178
        // but the return value will replace the entire comment.
179
        if ($m === 'keep') {
180
            return '/**/';
181
        }
182
        if ($m === '" "') {
183
            // component of http://tantek.com/CSS/Examples/midpass.html
184
            return '/*" "*/';
185
        }
186
        if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
187
            // component of http://tantek.com/CSS/Examples/midpass.html
188
            return '/*";}}/* */';
189
        }
190
        if ($this->_inHack) {
191
            // inversion: feeding only to one browser
192
            if (preg_match('@
193
                    ^/               # comment started like /*/
194
                    \\s*
195
                    (\\S[\\s\\S]+?)  # has at least some non-ws content
196
                    \\s*
197
                    /\\*             # ends like /*/ or /**/
198
                @x', $m, $n)) {
199
                // end hack mode after this comment, but preserve the hack and comment content
200
                $this->_inHack = FALSE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected false, but found FALSE.
Loading history...
201
                return "/*/{$n[1]}/**/";
202
            }
203
        }
204
        if (substr($m, -1) === '\\') { // comment ends like \*/
205
            // begin hack mode and preserve hack
206
            $this->_inHack = TRUE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
207
            return '/*\\*/';
208
        }
209
        if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
210
            // begin hack mode and preserve hack
211
            $this->_inHack = TRUE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected true, but found TRUE.
Loading history...
212
            return '/*/*/';
213
        }
214
        if ($this->_inHack) {
215
            // a regular comment ends hack mode but should be preserved
216
            $this->_inHack = FALSE;
0 ignored issues
show
Coding Style introduced by
TRUE, FALSE and NULL must be lowercase; expected false, but found FALSE.
Loading history...
217
            return '/**/';
218
        }
219
        // Issue 107: if there's any surrounding whitespace, it may be important, so 
220
        // replace the comment with a single space
221
        return $hasSurroundingWs // remove all other comments
222
            ? ' '
223
            : '';
224
    }
225
    
226
    /**
227
     * Process a font-family listing and return a replacement
228
     * 
229
     * @param array $m regex matches
230
     * 
231
     * @return string   
232
     */
233
    protected function _fontFamilyCB($m)
234
    {
235
        $m[1] = preg_replace('/
236
                \\s*
237
                (
238
                    "[^"]+"      # 1 = family in double qutoes
239
                    |\'[^\']+\'  # or 1 = family in single quotes
240
                    |[\\w\\-]+   # or 1 = unquoted family
241
                )
242
                \\s*
243
            /x', '$1', $m[1]);
244
        return 'font-family:' . $m[1] . $m[2];
245
    }
246
}
247