Existen casos en los que se desea o necesita minimizar las hojas de javascript en tiempo de ejecución. En este artículo analizaremos una función para tal fin, escrita en PHP y expresiones regulares.
Antes de comenzar, recomendamos el artículo anterior que nos brinda una visión más amplia del tema.
La función esta basada en la minimización que propone Douglas Crockford, y consta de tres partes. En la primera, se extraen los comentarios y se ponen a salvo las cadenas y expresiones regulares reemplazándolas por tokens seguros. El código queda entonces listo para que en la segunada parte se eliminen los espacios y nuevas líneas innecesarios.
Por último se restituyen las cadenas y expresiones regulares.
public function minify_js($input)
{
// strip BOM
if (substr($input, 0, 3) == "\xef\xbb\xbf") {
$input = substr($input, 3);
}
// vars
$output = str_replace(["\r\n", "\r"], "\n", $input);
$replaces = [];
$i = 0;
$pattern = '#'.
// replace string with key
'(["\'`])(?:\\\\.|[^\\\\])*?\1' .'|'.
// replace /* ... */ with white-spaces
'(?s:\/\*.*?\*\/)' .'|'.
// replace regex with key
'(?<=[=:.\(&!|,])(?:\s*)(\/[^*\/](?:\\\\.|[^\\\\])*?\/[gimuy]*)' .'|'.
// replace // ... with \n
'\/\/.*?([\n]|$)'
. '#';
$output = preg_replace_callback($pattern, function ($match) use (&$i, &$replaces) {
if (isset($match[3])) {
// ...
return "\n";
} elseif (isset($match[2])) {
// regex
$k = '"'. (++$i) .'"';
$replaces[$k] = $match[2];
return $k;
} elseif (isset($match[1])) {
// quotes
if ($match[0] == $match[1] . $match[1]) {
return $match[0];
}
$k = $match[1] . (++$i) . $match[1];
$replaces[$k] = $match[0];
return $k;
} else {
/* ... */
return ' ';
}
}, $output);
if (null === $output) {
throw new Exception('The minifier has failed.');
}
$output = preg_replace([
// It replaces all other control characters (including tab) with spaces
'#[^\n \S]+#',
// the multiple spaces and linefeeds are removed.
'#\s*\n+\s*#',
'# {2,}#',
// It omits spaces except when a space is preceded and followed
// by a non-ASCII character or by an ASCII letter or digit,
// or by one of these characters: \ $ _
'# (?![\w\\\\$]|[^\x20-\x7F])#',
'#(?<![\w\\\\$]|[^\x20-\x7F]) #',
// A linefeed is not omitted if it precedes a non-ASCII character or an ASCII
// letter or digit or one of these characters: \ $ _ { [ ( + -
'#\n(?![\w\\\\${[(+-]|[^\x20-\x7F])#',
// and if it follows a non-ASCII character or an ASCII letter or digit or one
// of these characters: \ $ _ } ] ) + - " '
'#(?<![\w\\\\$}\])+\-"\']|[^\x20-\x7F])\n#',
],[
' ',
"\n",
' ',
'',
], $output);
if (null === $output) {
throw new Exception('The minifier has failed.');
}
return strtr($output, $replaces);
}
A modo de conclusión, podemos observar que en la segunda parte de la función, es posible agregar otros filtros de minimización o incluso realizar compresiones de código.