Same name in other branches
  1. 7.x-2.x advagg_css_compress/yui/CSSMin.inc \CSSmin::run()
  2. 8.x-3.x advagg_css_minify/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);
}