CSS minifier

Gepost door Richard van Velzen op 25-05-2010 22:38.

Jawel, een simpele, kleine, regexgebaseerd (JA!) CSS-minifier.

Uitleg is nauwelijks nodig, de code spreekt voor zich: stop er een stuk CSS in en je krijgt een sterk verkleinde string terug.

Let op(1): het haalt alleen whitespace weg, attributen zoals "margin: 1px 0 1px 0" worden niet naar "margin:1px 0" verkleind. (sinds 22-03 wel, margin: 5px 0px; margin-top: 3px; zal hij wel niks mee doen)

Let op(2): in principe zou er niks fouten moeten gaan. Er is een kleine uitzondering: commentaar binnen een url(mijnurl.png) wordt ook weggehaald. Ja, dit is volgens de standaard toegestaan maar ik ben nog geen enkel geval tegengekomen waar dit zo was. Dus dit verander ik waarschijnlijk ook niet.

Dus, brand me maar af! :-)

Update (2010-03-07): preg_replace kan ook arrays als input krijgen ja, veel handiger...

Update (2010-03-22): behoorlijk uitgebreid met veelvoorkomende dingen die kleiner kunnen.

Bestanden van dit script

index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
<?php
/** 
 * Een helperfunctie voor PHP < 5.3.0 
 */ 
function minifyHelper($match) { 
    /** 
     * Alles moet lowercase, maakt verder niet uit maar is 
     * handig bij vergelijkingen :-) 
     */ 
    $entries = array_map( 
        'strtolower', 
        explode(' ', $match[0]) 
    ); 

    switch(count($entries)) { 
    case 4: 
        /** 5px 5px 5px 5px => 5px 5px 5px */ 
        if ($entries[3] === $entries[1]) { 
            // er zijn nog 3 over, doorvallen naar die case 
            array_pop($entries); 
        } else { 
            break; 
        } 
        // DOORVALLEN 
    case 3: 
        /** 5px 5px 5px => 5px 5px */ 
        if ($entries[2] === $entries[0]) { 
            // nu dus nog 2 over... 
            array_pop($entries); 
        } else { 
            break; 
        } 
        // DOORVALLEN 
    case 2: 
        /** 5px 5px => 5px */ 
        if ($entries[1] === $entries[0]) { 
            array_pop($entries); 
        } 
    } 

    return implode(' ', $entries); 
} 

/** 
 * CSS optimaliseren voor laag bandbreedtegebruik 
 * 
 * @param string $input 
 * @return string 
 * @author Richard van Velzen <rvanvelzen@expert-shops.com> 
 */ 
function minifyCSS($input, $newlineAfterBrace = false) { 
    $result = trim(preg_replace_callback( 
        '~(?xi) 
            (?<!\b(?: # uitzonderingen... bah 
                background-position 
            )) 
            :\K 
            (?:(?:\d*\.)?\d+ (?: [a-z]{2} | %)? | auto ) 
            (?: \s (?:\d*\.)?\d+ (?: [a-z]{2} | %)? | auto ) {0,3} 
            (?= [;}] ) 
        ~S', 
        'minifyHelper', 
        preg_replace( 
            array( 
                '~(?x) 
                    \s* 
                    (?: 
                        # debug-CSS weghalen 
                        \Q/* REMOVE */\E 
                        (?: 
                          [\r\n]+ 
                          (?! \Q/* END REMOVE */\E ) 
                          .* 
                        )+ 
                        [\r\n]+ \Q/* END REMOVE */\E 
                    | 
                        # commentaar, natuurlijk 
                        /\* [^*]* \*+ (?: [^/] [^*]* \*+ )* / 
                    ) 
                    \s* 
                ~', 
                '~(?x) 
                    # nietrelevante whitespace 
                    \s+ (?! [^\'"()\r\n]* \)) 
                ~', 
                '@(?x) 
                    (?<=[{};:,+>~]) \s+ 
                    | \s+ (?=[{}:,+>~;]) 
                    | ( \( [^)]* \) | " [^"]* " | \' [^\']* \' ) 
                    # een ; kan natuurlijk in een string voorkomen.. 
                    | ; (?: \s*; )* (?= \s* } ) 
                @', 
                '~#(?ix) 
                    # hex-kleuren, normaliseren naar kort formaat 
                    ([a-f\d])\1 
                    ([a-f\d])\2 
                    ([a-f\d])\3 
                ~', 
                '~(?x) 
                    # 0/0.0(px|em|etc.)? normaliseren naar 0 
                    (?<=[:, ]) (?:0*\.)?0 
                    (?i: [a-z]{2} | % )? 
                    (?! \w ) 
                ~', 
                '@(?xm) 
                    (?<= ^ | } ) 
                    (?: 
                        # een selector met lege regels 
                        (?: ^ | \s* (?: , \s*)? ) 
                        [>+~ ]? 
                        (?: [.#]? [a-z_-]+ | \* 
                            | \[[^]"\']* 
                                (?: 
                                    (?: "[^"]*" | \'[^\']*\' ) 
                                    [^]"\']* 
                                )* 
                            ] 
                        )++ 
                    )++ 
                    \s*{}\s* 
                @' 
            ), 
            array( 
                // commentaar 
                ' ', 
                // whitespace 
                ' ', 
                // whitespace weghalen 
                '$1', 
                // hex-verkleining 
                '#$1$2$3', 
                // 0-normalisatie 
                '0', 
                // lege declaratie 
                '' 
            ), 
            trim($input) 
        ) 
    )); 

    if ($newlineAfterBrace) { 
        $result = trim(str_replace('}', "}\n", $result)); 
    } 

    return $result; 
}

/**
 * Voorbeeld
 *
 * Ja, niet zeuren dat er niks van klopt! :)
 *
 * Update: extra voorbeelden
 */
echo minifyCSS('
body    div  .class #id [attr],
        body [attr] .class     div
            #id {
    margin: 0px 5px 2px 5px; /* alleen de laatste 5px mag weg */
    margin: 3px 3px 3px 3px; /* gelijke set */
    margin: 0% 0 0px 0em; /* verschillende notaties van 0, moet 0 worden */
    background: url(blaat;); /* ; in een url, de laatste ; moet wel weg */
}

body , html {
    margin: 0px;
    padding: 0;
    background-color :  #000;
}

   body  + a * > html {
      color: navy ;
}
');

/*
 Uit het voorbeeld zou moeten komen:

body div .class #id [attr],body [attr] .class div #id{margin:0 5px 2px;margin:3px;margin:0;background:url(blaat;)}body,html{margin:0;padding:0;background-color:#000}body+a *>html{color:navy}
*/
?>

Commentaar

04-03-2010 10:11

ik had er al eentje in gebruik op een server, maar zoals gebruikelijk weet Richard mijn 5 of meer regels in 2 regexen te proppen.

Overigens draai ik het script server sided als rewritemap (aanroep via .htaccess), zodat op de development server ook steeds de css als style.minified.css wordt weggeschreven.

ps: ik zoek er nog eentje voor javascript files.

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
04-03-2010 10:18

Je hebt geluk, ik ben ook bezig met eentje voor JavaScript. :-)

Maar je kunt ook kijken naar JSMin+ van Tweakers: http://crisp.tweakblogs.net/blog/1856/jsmin+-version-13.html

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
04-03-2010 10:51

http://code.google.com/p/minify/
Ik gebruik deze, te minsten ik heb dingen hieruit over genomen :)

Voor mijn framework maak ik gebruik van een combinatie van Minify en CSSTidy (helaas niet langer door ontwikkeld).

1
N.V.T.
04-03-2010 15:41

Netjes, korter dan die ik heb. Ik heb/had 5 expressies nodig.

Zal het eea testen morgen :)

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
07-03-2010 18:27

>> ps: ik zoek er nog eentje voor javascript files.

http://crisp.tweakblogs.net/blog/1856/jsmin+-version-13.html :-)

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
07-03-2010 18:31

Arien: volgens mij gaf ik al precies hetzelfde antwoord? :-)

Overigens vind ik het doel van hun project een beetje jammer, het ging ze voornamelijk om dingen als ; weg mogen laten, terwijl dat juist een groot minpunt van de taal is.

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
22-03-2010 11:34

Ziet er wel aardig uit. Helaas iets minder uitgebreid dan: http://csstidy.sourceforge.net/

1
2
3
4
5
6
7
8
<?php
try {
    throw new Ball('Voetbal');
}
catch(Ball $e) {
    echo 'You caught the ball';
}
?>
22-03-2010 11:45

Alle dingen die zij nog extra doen komen bij wat ik doe nauwelijks ooit voor en zijn het echt niet waard om nog bij te optimaliseren.

Daarnaast is dit een enkele functie die heel makkelijk in mijn deploymentproces past. :-)

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
22-03-2010 21:10

www.cleancss.com

1
2
3
Post hier de source-code van je script. Alle informatie tussen <? ... ?> en <?php ... ?> zal automatisch worden getoond in color-coding. 

Let op! Het is niet de bedoeling om hier een link naar je website te plaatsen. Post hier gewoon de code, veel simpeler, sneller en meer kans dat het blijft staan.
26-05-2010 19:47

www.cleancss.com

Helemaal onderaan na al die reclame.
(based on csstidy 1.1)

Het voordeel van dit zelf doen is dat je CSS duidelijk en onderhoudbaar blijft en pas bij het aanbieden klaar word gemaakt voor snelheid.

1
N.V.T.
12-10-2010 09:26

Ooit had ik eens onderstaande code gevonden om CSS te comprimeren. Heb er een kleine aanpassing in gedaan zodat alle css bestanden uit een folder werden gelezen zodat ik zelf vrij weinig eraan hoefde te doen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?php

    ob_start('ob_gzhandler');
    ob_start('cssCompress');
    
    header('Content-type: text/css;charset: UTF-8');
    header('Cache-Control: must-revalidate');
    header('Expires: ' . gmdate('D, d M Y H:i:s',time() + 2419200 ) . ' GMT');
    
    function cssCompress ( $outputBuffer ) 
    {
    
        $outputBuffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $outputBuffer);
        $outputBuffer = str_replace(array("\r\n", "\r", "\n", "\t", '  ', '    ', '    '), '', $outputBuffer);
        
        $outputBuffer = str_replace('{ ', '{', $outputBuffer);
        $outputBuffer = str_replace(' }', '}', $outputBuffer);
        $outputBuffer = str_replace('; ', ';', $outputBuffer);
        $outputBuffer = str_replace(', ', ',', $outputBuffer);
        $outputBuffer = str_replace(' {', '{', $outputBuffer);
        $outputBuffer = str_replace('} ', '}', $outputBuffer);
        $outputBuffer = str_replace(': ', ':', $outputBuffer);
        $outputBuffer = str_replace(' ,', ',', $outputBuffer);
        $outputBuffer = str_replace(' ;', ';', $outputBuffer);
        
        return $outputBuffer;
        
    }
            
    $fileHandler    = opendir('../styles/');
    $fileArray        = array();
    
    while ( false !== ( $file = readdir ( $fileHandler ) ) )
    {
    
        if ( ( $file != '.' ) and ( $file != '..' ) and ( $file != 'Thumbs.db' ) )
        {
        
            $fileInfo = pathinfo($file);
            
            if ( ( isset ( $fileInfo['extension'] ) ) and ( strtolower($fileInfo['extension']) == 'css' ) )
            {
            
                $fileArray[] = $file;
            
            }
            
        }
        
    }
    
    sort($fileArray);
    closedir($fileHandler);
    
    foreach ( $fileArray as $key => $value )
    {
    
        $fileStream = file_get_contents ( $value );
        
        echo $fileStream;
        
    }
    
?>
Inloggen wachtwoord vergeten? Aanmelden
ADSL – Telfort Internet – Ziggo Alles in 1