18 votos

Minify/comprimir CSS con regex?

En PHP se puede comprimir/minify CSS con regex (PCRE)?

(Como un teórico en regex. Estoy seguro de que hay bibliotecas, hay que hacerlo bien.)

Nota de antecedentes: Después de pasar horas escribiendo una respuesta a una eliminados (la mitad de la basura) pregunta, yo pensé que había puesto una parte de la base de la pregunta y de la respuesta a mi auto. Espero que sea ok.

30voto

Qtax Puntos 20487

Simple regex CSS minifier/compresor

(Ok, puede que no sea demasiado simple, pero bastante directa).

Requisitos

Esta respuesta supone que los requisitos son:

  • Quitar los comentarios
  • Sustituir los espacios en blanco combinaciones de más de 1 espacio con un solo espacio
  • Eliminar todos los espacios en blanco alrededor de la meta caracteres: {, }, ;, ,, >, ~, +, -
  • Quitar los espacios en torno !important
  • Quitar los espacios en torno :, excepto en los selectores (donde usted tiene que mantener un espacio antes)
  • Quitar los espacios alrededor de los operadores como $=
  • Quitar todos los espacios a la derecha de la (/[ y a la izquierda de )/]
  • Quitar todos los espacios al principio y al final de la cadena
  • Quitar el último ; en un bloque
  • No cambia nada en las cadenas de
  • No tiene que trabajar en no válido CSS

Tenga en cuenta que los requisitos aquí no incluyen la conversión de las propiedades CSS a versiones más cortas (como el uso de la taquigrafía propiedades en lugar de varios de longitud completa de propiedades, la eliminación de comillas, donde no se requiere). Esto es algo que regex no sería capaz de resolver en general.

Solución

Es más fácil resolver esta en dos fases: en primer lugar, quite los comentarios, luego todo lo demás.

Debe ser posible hacer en un solo paso, pero entonces usted tiene que reemplazar todos los \s con una expresión que coincide con los espacios y los comentarios (entre algunas otras modificaciones).

El primer paso expresión para eliminar los comentarios:

(?xs)
  # quotes
  (
    "(?:[^"\\]++|\\.)*+"
  | '(?:[^'\\]++|\\.)*+'
  )
|
  # comments
  /\* (?> .*? \*/ )

Reemplazar con $1.

Y para quitar todo lo demás que usted puede utilizar:

(?six)
  # quotes
  (
    "(?:[^"\\]++|\\.)*+"
  | '(?:[^'\\]++|\\.)*+'
  )
|
  # ; before } (and the spaces after it while we're here)
  \s*+ ; \s*+ ( } ) \s*+
|
  # all spaces around meta chars/operators
  \s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
|
  # spaces right of ( [ :
  ( [[(:] ) \s++
|
  # spaces left of ) ]
  \s++ ( [])] )
|
  # spaces left (and right) of :
  \s++ ( : ) \s*+
  # but not in selectors: not followed by a {
  (?!
    (?>
      [^{}"']++
    | "(?:[^"\\]++|\\.)*+"
    | '(?:[^'\\]++|\\.)*+' 
    )*+
    {
  )
|
  # spaces at beginning/end of string
  ^ \s++ | \s++ \z
|
  # double spaces to single
  (\s)\s+

Reemplazado con $1$2$3$4$5$6$7.

El selector de verificación para la eliminación de los espacios antes de : (el negativo de oteo) puede disminuir el este hacia abajo en comparación con adecuado de los analizadores. Los analizadores ya saben, si están en un selector o no, y no tienes que hacer extra búsquedas para comprobar que.

Ejemplo de aplicación en PHP

function minify_css($str){
    # remove comments first (simplifies the other regex)
    $re1 = <<<'EOS'
(?sx)
  # quotes
  (
    "(?:[^"\\]++|\\.)*+"
  | '(?:[^'\\]++|\\.)*+'
  )
|
  # comments
  /\* (?> .*? \*/ )
EOS;

    $re2 = <<<'EOS'
(?six)
  # quotes
  (
    "(?:[^"\\]++|\\.)*+"
  | '(?:[^'\\]++|\\.)*+'
  )
|
  # ; before } (and the spaces after it while we're here)
  \s*+ ; \s*+ ( } ) \s*+
|
  # all spaces around meta chars/operators
  \s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
|
  # spaces right of ( [ :
  ( [[(:] ) \s++
|
  # spaces left of ) ]
  \s++ ( [])] )
|
  # spaces left (and right) of :
  \s++ ( : ) \s*+
  # but not in selectors: not followed by a {
  (?!
    (?>
      [^{}"']++
    | "(?:[^"\\]++|\\.)*+"
    | '(?:[^'\\]++|\\.)*+' 
    )*+
    {
  )
|
  # spaces at beginning/end of string
  ^ \s++ | \s++ \z
|
  # double spaces to single
  (\s)\s+
EOS;

    $str = preg_replace("%$re1%", '$1', $str);
    return preg_replace("%$re2%", '$1$2$3$4$5$6$7', $str);
}

Prueba rápida

Se puede encontrar en ideone.com:

$in = <<<'EOS'

p * i ,  html   
/* remove spaces */

/* " comments have no escapes \*/
body/* keep */ /* space */p,
p  [ remove ~= " spaces  " ]  :nth-child( 3 + 2n )  >  b span   i  ,   div::after

{
  /* comment */
    background :  url(  "  /* string */  " )   blue  !important ;
        content  :  " escapes \" allowed \\" ;
      width: calc( 100% - 3em + 5px ) ;
  margin-top : 0;
  margin-bottom : 0;
  margin-left : 10px;
  margin-right : 10px;
}

EOS;


$out = minify_css($in);

echo "input:\n";
var_dump($in);

echo "\n\n";
echo "output:\n";
var_dump($out);

Salida:

input:
string(435) "
p * i ,  html   
/* remove spaces */

/* " comments have no escapes \*/
body/* keep */ /* space */p,
p  [ remove ~= " spaces  " ]  :nth-child( 3 + 2n )  >  b span   i  ,   div::after

{
  /* comment */
    background :  url(  "  /* string */  " )   blue  !important ;
    content  :  " escapes \" allowed \\" ;
      width: calc( 100% - 3em + 5px ) ;
  margin-top : 0;
  margin-bottom : 0;
  margin-left : 10px;
  margin-right : 10px;
}
"


output:
string(251) "p * i,html body p,p [remove~=" spaces  "] :nth-child(3+2n)>b span i,div::after{background:url("  /* string */  ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}"

En comparación con

cssminifier.com

Resultados de cssminifier.com para la misma entrada como en la prueba anterior:

p * i,html /*\*/body/**/p,p [remove ~= " spaces  "] :nth-child(3+2n)>b span i,div::after{background:url("  /* string */  ") blue;content:" escapes \" allowed \\";width:calc(100% - 3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}

Longitud 263 byte. 12 byte más que el resultado de la expresión regular minifier arriba.

cssminifier.com tiene algunas desventajas con respecto a esta expresión minifier:

  • Deja partes de comentarios. (Puede haber una razón para ello. Tal vez algunos hacks de CSS.)
  • No quitar los espacios alrededor de los operadores en algunas expresiones

CSSTidy

Salida de CSSTidy 1.3 (a través de codebeautifier.com) en el más alto nivel de compresión de preset:

p * i,html /* remove spaces */
/* " comments have no escapes \*/
body/* keep */ /* space */p,p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i,div::after{background:url("  /* string */  ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin:0 10px;}

La longitud de 286 byte. 35 byte más que el resultado de la expresión regular minifier.

CSSTidy no eliminar comentarios o espacios en algunos selectores. Pero minify a la taquigrafía propiedades. Este último probablemente debería ayudar a comprimir normal CSS mucho más.

De lado a lado la comparación

Record de salida de los diferentes minifiers para la misma entrada como en el ejemplo anterior. (Las sobras de los saltos de línea se reemplazan con espacios.)

this answern    (251): p * i,html body p,p [remove~=" spaces  "] :nth-child(3+2n)>b span i,div::after{background:url("  /* string */  ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}
cssminifier.com (263): p * i,html /*\*/body/**/p,p [remove ~= " spaces  "] :nth-child(3+2n)>b span i,div::after{background:url("  /* string */  ") blue!important;content:" escapes \" allowed \\";width:calc(100% - 3em+5px);margin-top:0;margin-bottom:0;margin-left:10px;margin-right:10px}
CSSTidy 1.3     (286): p * i,html /* remove spaces */ /* " comments have no escapes \*/ body/* keep */ /* space */p,p [ remove ~= " spaces " ] :nth-child( 3 + 2n ) > b span i,div::after{background:url("  /* string */  ") blue!important;content:" escapes \" allowed \\";width:calc(100%-3em+5px);margin:0 10px;}

Para el normal CSS CSSTidy es probablemente el mejor, ya que convierte a la taquigrafía propiedades.

Supongo que hay otros minifiers (como el de YUI compressor) que debe ser mejor en esto, y dar más corto resultado de esta expresión minifier.

-1voto

B.F. Puntos 128

Aquí es un compacto de origen de ¿cómo lo hago. Con la compresión. Y usted no tiene cuidado, si ha cambiado algo en la fuente.

De hecho, la '//comentarios " no están permitidos en el css.

ob_start('ob_handler');
if(!file_exists('style/style-min.css) 
or filemtime('style/style.css') > filemtime('style/style-min.css')){
  $css=file_get_contents('style/style.css');    
  //you need to escape some more charactes if pattern is an external string.
  $from=array('@\\s*/\\*.*\\*/\\s*@sU', '/\\s{2,}/');
  $to=  array(''                      , ' ');
  $css=preg_replace($from,$to,$css); 
  $css=preg_replace('@\s*([\:;,."\'{}()])\s*@',"$1",$css);  
  $css=preg_replace('@;}@','}',$css);
  header('Content-type: text/css');
  echo $css;
  file_put_contents('style/style-min.css',$css);
  //etag- modified- cache-control- header
  }
else{
  //exit if not modified?
  //etag- modified- cache-control- header
  header('Content-type: text/css');
  readfile('style/style-min.css');
  }   
ob_end_flush();

PS Que me dio el signo menos antes de que yo estoy dispuesto a escribir? QTax - Por un corto tiempo me olvidé de escapar de las barras en el $fom matriz. PSS. Sólo la nueva versión de PHP se entienda la 'U' param que hace un regex ungreedy.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by: