zurück zur Startseite
  


Zurück XHTMLforum > Webentwicklung (außer XHTML und CSS) > Serveradministration und serverseitige Scripte
Seite neu laden [PHP] Funktion zum Optimieren von CSS

Antwort
 
LinkBack Themen-Optionen Ansicht
  #1 (permalink)  
Alt 03.08.2005, 17:43
Benutzerbild von Floele
Erfahrener Benutzer
XHTMLforum-Mitglied
Thread-Ersteller
 
Registriert seit: 30.03.2005
Beiträge: 355
Floele befindet sich auf einem aufstrebenden Ast
Standard [PHP] Funktion zum Optimieren von CSS

Gleich eines vorweg: Wer sich den Beitrag komplett durchlesen will braucht ein bisschen Zeit

Ich arbeite im Moment wieder an meinem CSS Optimierer, und für die nächste Version würde ich gerne einen einzigsten Fehler beheben - leider ist das etwas komplizierter als es aussieht.

Beispiel:

Code:
b {color:red;}
c,b {color:green;}
a {color:red;}
Daraus wird anschließend

Code:
c,b {
color:green
}

b,a {
color:red
}
Offensichtlich stimmt das so nicht. Um diesen Fehler zu beheben müsste das Programm erstmal alle Selektoren zerlegen (am ",") und anschließend wieder richtig zusammenfassen (oder einfach die Option zum automatischen Zusammenfassen ausschalten, aber das ist nicht Sinn der Sache). Während das Zerlegen kein Problem darstellt, ist das richtige Zusammenfassen aber problematisch. Das Programm muss nämlich erstmal überprüfen wo Übereinstimmungen vorhanden und wie groß diese sind. Anhand dieser Informationen können die Eigenschaften dann sinnvoll zusammengefasst werden.

Dafür habe ich sogar auch schon eine Funktion geschrieben (die fast perfekt funktioniert), allerdings ist diese so aufwändig, dass damit schnell alle Zeit- und Speicherlimits überschritten werden (je nach Größe des Stylesheets). Und deswegen bitte ich hier um ein bisschen Hilfe, vielleicht hat ja jemand eine Idee wie man das sinnvoll machen kann (bzw. wie man meiner Funktion ein bisschen Feuer unterm Hintern macht).

Hier erstmal meine Funktion + Infos:

Struktur der Daten
Die Daten in denen das CSS gespeichert wird, sehen etwa so aus:
Code:
$css['standard']['a'][]['color'] = 'red';
$css['standard']['a'][]['size'] = '1em';
$css['standard']['b'][]['size'] = '1em';
// also $css[MEDIUM][SELEKTOR][ZAHL][EIGENSCHAFT] = WERT
Bisherhige Funktion
Das hier ist meine Funktion (das ultimative foreach-Chaos), hoffentlich gut genug kommentiert. Vorraussetzung ist, dass beim ersten Durchlauf keine Selektoren wie "a,b" oder "a,b#id,c" etc. vorhanden sind, bzw. diese zerlegt sind (dafür kann der Rest meines CSS Parsers garantieren).

Code:
function merge_selectors(&$css)
{
	$return = FALSE;
Das hier wird nacher auf TRUE gesetzt falls irgendetwas zusammengefasst wurde
Code:
	foreach($css as $medium => $vali)
	{
		$diff = array();
		$diff_c = array();
Zuerstmal geht die Funktion hier alle Medientypen durch und legt die Variablen $diff (Liste der Gemeinsamkeiten, $diff[Selektoren] => Größe der Gemeinsamkeit, wird aus der Zeichenlänge der gemeinsamen Eigenschaften und deren Werten bestimmt) und $diff_c an (speichert anstatt der Gemeinsamkeitsgröße die gemeinsamen Eigenschaften+Werte),
Code:
	
		foreach($vali as $selector => $valj)
		{
Anschließend geht sie alle Selektoren durch. Die Werte der Arrays werden immer in $val[Buchstable] gespeichert, je nach Verschachtelungstiefe.
Code:
			foreach($valj as $num_key => $valk)
			{
				foreach($valk as $property => $value)
				{
Danach folgen die Nummern der Eigenschaften und die Eigenschaften selbst.
Code:
					foreach($css[$medium] as $selector2 => $vall)
					{
Jetzt geht die Funktion innerhalb des gleichen Mediums die Selektoren durch (wieder von vorne anfangend), da wir ja alle Kombinationen durchlaufen müssen.
Code:
						$selector_combine_r = serialize(array($selector2,$selector));
So, jetzt wird's erstmal komisch. Da $diff bzw. $diff_c ja die Selektoren speichern, für die die Gemeinsamkeiten gelten kommen diese sinnvollerweise in den Schlüssel. Da es aber auch logischerweise zwei Selektoren sind, müssen diese so gespeichert werden, dass man sie nacher wieder mühelos trennen kann. Das wäre bei einem Array der Fall ( $dif[array('a','b')]). Das geht so natürlich nicht, also wird das array serialisiert. Damit überlasse ich PHP das Trennen der Selektoren und muss nicht irgendeine selbstgebaute Funktion die beiden Selektoren wieder trennen lassen (wir erinnern uns: So geht es nicht explode(',',$selektoren), da das "," auch als String in den Selektoren vorkommen könnte).

Hier wird also die serialisierte Form der Selektoren 1 und 2 in umgekehrter Reichenfolge gespeichert, womit wir...
Code:
						
						// Prevent duplicate comparisions (a-b, b-a) and self-comparisons (a-a)
						if($selector2 === $selector || isset($diff[$selector_combine_r]))
						{
							continue;	
						}
...hier die Schleife überspringen falls wir für die gleichen Selektoren (nur in umgekehrter Reihenfolge) schon nach Übereinstimmungen gesucht haben. Außerdem überspringen wir alle Vergleiche eines Selektors mit sich selbst.
Code:
						
						$selector_combine = serialize(array($selector,$selector2));
						if(!isset($diff[$selector_combine]))
						{
							$diff[$selector_combine] = 0;
							$diff_[$selector_combine] = array();
						}
Hier legen wir jetzt, falls noch nicht vorhanden, die Speicherplätze für die Größe der Gemeinsamkeiten und für die Gemeinsamkeiten selbst an (und zwar mit einem Schlüssel, der die beiden Selektoren die wir grade vergleichen enthält).
Code:
										
						foreach($vall as $num_key2 => $valm)
						{
							foreach($valm as $property2 => $value2)
							{
Hier geht die Funktion jetzt auch durch die Eigenschaften des 2. (Vergleichs-)Selektors.
Code:
								if($property2 === $property && $value2 === $value)
								{
									$diff[$selector_combine] += (strlen($property)+strlen($value));
									$diff_c[$selector_combine][][$property] = $value; 
								}
Wenn Eigenschaft und Wert zwischen beiden Selektoren übereinstimmt, speichern wir die Zeichenlänge dieser (damit wir nacher wissen, wo die Übereinstimmungen am größten sind, diese müssen dann zuerst zusammengefasst werden) sowie Eigenschaft+Wert selbst ab.
Code:
							}
						}
						
						if($diff[$selector_combine] === 0)
						{
							unset($diff[$selector_combine]);
							unset($diff_c[$selector_combine]);
						}
Um den Speicherverbrauch wenigstens ansatzweise in den Griff zu bekommen, löschen wir hier den Eintrag der Selektoren in $diff und $diff_ wenn es keine Übereinstimmungen hab.
Code:
					}
				}
			}
		}
So, wo sind wir hier? Genau, am Ende eines Mediums (alle Selektoren die nicht in einem @media drin sind, sind in 'standard'). Das heißt jetzt, nachdem wir wissen was zusammengefasst werden kann, fangen wir an zusammenzufassen. Das nachfolgende wird also für jeden Mediumtyp ausgeführt.
Code:
		// Move best matches to the top
		array_multisort($diff,SORT_NUMERIC,SORT_DESC);
Wie der Kommentar schon sagt werden die größten Übereinstimmungen nach oben gesetzt damit möglichst "intelligent" optimiert wird.
Code:
	
		foreach($diff as $key => $value)
		{
			$key_u = unserialize($key);
			foreach($key_u as $selector)
			{
				// If there are no matches or if selector is already dissolved
				if(!isset($diff_c[$key]) || !isset($css[$medium][$selector]))
				{
					continue;
				}
Jetzt geht die Funktion die Variable $diff mit den Unterschieden durch. Dabei wird auch jeder Selektor einzeln behandelt, da wir andere Kombinationen mit diesem Selektor aus $diff entfernen müssen. Wenn wir nämlich einen Selektor mit einem anderen kombinieren, kann es ja sein, dass andere Kombinationen die vorher gefunden wurden gar nicht mehr möglich sind.
Code:
				
				foreach($diff_c[$key] as $num_key => $valj)
				{
					foreach($valj as $property => $value)
					{
						rm_subkey($property,$css[$medium][$selector],$value);
					}
				}
Hier löschen wir jetzt alle Eigenschaften eines Selektors, die in Kürze mit einem anderen Selektor zusammengefasst werden. Wenn a{color:red;} also mit b{color:red;} kombiniert wird, muss color:red; in beiden Selektoren gelöscht werden. rm_subkey ist selbstgebaut und funktioniert wie unset($css[$medium][$selector][?][$property]);.
Code:
				
				// If no properties are left, remove selector
				if(empty($css[$medium][$selector]))
				{
					unset($css[$medium][$selector]);
				}
Und wenn dann wie im obigen Beispiel gar keine Eigenschaften in einem Selektor mehr drin sind, dann wird er komplett gelöscht.
Code:
				
				// Create the new selector if matches are left
				if(!empty($diff_c[$key]))
				{
					$css[$medium][implode(',',$key_u)] = $diff_c[$key];
					$return = TRUE;
				}
Hier überprüft die Funktion nochmal ob dem Selektor überhaupt noch Eigenschaften die kombiniert werden können zur Verfügung stehen (und fügt dann den neuen Selektor ein. Zu beachten ist noch dass TRUE zurückgegeben wird falls eine Zusammenfassung möglich war), weil
Code:
			}
			
			// Go through diff_c and remove no-more-shareable properties
			// and possibly the complete entry
			foreach($diff_c as $keyi => $valuei)
			{
				$selectors = unserialize($keyi);
				if(in_array($key_u[0],$selectors) || in_array($key_u[1],$selectors))
				{
					unset($diff_c[$keyi]);
				}
			}
hier alles aus $diff_c gelöscht wird was zum Zusammenfassen nicht mehr zur Verfügung steht.
Code:
		}
	}
	return $return;
}
Hier noch der Rückgabewert. Wichtig für die praktische Anwendung der Funktion die so aussieht:

Code:
while(csspp::merge_selectors($this->css)) {}
Das heißt, dass diese ohnehin schon aufwändige Optimierung so lange ausgeführt wird, bis es nirgendwo mehr was zum zusammenfassen gibt. Falls es nämlich nicht schon aufgefallen sein sollte, es werden immer nur 2 Selektoren zusammengefasst, allerdings sollte das mit 3 oder mehr Selektoren ebenfalls möglich sein.

Das einzigste was die Funktion noch beachten müsste ist, dass ein gewisses Verhältnis zwischen Größe der optimierten Eigenschaften und Anzahl der Selektoren erhalten bleibt (damit nacher nicht sowas wie
Code:
ein,ganz,langer,langer,................................,selektor{size:1em;}
bei rauskommt. Aber das ist erstmal nebensächlich.

Wer alles nochmal im Zusammenhang sehen möchte:
http://cdburnerxp.se/cssparse/css_parser.txt

Die Funktion ist da komplett drin, wer will kann den Parser damit mal durchlaufen lassen (in der Klasse die Funktion parse($string) [diese "Superoptimierung wird automatisch mit ausgeführt), das Ergebnis als Array befindet sich danach in der Variable $css).

Ich hoffe auf zahlreiche Antworten



Edit: Vielleicht nochmal ein kleines Beispiel damit ihr besser nachvollziehen könnt was die Funktion macht.

Das wird optimiert:
Code:
$css['standard']['a'][]['margin'] = '0';
$css['standard']['a'][]['color'] = 'red';
$css['standard']['a'][]['text'] = 'none';
$css['standard']['a'][]['text'] = '!important';

$css['standard']['b'][]['text'] = 'none';
$css['standard']['b'][]['color'] = 'red';

$css['standard']['d'][]['color'] = 'red';
$css['standard']['e'][]['margin'] = '2';
$diff sieht dann beim ersten Durchlauf so aus:

Code:
Array
(
    [a:2:{i:0;s:1:"a";i:1;s:1:"b";}] => 16
    [a:2:{i:0;s:1:"a";i:1;s:1:"d";}] => 8
    [a:2:{i:0;s:1:"b";i:1;s:1:"d";}] => 8
)
$diff_c so:

Code:
Array
(
    [a:2:{i:0;s:1:"a";i:1;s:1:"b";}] => Array
        (
            [0] => Array
                (
                    [color] => red
                )

            [1] => Array
                (
                    [text] => none
                )

        )

    [a:2:{i:0;s:1:"a";i:1;s:1:"d";}] => Array
        (
            [0] => Array
                (
                    [color] => red
                )

        )

    [a:2:{i:0;s:1:"b";i:1;s:1:"d";}] => Array
        (
            [0] => Array
                (
                    [color] => red
                )

        )

)
Das Ergebnis ist:

Code:
Array
(
    [standard] => Array
        (
            [a] => Array
                (
                    [0] => Array
                        (
                            [margin] => 0
                        )

                    [3] => Array
                        (
                            [text] => !important
                        )

                )

            [e] => Array
                (
                    [0] => Array
                        (
                            [margin] => 2
                        )

                )

            [a,b] => Array
                (
                    [1] => Array
                        (
                            [text] => none
                        )

                )

            [d,a,b] => Array
                (
                    [0] => Array
                        (
                            [color] => red
                        )

                )

        )

)
Mit Zitat antworten
Sponsored Links
  #2 (permalink)  
Alt 13.08.2005, 11:08
Benutzer
neuer user
 
Registriert seit: 01.11.2004
Beiträge: 66
Lestat befindet sich auf einem aufstrebenden Ast
Standard

Hi!

Schlag das ganze doch als PEAR Paket vor? Schon daran gedacht?

http://pear.php.net/

Wenn du hier schaust, dann siehst du das es einen XML_Beautifier und einen PHP_Beautifier bereits gibt.

Ein CSS_Beautifier fehlt noch! Ich bin gespannt was du von meinem Vorschlag hälst

mfg
Lestat
Mit Zitat antworten
Sponsored Links
  #3 (permalink)  
Alt 13.08.2005, 11:31
Benutzerbild von Floele
Erfahrener Benutzer
XHTMLforum-Mitglied
Thread-Ersteller
 
Registriert seit: 30.03.2005
Beiträge: 355
Floele befindet sich auf einem aufstrebenden Ast
Standard

Ich kann ja mal drüber nachdenken...aber erstmal muss das Ding fertig werden
Mit Zitat antworten
Antwort

Themen-Optionen
Ansicht

Forumregeln
Es ist Ihnen nicht erlaubt, neue Themen zu verfassen.
Es ist Ihnen nicht erlaubt, auf Beiträge zu antworten.
Es ist Ihnen nicht erlaubt, Anhänge hochzuladen.
Es ist Ihnen nicht erlaubt, Ihre Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus


Ähnliche Themen
Thema Autor Forum Antworten Letzter Beitrag
Redesign für Steiner Cycling Team pkipper Site- und Layoutcheck 11 09.02.2011 13:25
Einbindung von frei erhältlichen Scripten - CSS Problem DonL CSS 1 22.01.2011 17:09
MYspace mehr als nur CSS oder ? Vinceone CSS 0 12.07.2007 03:21
Eric Meyer's CSS Petty Ressourcen 0 21.11.2005 09:18
Mozilla ignoriert externes css DarkWanderer CSS 9 22.09.2005 12:39


Alle Zeitangaben in WEZ +2. Es ist jetzt 20:17 Uhr.