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
Class
- Files
- Provides AdvAgg with a file status state system using a key value store.
Namespace
Drupal\advagg\StateCode
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,
];
}
}
}