You are here

public function CSSmin::run in Advanced CSS/JS Aggregation 8.2

Same name and namespace in other branches
  1. 8.3 advagg_css_minify/yui/CSSMin.inc \CSSmin::run()
  2. 6 advagg_css_compress/yui/CSSMin.inc \CSSmin::run()
  3. 7.2 advagg_css_compress/yui/CSSMin.inc \CSSmin::run()

Minify a string of CSS

Parameters

string $css:

int|bool $linebreak_pos:

Return value

string

File

advagg_css_minify/yui/CSSMin.inc, line 74

Class

CSSmin

Code

public function run($css = '', $linebreak_pos = FALSE) {
  if (empty($css)) {
    return '';
  }
  if ($this->raise_php_limits) {
    $this
      ->do_raise_php_limits();
  }
  $this->comments = array();
  $this->preserved_tokens = array();
  $start_index = 0;
  $length = strlen($css);
  $css = $this
    ->extract_data_urls($css);

  // collect all comment blocks...
  while (($start_index = $this
    ->index_of($css, '/*', $start_index)) >= 0) {
    $end_index = $this
      ->index_of($css, '*/', $start_index + 2);
    if ($end_index < 0) {
      $end_index = $length;
    }
    $comment_found = $this
      ->str_slice($css, $start_index + 2, $end_index);
    $this->comments[] = $comment_found;
    $comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
    $css = $this
      ->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this
      ->str_slice($css, $end_index);

    // Set correct start_index: Fixes issue #2528130
    $start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
  }

  // preserve strings so their content doesn't get accidentally minified
  $css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|' . "(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array(
    $this,
    'replace_string',
  ), $css);

  // Let's divide css code in chunks of 5.000 chars aprox.
  // Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
  // of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
  // long strings and a (sub)pattern matches a number of chars greater than
  // the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
  // returning NULL and $css would be empty.
  $charset = '';
  $charset_regexp = '/(@charset)( [^;]+;)/i';
  $css_chunks = array();
  $css_chunk_length = 5000;

  // aprox size, not exact
  $start_index = 0;
  $i = $css_chunk_length;

  // save initial iterations
  $l = strlen($css);

  // if the number of characters is 5000 or less, do not chunk
  if ($l <= $css_chunk_length) {
    $css_chunks[] = $css;
  }
  else {

    // chunk css code securely
    while ($i < $l) {
      $i += 50;

      // save iterations
      if ($l - $start_index <= $css_chunk_length || $i >= $l) {
        $css_chunks[] = $this
          ->str_slice($css, $start_index);
        break;
      }
      if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {

        // If there are two ending curly braces }} separated or not by spaces,
        // join them in the same chunk (i.e. @media blocks)
        $next_chunk = substr($css, $i);
        if (preg_match('/^\\s*\\}/', $next_chunk)) {
          $i = $i + $this
            ->index_of($next_chunk, '}') + 1;
        }
        $css_chunks[] = $this
          ->str_slice($css, $start_index, $i);
        $start_index = $i;
      }
    }
  }

  // Minify each chunk
  for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
    $css_chunks[$i] = $this
      ->minify($css_chunks[$i], $linebreak_pos);

    // Keep the first @charset at-rule found
    if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
      $charset = strtolower($matches[1]) . $matches[2];
    }

    // Delete all @charset at-rules
    $css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
  }

  // Update the first chunk and push the charset to the top of the file.
  $css_chunks[0] = $charset . $css_chunks[0];
  return implode('', $css_chunks);
}