Mehr CSS – mit Less?

Nach dem ich mit Bootstrap von Twitter in Berührung kam, stieß ich auch auf Less. Bootstrap selbst bietet bereits einfache und flexible HTML, CSS und Javascript Kompontenten und setzt bei CSS auf Less, das CSS um Variablen, Operationen, Funktionen, Mixins und einiges mehr, erweitert.

Im Prinzip wird die erweiterte Syntax von einem Compiler in reguläres CSS überführt. Die Vorteile liegen scheinbar auf der Hand:

  • Variablen können ausgelagert werden, somit können z. B. unterschiedliche Farbgebungen oder Layout-Breiten realisiert werden.
  • Während der Programmierung können codesparende Techniken genutzt werden z. B. ermöglichen die sogenannten Mixins eine Wiederverwendung von bereits genutzten Anweisungen.
  • Mehrere Less-Definitionen können durch die import-Anweisung in eine CSS-Datei kompiliert werden.
  • Das CSS kann optional durch den Compiler minimiert werden,
  • Es können Berechnungen durchgeführt werden, …

Im Gegenzug sehe ich jedoch auch ein paar Probleme. Zum einem muß ein zusätzliches CSS-Framework (?) in den Entwicklungsprozess eingebettet werden. Zum anderen gefällt mir eine entscheidende Funktionalität nicht besonders. Dazu gleich mehr. Jetzt erstmal alle Nachteile die ich sehe:

  • Überschreiben von 3rdparty-Less-Definitionen (was für ein Wort…) ist umständlich bzw. erzeugt unnötigen Code.
  • Erschwertes Debuggen, da die Zeilennummern des generierten CSS nicht mit denen der Less-Definition übereinstimmen.
  • Aufwand für die Einarbeitung.
  • Die Kompilier sind teilweise fehleranfällig (z. B. werden keine Schleifen bei den import-Anweisungen erkannt).

Nun zu dem erwähnten Beispiel. Wir haben z. B. fremden Code z. B. Twitter Bootstrap in unser Projekt eingebunden. Wir hüten uns natürlich davor diesen Code zu verändern, damit er leicht zu aktualisieren bleibt (z. B. könnte auch über svn:externals Code eingebunden werden). Nun möchten wir das Layout entsprechend den eigenen Designvorstellungen anpassen. Ich nehme exemplarisch folgende Konstellation an:

Less Beispiel

Die Datei new/default.less stellt die neu zu schreibende Less-Definition dar, die Datei 3rdparty/lib.css ist eine Definition einer Fremdbibliothek.

Der Inhalt der Dateien nehme ich wie folgt an:

Less Dateien in der Gegenüberstellung

Im Compiler (WinLess) wird nur die Datei new/default.less angegeben und kompiliert:

Less Kompilierer

Nun die spannende Frage: Was wird wohl rauskommen? Unter der Annahme, dass der Kompilierer zuerst die 3rdparty-Datei abarbeitet und anschließend alle weiteren Dateien (override.less und enthaltene Anweisungen) könnte man meinen, er würde im schlechtesten Fall einfach alle Anweisungen nacheinander aufreihen. Im besten Fall würde eine einzige body-Anweisung erzeugt werden, die lautet

body {
  background-color:#ff0000; // übersetzter Wert "red"
  border:none;
  font-family:verdana;
  width:500px;
}

Das Ergebnis sieht – wie vermutet – folgendermaßen aus:

/* Einbinden fremde Definition */
body {
  background-color: #ff0000;
  font-family: verdana;
  border: 1px solid black;
}
/* Überschreiben */
body {
  background-color: #ff0000;
  border: none;
}
/* neue Anweisungen */
body {
  width: 500px;
}

Von schlankem CSS will ich hier nicht sprechen. Außerdem ist nicht mehr ersichtlich, dass der Hintergrund in der ursprünglichen Anweisung den Wert “#5B83AD” hatte.

Fazit

Auf den ersten Blick erscheint eine zusätzliche Abstraktionsschicht interessant, insbesondere die Verwendung der Variablen und der sogenannten Mixins. In der Praxis erhöht sich der Entwicklungsaufwand jedoch nicht unerheblich (Stichwort: Debugging und Integration) und das erzeugte CSS ist in den meisten Fällen nicht kleiner als eigener Code. Der eigentlich Kernnutzen für mich läge im einfachen und optimierten Überschreiben von bestehenden Less-Definitionen. Leider sind dafür die Kompilier noch nicht optimiert.

PHP Optimierungen

Auf der Seite PHP Benchmark gibt es eine kleine Auflistung von PHP Optimierungen, die ganz gelungen sind. Natürlich ist die Optimierung von Code bis auf die letzte Zeile übertrieben, aber es schadet nicht ein paar Regeln zu beachten.

Referenzierungsoperators &

Im ersten Test wird der Einsatz des Referenzierungsoperators & geprüft. Als Ergebnis kommt heraus, dass bei eindimensionalen Arrays der Zugriff schneller erfolgt als bei mehrdimensionalen Arrays.

$value = &$simpleArray[$i]; // Laufzeit schneller
$value = $simpleArray[$i]; // als diese Variante

Im Gegenzug zu mehrdimensionalen Arrays:

$value = &$bigArray[$a][0][0][0] // Laufzeit langsamer
$value = $bigArray[$a][0][0][0] // als diese Variante

IF und Switch

Zwischen den Varianten gibt es keinen nennenswerten Laufzeitunterschied. Lediglich bei den Vergleichen mit Typisierung (===) gab es einen kleinen Vorteil.

Einfache vs. doppelte Anführungszeichen

Bei reine Strings macht es schienbar keinen Unterschied, ob einfache oder doppelte Anführungszeichen verwendet werden. Leider stimmt das nur, solange keine Variablen innerhalb des Strings ersetzt werden müssen. Ich habe das mal ausprobiert:

"aa $x aaaa $x aaaa $x a"; // 0.000581
'aa $x aaaa $x aaaa $x a'; // 0.000160
'aa '. $x. ' aaaa '. $x. ' aaaa '. $x. ' a'; // 0.000565
echo 'aa ', $x, ' aaaa ', $x, ' aaaa ', $x, ' a'; // 0.000378

Funktionen ISSET und EMPTY UND IN_ARRAY

Auch hier gab es keinen nenneswerten Sieger. Die Funktion in_array ist teurer als die anderen zwei und sollte nur auf existierenden Variablen aufgerufen werden.

is_array($gibtsNicht); // langsam (und PHP Notice: Undefined variable)
isset($gibsNicht) && is_array($gibtsNicht) // besser

 Schleifen

Ein Funktionsaufruf im Schleifenkopf ist keine gute Idee. Die vielen Aufrufe kosten sehr viel Laufzeit.

<code>for ($i=0; $i<count($x); $i++);</code> // ganz schlecht
$count=count($x);
for ($i=0; $i<$count; $i++); // super

Außerdem kostet der Befehl list ebenfalls sehr viel Laufzeit. Meiner Meinung nach sollte man diese Funktion nicht verwenden, da der Code dadurch schlecht lesbar wird:

while(list($key) = each($hash))... // langsam und schlecht lesbar
foreach($hash as $key=>$value)... // schnell

Gegen Ende des Beitrags wurde nochmal die Schleifen bzw. die Modifikation des Array betrachtet, über das iteriert wird. Das die for-Schleife in folgender Variante so schlecht abschneidet, hat mich doch sehr verwundert:

// 425%
foreach($aHash as $key=>$val)
  $aHash[$key] .= "a"; 
 
// 100%
$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++)
  $aHash[$key[$i]] .= "a";

Das schlechte Ergebnis wird aber durch die Operation .= verursacht, die den bestehenden Wert mit “a” konkateniert. Mit einer reinen Zuweisung läuft die foreach-Schleife sehr performant. Ich habe zusätzlich folgendes ausprobiert:

foreach($aHash as $key=>$val);  // 0.00001
foreach($aHash as $val);        // 0.000007
foreach($aHash as $key=>&$val); // 0.00023
foreach($aHash as &$val);       // 0.00024
foreach($aHash as $key=>$val)
  $aHash[$key] .= "a";          // 0.00024 (ursprüngliche Variante)
 
while(list($key) = each($aHash))
  $aHash[$key] .= "a";          // 0.00008 (ursprüngliche Variante)

Anmerkung: Das Array bestand ebenfalls aus 100 Elemente mit einer Schlüssellänge von 24byte und einem Wert mit 10k pro Eintrag.

Ich schätze, dass es damit zusammenhängt, dass die foreach-Schleife intern mit einer Kopie des Array arbeitet. Notiz an mich: Diesen Artikel mal genauer lesen: PHP internals: When does foreach copy?

Fazit

Ich glaube, dass die wenigsten Ergebnisse praxisrelevant sind. Bester Tip ist, dass man in Schleifenkopfen keine Funktionen aufrufen soll bzw. in Schleifen allgemein auf Funktionsaufrufe achten soll. Ein super Artikel fand ich auf ircmaxell’s blog im Artikel On Optimization in PHP. Code soll in erster Linie lesbar und somit besser warten sein. Außerdem wird dort die 90/10-Regel von R. Pattis erwähnt: “90% of a program’s execution time is spent in only 10% of its code.”. Die Kunst des Optimieres ist es also, die 10% des Codes in angemessener Zeit ausfindig zu machen…

 

 

 

PHP Curl mit Proxy

Hier ein Snippet, wie man sich an einem Proxy über CURL authentifizieren kann:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://www.foo.bar');
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
curl_setopt($ch, CURLOPT_PROXY, 'proxy:8080');    
curl_setopt($ch, CURLOPT_PROXYPORT, 8080);    
curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'DOMÄNE\benutzer:password');    
 
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20);
curl_setopt($ch, CURLOPT_REFERER, 'http://myreferer.de');
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla...');
 
$result = curl_exec($ch);
$info = curl_getinfo($ch);
$error = curl_error($ch);
 
curl_close($ch);

 

Interoperabilität zwischen Typo3 und MSSQL

Wer sich schon einmal den Wiki-Eintrag zu MSSQL durchgelesen hat, wird schnell merken, dass Typo3 an der einen oder anderen Stelle nicht wirklich rund mit der Microsoft Datenbank zusammenarbeiten wird. Ob diese Info aus dem Wiki noch aktuell ist, steht auf einem anderen Blatt… dort aufgeführte Bugmeldungen wurden jedoch bereits behoben. Ich habe jedoch einige Blog-Einträge (siehe Links unten),  gefunden, die von “großem” Aufwand sprechen, ein Typo3 unter MSSQL zu installieren. Die Unterstützung scheint jedoch besser zu werden. Zumindest stellt eine PHP Installation unter Windows kein Problem mehr da. Wer den Typo-Source gern wie unter Linux mittels symbolischen Link in sein Projekt einbinden will, kann das auch unter Windows mit einem kleinen Tool Junction von Microsoft ermöglichen.

Für die Bildgenerierung muß ImageMagick und Ghostscript installiert werden. Auch dies ist für Windows verfügbar.

Das “Rewrite” von URLs erfordert Zusatzaufwand ist jedoch auch in einem Typo3-Wiki-Eintrag beschrieben.

Das größtes Problem, das ich sehe, sind jedoch die Erweiterungen, die nicht unbedingt mit MSSQL kompatibel sein müssen. Diese sind dann entsprechend zu testen.

Wer seine Webseite unter Typo3 betreiben will und Daten von einem Microsoft SQL Server benötigt, könnte auch das Typo3 auf MySQL betreiben. MySQL läuft problemlos auf einem Windows-Server und ist (mitterweile) auch dort nicht wesentlich langsamer als unter Linux. Die Daten des MSSQL-Servers können in einer eigenen (Extbase-)Extension verarbeitet und dargestellt werden. Ich denke da z. B. an ein Produktmodul, dass die Produkteinformationen aus dem ERP-System erhält. Leider habe ich die Vermutung, dass Extbase keine komfortables Möglichkeit für den Zugriff auf MSSQL bietet, zumindest habe ich dazu nichts gefunden. Mag sein, dass das mit Typo5 mit Flow besser wird, da dort wohl Doctrine als Abstraktionsschicht verwendet wird, aber bis dahin wird noch etwas Zeit vergehen.

Aber was spricht dagegen den Zugriff auf die Datenbank mit etwas anderem durchzuführen? Nix (naja, ein paar Argumente gibts sicherlich). Und wie? Beispielsweise mit dem Zend Framework. Ich habe dazu eine Extension geschrieben, die den Autoloader des ZF einbindet (Extension an erster Stelle laden “top”). In jeder anderen Extension kann mittels passendem Zend Adapter eine Verbindung aufgebaut werden. Natürlich gibt es auch damit das eine oder andere Problem z. B. funktioniert der für den Microsoft SQL Server benötigte Adapter nur mit der PHP Erweiterung sqlsrv und wird erst aber Microsoft SQL Server 2005 oder höher unterstützt (darunter funktionierte bei mir die limit(Anzahl, Offset) Funktion nicht korrekt… ich hab den Adapter abgeleitet und diese Funktion überschrieben). Ansonst kann man nicht das ORM von Extbase verwenden, aber zumindest kann man sich daran “orientieren”, indem man die Zugriffe auf MSSQL-Daten in einer Repository-Klasse kapselt.

Fazit:

Wenn man ein Typo3-System auf MSSQL betreiben will, kann dies sicherlich mit etwas Aufwand hinbekommen. Wenn man jedoch die Möglichkeit hat MySQL unter Windows zu betreiben, würde ich das auch tun. Meist liegen die Daten im Business-Umfeld auf Windows-Servern und stammen dann wohl von anderen Anwendungen, die sowieso durch eine spezielle Implementierung aufbereitet werden müssen.

Links:

Zend Controller produziert JSON

Damit ein Controller in einer Zend Applikation kein Layout ausgibt bzw. auch keine View-Dateien verarbeitet, genügen zwei Zeilen im Controller:

$this->_helper->layout()->disableLayout();
$this->_helper->viewRenderer->setNoRender(true);

Dadurch wird keinerlei Markup ausgegeben.

Ein möglicher Anwendungsfall sind Webseiten, die Inhalte per AJAX nachladen. Das kann mittels jQuery leicht über folgende Funktion realisiert werden:

jQuery.getJSON( url [, data] [, success(data, textStatus, jqXHR)] )

Der Parameter url ruft eine Aktion eines Controllers auf, der eben nur JSON zurückliefert. Dies kann anschließend im Javascript weiterverarbeitet werden.

Update:

Noch einfacher geht es über folgenden Helper:

$this->_helper->json($data);

 

PHP-Warning abfangen

Man sollte potenzielle Warning-Kandidaten wie z. B. file_get_contents nicht mit dem @-Operator unterdrücken (das @ unterdrückt alle Fehlermeldungen), sondern sollte das so lösen, wie es auch im Zend-Framework gemacht wird:

set_error_handler('handleError');
file_get_contents($url);
restore_error_handler();

Das kann natürlich auch ein Errorhandler innerhalb einer Klasse sein. Der Handler selbst kann in rudimentärer Form so aussehen:

function handleError($errno, $errstr, $errfile, $errline, array $errcontext)
{
  // error was suppressed with the @-operator
  if (0 === error_reporting())
    return false;
  throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

Nun hat man den Vorteil, dass man mit einem try-catch-Block flexibler reagieren.

Ein bisschen mehr Delegation bitte…

Die Vererbung in der OOP hat ja durchaus eine Berechtigung. Leider wird sie viel zu oft unbedacht eingesetzt. Ein prominentes Beispiel ist die Java-Klasse Properties. Diese leitet von der Klasse Hashtable ab und erbt somit auch alle Methoden der Oberklasse. Aber dieser Zusammenhang zwischen Properties und HashMap passt nicht ganz (Properties sind eine HashMap, “is-a”-Beziehung). Meiner Meinung nach würde hier eine “Properties verwendet eine HashMap”-Beziehung besser passen. Natürlich kann man darüber streiten, aber ich möchte hier nicht einer OOP-Diskussion lostreten, sondern für PHP die Möglichkeit der Delegation vorstellen.

Continue reading

Einstellungen in XML auslagern

Ab PHP 5 ist es ziemlich einfach Einstellungen in eine XML-Datei auszulagern und diese auszulesen. Eine solche XML kann beispielsweise so aussehen:

<?xml version="1.0" encoding="UTF-8"?>
<custom:settings xmlns:custom=...>
  <custom:setting key="key1" value="Einstellung Nr. 1" />
</custom:settings>

Natürlich habe ich die XML nicht ausformuliert. Am besten man baut sich dazu eine DTD oder eine XSD.

Die PHP Implementierung kann vereinfacht folgendermaßen umgesetzt werden:

$xml=dirname(__FILE__).'/settings.xml';
$simpleXml=new SimpleXMLElement($xml, null, true);
$setting=current($simpleXml->xpath("custom:setting[@key='$key_']/@value"));
if($setting===false)
  return false;
return (string)$setting;

Voraussetzung ist natürlich, dass SimpleXML auf dem Server zur Verfügung steht. Wenn nicht gibt es noch DomDocument.