Zrób to sam, z nudów układamy css’a

Z nudów człowiek robi dziwne rzeczy. Jedną z nich jest parser dla plików CSS. Początkowo miał być to tokenizer który pozwoliłby na tworzenie różnego rodzaju narzędzi operujących na stylach CSS. Niestety po zapoznaniu się dokładnie ze strukturą CSS w wersji 2.1 odkładam ten plan na bliżej nie określoną przyszłość. Ale na pewno napisze, bo może być to ciekawe doświadczenie.

Po co mi ten skrypt ? Bo czasami jak otrzymam od kogoś kawałek szablonu ze skryptami. To wygląda to jak pole po bitwie lub niekończące się tasiemce. Często w pracy grafik przysyła nam szablony pocięte do tego CSS gdzie każda klasa szablonu jest napisana w jednej linii, a do tego bardzo długiej. Ja preferuje zupełnie odmienny styl.

Na początek musimy przeanalizować plik CSS. Wykorzystamy do tego proste wyrażenia regularne, które rozpoznają nam poszczególne części. Musimy rozpoznać 3 podstawowe rzeczy: Nazwę klasy wraz z jej ‘ciałem’, następnie z ów ciała musimy wyciągnąć atrybuty i ich wartości. Trzecią i ostatnią rzeczą są komentarze, które z mojego punktu widzenia są w ogóle niepotrzebne więc w tej wersji są po prostu usuwane.

/* wyrazenie regularne do wyszukiwania klas */
$patern_main = '~([\.|#]?[^{]*)[\s]*\{([^}]*)\}~i';
/* wyrazenie regularne do wyszukiwania atrybutow w klasach */
$patern_css = '~([^\:\;\s]+)\s*:\s*([^\;\s]+)~i';
/* wyrazenie regularne do wyszukania i usuniecia komentarzy */
$patern_comments = '~\/\*[^*]*\*+([^/*][^*]*\*+)*\/~i';

Cała reszta jest już prosta. W pierwszej kolejności usuwamy wszystkie znaki nowej linii oraz tabulatory, a następnie pozbywamy się wszystkich komentarzy. czyli:

$css = file_get_contents('/sciezka/do/pliku.css');
$css = str_replace(array("\n", "\r", "\t"), '', $css );
/* usuwanie komentarzy */
$css = preg_replace($patern_comments, '', $css);

Tak obrobiony styl będziemy teraz przeszukiwać, korzystając z wyrażeń regularnych.

/* wyszukiwanie klas */
preg_match_all($patern_main, $css, $match);
 
$css_array = array();
foreach (array_keys($match[0]) as $key) {
	$css_match = array(); //czyscimy tablice w przypadku pustej klasy
	$css_body = trim( $match[2][ $key ] );
	if( $css_body != '' ) { //jesli definicja klasy jest pusta to ja opuszczamy
		preg_match_all($patern_css, $css_body, $css_match);
		$css_array[ trim( $match[1][ $key ] ) ] = array_combine($css_match[1], $css_match[2]);
	} else {
		$css_array[ trim( $match[1][ $key ] ) ] = array();
	}
}

W ten sposób mamy już „rozłożony” styl CSS na czynniki pierwsze. Można by się pokusić oczywiście o sprawdzanie poprawności poszczególnych elementów i ich wartości. Ale sądzę że jest to raczej czysta formalność stworzyć słownik atrybutów oraz ich dopuszczalnych wartości.
Drugim pomysłem może być powiedzmy analiza i wyszukanie powtarzających się klas lub też wielokrotne powtarzanie tych samych atrybutów wraz z wartościami w różnych klasach. Ale podejrzewam, że będzie to materiał na inny wpis.

Teraz może poukładamy wszystkie klasy na dwa sposoby, „tasiemiec” oraz „drzewko”. Żeby wyjaśnić różnice pokaże przykładzie.

body { background-color: white; margin: 0px; padding: 0px; }
/*lub*/
body { 
	background-color: white; 
	margin: 0px; 
	padding: 0px; 
}

Poniżej układamy CSS’a jeśli chcemy tasiemce zmienne $nl i $tab pozostawiamy puste lub też pozostawiamy tak jak teraz i mamy drzewka.

$nl = "\n";
$tab = "\t";
$string = '';
foreach( $css_array as $klasa => $body ) {
	$string .= $klasa.' {'.$nl;
	foreach( $body as $att => $value ) {
		$string .= $tab."$att: $value;".$nl;
	}
	$string .= "}\n";
}

Oczywiście kod ma parę niedociągnięć. Nie obsługuje takich tagów jak import, charset ale może to kiedy indziej.
Czy komuś się to przyda nie wiem, jak w tytule był robiony w chwili wolnego czasu. Będzie chociaż troche ładnie poukładane.

Kod poskładany w całość:

<?php
$file = '/sciezka/do/pliku.css';
$css = file_get_contents($file);
$css = str_replace(array("\n", "\r", "\t"), '', $css );
 
/* wyrazenie regularne do wyszukiwania klas */
$patern_main = '~([\.|#]?[^{]*)[\s]*\{([^}]*)\}~i';
/* wyrazenie regularne do wyszukiwania atrybutow w klasach */
$patern_css = '~([^\:\;\s]+)\s*:\s*([^\;\s]+)~i';
/* wyrazenie regularne do wyszukania i usuniecia komentarzy */
$patern_comments = '~\/\*[^*]*\*+([^/*][^*]*\*+)*\/~i';
 
/* usuwanie komentarzy */
$css = preg_replace($patern_comments, '', $css);
 
/* wyszukiwanie klas */
preg_match_all($patern_main, $css, $match);
 
$css_array = array();
foreach (array_keys($match[0]) as $key) {
	$css_match = array(); //czyscimy tablice w przypadku pustej klasy
	$css_body = trim( $match[2][ $key ] );
	if( $css_body != '' ) { //jesli definicja klasy jest pusta to ja opuszczamy
		preg_match_all($patern_css, $css_body, $css_match);
		$css_array[ trim( $match[1][ $key ] ) ] = array_combine($css_match[1], $css_match[2]);
	} else {
		$css_array[ trim( $match[1][ $key ] ) ] = array();
	}
}
$nl = "\n";
$tab = "\t";
$string = '';
foreach( $css_array as $klasa => $body ) {
	$string .= $klasa.' {'.$nl;
	foreach( $body as $att => $value ) {
		$string .= $tab."$att: $value;".$nl;
	}
	$string .= "}\n";
}
?>

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">