You are here

private function Files::splitCssFile in Advanced CSS/JS Aggregation 8.2

Given a file info array it will split the file up.

Parameters

array $file_info: File info array.

Return value

array Array with file and split data.

1 call to Files::splitCssFile()
Files::scanFile in src/State/Files.php
Given a filename calculate various hashes and gather meta data.

File

src/State/Files.php, line 316
Given a filename calculate various hashes and gather meta data.

Class

Files
Provides AdvAgg with a file status state system using a key value store.

Namespace

Drupal\advagg\State

Code

private function splitCssFile(array &$file_info) {

  // Get the CSS file and break up by media queries.
  if (!isset($file_info['contents'])) {
    $file_info['contents'] = file_get_contents($file_info['data']);
  }
  $media_blocks = $this
    ->parseMediaBlocks($file_info['contents']);

  // Get 98% of the css.ie.selector_limit; usually 4013.
  $selector_split_value = (int) max(floor($this->config
    ->get('css.ie.selector_limit') * 0.98), 100);
  $part_selector_count = 0;
  $major_chunks = [];
  $counter = 0;

  // Group media queries together.
  foreach ($media_blocks as $media_block) {

    // Get the number of selectors.
    // http://stackoverflow.com/a/12567381/125684
    $selector_count = preg_match_all('/\\{.+?\\}|,/s', $media_block);
    $part_selector_count += $selector_count;
    if ($part_selector_count > $selector_split_value) {
      if (isset($major_chunks[$counter])) {
        ++$counter;
        $major_chunks[$counter] = $media_block;
      }
      else {
        $major_chunks[$counter] = $media_block;
      }
      ++$counter;
      $part_selector_count = 0;
    }
    else {
      if (isset($major_chunks[$counter])) {
        $major_chunks[$counter] .= "\n" . $media_block;
      }
      else {
        $major_chunks[$counter] = $media_block;
      }
    }
  }
  $file_info['parts'] = [];
  $overall_split = 0;
  $split_at = $selector_split_value;
  $chunk_split_value = (int) $this->config
    ->get('css.ie.selector_limit') - $selector_split_value - 1;
  foreach ($major_chunks as $chunks) {

    // Get the number of selectors.
    $selector_count = preg_match_all('/\\{.+?\\}|,/s', $chunks);

    // Pass through if selector count is low.
    if ($selector_count < $selector_split_value) {
      $overall_split += $selector_count;
      $subfile = $this
        ->createSubfile($chunks, $overall_split, $file_info);
      if (!$subfile) {

        // Somthing broke; do not create a subfile.
        \Drupal::logger('advagg')
          ->notice('Spliting up a CSS file failed. File info: <code>@info</code>', [
          '@info' => var_export($file_info, TRUE),
        ]);
        return [];
      }
      $file_info['parts'][] = [
        'path' => $subfile,
        'selectors' => $selector_count,
      ];
      continue;
    }
    $media_query = '';
    if (strpos($chunks, '@media') !== FALSE) {
      $media_query_pos = strpos($chunks, '{');
      $media_query = substr($chunks, 0, $media_query_pos);
      $chunks = substr($chunks, $media_query_pos + 1);
    }

    // Split CSS into selector chunks.
    $split = preg_split('/(\\{.+?\\}|,)/si', $chunks, -1, PREG_SPLIT_DELIM_CAPTURE);

    // Setup and handle media queries.
    $new_css_chunk = [
      0 => '',
    ];
    $selector_chunk_counter = 0;
    $counter = 0;
    if (!empty($media_query)) {
      $new_css_chunk[0] = $media_query . '{';
      $new_css_chunk[1] = '';
      ++$selector_chunk_counter;
      ++$counter;
    }

    // Have the key value be the running selector count and put split array
    // semi back together.
    foreach ($split as $value) {
      $new_css_chunk[$counter] .= $value;
      if (strpos($value, '}') === FALSE) {
        ++$selector_chunk_counter;
      }
      else {
        if ($counter + 1 < $selector_chunk_counter) {
          $selector_chunk_counter += ($counter - $selector_chunk_counter + 1) / 2;
        }
        $counter = $selector_chunk_counter;
        if (!isset($new_css_chunk[$counter])) {
          $new_css_chunk[$counter] = '';
        }
      }
    }

    // Group selectors.
    while (!empty($new_css_chunk)) {

      // Find where to split the array.
      $string_to_write = '';
      while (array_key_exists($split_at, $new_css_chunk) === FALSE) {
        --$split_at;
      }

      // Combine parts of the css so that it can be saved to disk.
      foreach ($new_css_chunk as $key => $value) {
        if ($key !== $split_at) {

          // Move this css row to the $string_to_write variable.
          $string_to_write .= $value;
          unset($new_css_chunk[$key]);
        }
        else {

          // Get the number of selectors in this chunk.
          $chunk_selector_count = preg_match_all('/\\{.+?\\}|,/s', $new_css_chunk[$key]);
          if ($chunk_selector_count < $chunk_split_value) {

            // The number of selectors at this point is below the threshold;
            // move this chunk to the write var and break out of the loop.
            $string_to_write .= $value;
            unset($new_css_chunk[$key]);
            $overall_split = $split_at;
            $split_at += $selector_split_value;
          }
          else {

            // The number of selectors with this chunk included is over the
            // threshold; do not move it. Change split position so the next
            // iteration of the while loop ends at the correct spot. Because
            // we skip unset here, this chunk will start the next part file.
            $overall_split = $split_at;
            $split_at += $selector_split_value - $chunk_selector_count;
          }
          break;
        }
      }

      // Handle media queries.
      if (!empty($media_query)) {

        // See if brackets need a new line.
        if (strpos($string_to_write, "\n") === 0) {
          $open_bracket = '{';
        }
        else {
          $open_bracket = "{\n";
        }
        if (strrpos($string_to_write, "\n") === strlen($string_to_write)) {
          $close_bracket = '}';
        }
        else {
          $close_bracket = "\n}";
        }

        // Fix syntax around media queries.
        if ($first) {
          $string_to_write .= $close_bracket;
        }
        elseif (empty($new_css_chunk)) {
          $string_to_write = $media_query . $open_bracket . $string_to_write;
        }
        else {
          $string_to_write = $media_query . $open_bracket . $string_to_write . $close_bracket;
        }
      }

      // Write the data.
      $subfile = $this
        ->createSubfile($string_to_write, $overall_split, $file_info);
      if (!$subfile) {

        // Somthing broke; did not create a subfile.
        \Drupal::logger('advagg')
          ->notice('Spliting up a CSS file failed. File info: <code>@info</code>', [
          '@info' => var_export($file_info, TRUE),
        ]);
        return [];
      }
      $sub_selector_count = preg_match_all('/\\{.+?\\}|,/s', $string_to_write, $matches);
      $file_info['parts'][] = [
        'path' => $subfile,
        'selectors' => $sub_selector_count,
      ];
    }
  }
}