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.