<?php
lessSorter($argv[1] ?? 'php://stdin');
function lessSorter($filename)
{
$f = fopen($filename, 'rb');
$stack = [];
$current = (object) ['type' => 'root', 'content' => []];
$buffer = '';
$dqOpen = false;
$sqOpen = false;
$counter = 1000000;
while (!feof($f)) {
$input = fread($f, 20480);
while ($input) {
$pos = [
'open' => strpos($input, '{'),
'close' => strpos($input, '}'),
'end' => strpos($input, ';'),
'sq' => strpos($input, "'"),
'dq' => strpos($input, '"'),
];
$pos = array_filter($pos, 'is_numeric');
if (!$pos) {
$buffer .= $input;
$input = false;
continue;
}
asort($pos);
$firstPos = reset($pos);
switch (key($pos)) {
case 'open':
$row = $buffer . substr($input, 0, $firstPos);
$buffer = '';
$input = substr($input, $firstPos + 1);
if ($current) {
$stack[] = $current;
}
$current = (object) [
'type' => 'block',
'key' => trim($row),
'content' => [],
];
break;
case 'close':
$row = trim($buffer . substr($input, 0, $firstPos));
$buffer = '';
$input = substr($input, $firstPos + 1);
if (!$current || !$stack) {
throw new \RuntimeException('css closed non-open block');
}
if ($row) {
$sortValue = trim(strstr($row, ':', true)) . '-' . $counter++;
$current->content[$sortValue] = $row . ';';
}
asort($current->content);
$old = $current;
$current = array_pop($stack);
$prefix = in_array($old->key[0], ['#', '.']) ? '' : '*';
$current->content[$prefix . $old->key . '-' . $counter++] = $old->key . ' {' . PHP_EOL .
"\t" . trim (str_replace("\n", "\n\t", implode(PHP_EOL, $old->content))) . PHP_EOL .
'}' . PHP_EOL;
break;
case 'end':
$row = $buffer . substr($input, 0, $firstPos + 1);
$buffer = '';
$input = substr($input, $firstPos + 1);
if (!$current || $current->type === 'root') {
throw new \RuntimeException('css outside block: ' . $row);
}
$sortValue = '~' . trim(strstr($row, ':', true)) . '-' . $counter++;
$current->content[$sortValue] = trim($row);
break;
case 'sq':
$buffer .= substr($input, 0, $firstPos + 1);
$input = substr($input, $firstPos + 1);
$sqOpen = !$sqOpen;
break;
case 'dq':
$buffer .= substr($input, 0, $firstPos + 1);
$input = substr($input, $firstPos + 1);
$dqOpen = !$dqOpen;
break;
default:
die('unknown key');
}
}
}
fclose($f);
if (trim($buffer)) {
throw new \RuntimeException('Unexpected end of file: ' . $buffer);
}
if (!$current || $stack || $current->type !== 'root') {
throw new \RuntimeException('Wrong number of blocks closed. stack size: ' . count($stack));
}
asort($current->content);
echo implode(PHP_EOL, $current->content);
}
Comments