Created
July 23, 2012 16:27
-
-
Save StanAngeloff/3164569 to your computer and use it in GitHub Desktop.
Draft PHP script to join @media queries in a CSS file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# [..] | |
require 'shellwords' | |
on_stylesheet_saved do |filename| | |
script = File.dirname(__FILE__) + '/_scripts/join-media-queries.php' | |
system('php -q ' + Shellwords.escape(script) + ' -i ' + Shellwords.escape(filename)) | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
# Prepare the environment, be strict about silly mistakes. | |
error_reporting(E_ALL | E_STRICT); | |
ini_set('display_errors', 'On'); | |
# Try and reset the memory limit imposed on CLI scripts. | |
ini_set('memory_limit', '64M'); | |
# Handy function to format and print a string to STDERR and exit with a level. | |
function halt($message, $level, array $options = NULL) { | |
file_put_contents('php://stderr', strtr($message, (array) $options) . PHP_EOL); | |
exit(0 << $level); | |
} | |
# Start by making sure a file was passed as an argument to this script. | |
if (empty ($_SERVER['argc']) || $_SERVER['argc'] <= 1) { | |
halt(<<<EOD | |
Usage: php -q :self [OPTION]... FILE | |
-i[SUFFIX], --in-place[=SUFFIX] | |
Edit files in place (makes backup if extension supplied) | |
EOD | |
, 1, array( | |
':self' => escapeshellarg($_SERVER['argv'][0]), | |
)); | |
} | |
# Parse script options, e.g., '-i' for in-place updates. | |
$options = getopt('i::', array('in-place::')); | |
# The file always comes last. | |
$source = $_SERVER['argv'][$_SERVER['argc'] - 1]; | |
$input = NULL; | |
# Accept input from STDIN. | |
if ($source === '-') { | |
$resource = fopen('php://stdin', 'rb'); | |
while ( ! feof($resource)) { | |
$input = $input . fgets($resource, 4096); | |
} | |
fclose($resource); | |
} else { | |
# The source is expected to be a file... | |
if ( ! is_file($source)) { | |
halt("[E] ':source' is not a file.", 2, array( | |
':source' => $source, | |
)); | |
} | |
# ...which exists on disk... | |
if ( ! file_exists($source)) { | |
halt("[E] ':source' does not exist.", 3, array( | |
':source' => $source, | |
)); | |
} | |
# ...and the User has sufficient permissions to access it for reading. | |
if ( ! is_readable($source)) { | |
halt("[E] ':source' cannot be read, permission denied.", 4, array( | |
':source' => $source, | |
)); | |
} | |
# Read the entire file in one go. This could take a lot of memory for larger | |
# buffers, but should be OK for the average sized CSS file. | |
$input = file_get_contents($source); | |
} | |
# Recursively descend and match all `{ .. }` blocks. | |
preg_match_all('#(?<media>(?:@media[^{]+)?){(?:(?:[^{}]+)|(?R))*}#s', $input, $captures); | |
# Loop through all matches by key. | |
foreach (array_keys($captures[0]) as $key) { | |
# If the value does not contain a @media query, drop it from the array. Or | |
# even if we found a @media query, make sure it was not within a string or | |
# a comment. | |
if (strpos($captures[0][$key], '@media') === FALSE || empty ($captures['media'][$key])) { | |
unset ($captures[0][$key]); | |
} | |
} | |
# Erase all @media queries from the input CSS. | |
foreach ($captures[0] as $query) { | |
$input = preg_replace('#\s*' . preg_quote($query, '#') . '#u', '', $input); | |
} | |
# Utility function to create an array key from a media query. | |
function sanitize_media_query_for_key($query) { | |
$key = trim($query); | |
$key = preg_replace('#[^\w\d]+#u', '-', $key); | |
$key = trim($key, '-'); | |
return $key; | |
} | |
# For each @media query, group CSS code together. | |
$groups = array(); | |
foreach (array_keys($captures[0]) as $key) { | |
$media = $captures['media'][$key]; | |
# The code for this block starts off with the @media query in it... | |
$code = $captures[0][$key]; | |
# ...which then gets stripped off... | |
$code = preg_replace('#' . preg_quote($media, '#') . '#u', '', $code); | |
# ...and any leading or trailing curly bracket also gets removed. | |
$code = preg_replace('#^{|}$#u', '', $code); | |
# If this is the first time the @media query is encountered, a new group is | |
# created. | |
$group_key = sanitize_media_query_for_key($media); | |
array_key_exists($group_key, $groups) OR ($groups[$group_key] = array($media, '{')); | |
$groups[$group_key][] = $code; | |
} | |
$result = NULL; | |
# Output is the input stripped of any @media queries... | |
$result = $result . rtrim($input); | |
# ...followed by those @media queries grouped together. | |
foreach (array_keys($groups) as $key) { | |
$result = $result . PHP_EOL . PHP_EOL . implode(NULL, $groups[$key]) . '}'; | |
} | |
# If --in-place was specified, print result to a file. | |
if (array_key_exists('i', $options) || array_key_exists('in-place', $options)) { | |
# If we read from STDIN, assume option value is the file name. | |
if ($source === '-') { | |
$source = NULL; | |
} | |
file_put_contents($source . (isset ($options['i']) ? $options['i'] : $options['in-place']), $result); | |
} else { | |
print $result; | |
} |
Nice one thank you!
How about sorting the media queries?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
On large files we get this PHP Error.
Warning: preg_replace(): Compilation failed: regular expression is too large at offset 41501 in PATH/sass/_scripts/join-media-queries.php on line 89
These parameter changed nothing.
...
// Try and reset the memory limit imposed on CLI scripts.
ini_set('memory_limit', '512M');
// essential for huge PCREs
ini_set("pcre.backtrack_limit", "23001337");
ini_set("pcre.recursion_limit", "23001337");
// imagine your PCRE here...
...
Do you have a solution for this?