Heute werde ich wieder einmal eine nützliche PHP-Funktion vorstellen. Genauer gesagt handelt es sich um eine Klasse, die CSS und JavaScript komprimieren kann. Bei diesem Minification (oder minify) genannten Prozess, werden unnötige Kommentare, Leerzeichen, Tabulatoren und Zeilenumbrüche entfernt, wodurch die Datei kleiner wird und so schneller an den Benutzer übertragen werden kann. Zusätzlich kann in der Klasse der „bereinigte“ Quellcode auch noch vor der Übertragung mit gzip oder Deflate komprimiert werden, damit er noch schneller übertragen werden kann.
<?php
class minifier {
public function minify_CSS($str, $zip = FALSE) {
$str = str_replace(array("\r", "\n", "\t"), '', $str);
$str = preg_replace('#/\*(.*)\*/#U', '', $str);
header('Content-Type: text/css; charset=UTF-8');
header('Cache-Control: public, max-age=1209600');
if($zip === TRUE)
$str = $this->zip($str);
echo $str;
}
public function minify_JS($str, $zip = FALSE) {
$str = preg_replace('#//.*#', '', $str);
$str = str_replace(array("\r", "\n", "\t"), '', $str);
$str = preg_replace('#/\*(.*)\*/#U', '', $str);
header('Content-Type: text/javascript; charset=UTF-8');
header('Cache-Control: public, max-age=1209600');
if($zip === TRUE)
$str = $this->zip($str);
echo $str;
}
protected function zip($str) {
if(isset($_SERVER['HTTP_ACCEPT_ENCODING']) === TRUE) {
if(stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE) {
header('Content-Encoding: deflate');
$str = gzdeflate($str, 9);
} elseif(stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
header('Content-Encoding: gzip');
$str = gzencode($str, 9);
}
}
return $str;
}
}
?>
Die Klasse kann dann in einer beliebigen Datei so aufgerufen werden:
<?php
require_once('minifier.class.php');
$min = new minifier();
$min->minify_JS('[JavaScript-Quellcode]', TRUE);
$min->minify_CSS('[CSS-Quellcode]', TRUE);
?>
Im Gegensatz zu vielen anderen Minifiern habe ich versucht die Optimierungen „leichtgewichtig“ zu gestalten, also einen Kompromiss aus Bandbreiteneinsparung und Ressourcenschonung zu finden. Viele andere Minifier nehmen nämlich noch mehr Optimierungen vor (Variablenumbenennung, komplizierte RegEx-Konstrukte), die viel Rechenzeit benötigen und daher nur bedingt für die On-the-fly-Erstellung von komprimiertem Code geeignet sind. Sicherlich lassen sich damit die Dateigrößen teils erheblich weiter reduzieren aber auf Kosten der serverseitigen Performance.
Auf blogeum wird die Klasse in einer erweiterten Form auch eingesetzt, um CSS und JS vor der Übertragung zu verkleinern und die Übertragungszeiten zu vermindern.
Wie immer steht der Code unter einer Creative Commons-Lizenz.
Mal wieder eine kleine aber feine PHP-Funktion. Diesmal geht es darum, einen Rechner per Wake-on-LAN-Signal (WOL) über das Netzwerk aufzuwecken. Prinzipiell muss dazu nur eine Verbindung zu einem anderen Rechner im Netzwerk oder einem Router aufgebaut werden, der dann das entsprechende Signal im Netzwerk herumschickt und so den gewünschten Computer aufweckt, sobald dieser das Signal empfängt.
Das Signal ist relativ einfach aufgebaut: 6 Mal der Hexwert FF und anschließend 16 Mal die MAC-Adresse der Netzwerkkarte des zu weckenden Rechners. Dieses sogenannte MagicPacket wird via UDP an Port 0, 7 oder 9 gesendet.
Wenn man die IP-Adresse des Routers nicht kennt, kann auch ein Broadcast (Signal an alle Geräte im Netzwerk) gesendet werden, sofern der Router dies zulässt.
<?php
$mac = '00:00:00:00:00:00';
$ip = 'udp://255.255.255.255';
function wakeup($mac, $router) {
if(strlen($mac) !== 17)
return FALSE;
if(filter_var($router, FILTER_VALIDATE_URL) === FALSE)
return FALSE;
$raw = sscanf($mac, '%2x:%2x:%2x:%2x:%2x:%2x');
$func = create_function('$v', 'return isset($v);');
$raw = array_filter($raw, $func);
if(sizeof($raw) !== 6)
return FALSE;
$fp = fsockopen($router, 9, $errnum, $errstr, 4);
if(is_resource($fp) === FALSE) {
trigger_error('['.$errnum.'] '.$errstr, E_USER_ERROR);
return FALSE;
}
$magicp = "\xFF\xFF\xFF\xFF\xFF\xFF";
$hexmac = '';
foreach($raw AS $c) {
$hexmac .= chr($c);
}
$magicp .= $hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac.$hexmac;
fwrite($fp, $magicp);
fclose($fp);
printf('Wake-Up-Signal an %02X:%02X:%02X:%02X:%02X:%02X übertragen!', $raw[0], $raw[1], $raw[2], $raw[3], $raw[4], $raw[5]);
return TRUE;
}
wakeup($mac, $ip);
?>
Ich benutze diese Funktion um den Server aus der Ferne aus dem Ruhezustand zu holen, um mit ihm arbeiten zu können.
Diese Frage beantwortet die Ranking-Liste von TIOBE Software. Die Liste wird auf Grund von Daten der beliebtesten Suchmaschinen erstellt.
The ratings are based on the number of skilled engineers world-wide, courses and third party vendors. The popular search engines Google, MSN, Yahoo!, Wikipedia and YouTube are used to calculate the ratings.
TIOBE Software: TIOBE Index

An der Spitze steht aktuell (das bei mir unbeliebte) Java. Das altehrwürdige C kommt nur auf Platz 2. Interessant ist auch, dass sich, im Vergleich zum Oktober letzten Jahres, keine Veränderung ergeben hat. Das beweist wiederum, dass sich nur durchdachte und gut dokumentierte Programmiersprachen durchsetzen.
Ein wenig verwundert mich, dass PHP schon auf Platz 3 folgt. Ich hätte nicht gedacht, dass es derart weit verbreitet ist um gleich auf Java und C zu folgen wohingegen C++ erst nach PHP kommt.
[via SJMP]
In PHP sind mehrere Funktionen eingebaut, um eine Zeichenfolge zu durchsuchen. Leider wird viel zu oft mit den sprichwörtlichen Kanonen auf Spatzen geschossen. Viel zu oft werden überfrachtete Funktionen benutzt, um eine einfache Existenzprüfung in einem String durchzuführen. Dabei bietet PHP auch wesentlich effizientere aber bedauerlicherweise unbekannte Funktionen für diesen Zweck. Im Folgenden werden einmal einige dieser Funktionen näher beleuchtet und deren Effizienz getestet.
Die Kontrahenten
Für einen repräsentativen Test wurden die beliebte Perl-kompatible Funktion preg_match() sowie die beiden String-Funktionen strpbrk() und strpos() herangezogen.
preg_match()
Mit Hilfe der Funktion preg_match() kann man eine Zeichenkette anhand eines regulären Ausdrucks durchsuchen. Ein regulärer Ausdruck ist dabei, vereinfacht ausgedrückt, eine Art von speziellem Suchmuster mit dem man geschickt eine Zeichenfolge nach bestimmten Kriterien durchsuchen kann.
Die Schnittstelle von preg_match() ist nachfolgend angegeben:
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags [, int $offset ]]] )
Für diesen Test sind nur die ersten beiden obligatorischen Parameter interessant, die das Suchmuster und die zu untersuchenden Zeichenfolge angeben.
strpbrk()
strpbrk() ist eine relativ unbekannte Funktion mit deren Hilfe man eine Zeichenfolge auf das erste Vorkommen eines beliebigen Zeichens aus einer vorgegebenen Zeichenliste untersuchen kann. Der Rückgabewert ist der Rest-String, beginnend bei der ersten Fundstelle des zuerst gefundenen Zeichens.
Bei einer einfachen Existenzprüfung würde $char_list demnach dem regulären Ausdruck [abcd] entsprechen (wobei a, b, c und d beliebige Zeichen sind).
string strpbrk ( string $haystack , string $char_list )
strpos()
Diese Funktion ähnelt strpbrk(). Sie durchsucht eine Zeichenfolge nach dem ersten Vorkommen einer Such-Zeichenfolge und liefert deren Position innerhalb der Zeichenfolge zurück.
int strpos ( string $haystack , mixed $needle [, int $offset= 0 ] )
Benchmark
Zur Untersuchung der Performance der drei Funktionen wurde nachfolgender Code benutzt:
<?php
$array = range('a', 'z');
$array = array_merge($array, $array, $array, $array, $array, $array, $array, $array, $array, $array);
$string = str_shuffle(join('', $array));
$char = chr(mt_rand(97, 122));
$char = chr(mt_rand(97, 122)).chr(mt_rand(97, 122)).chr(mt_rand(97, 122));
$char = chr(mt_rand(97, 122)).'|'.chr(mt_rand(97, 122)).'|'.chr(mt_rand(97, 122));
$timer = microtime(TRUE);
for($x = 0; $x < 100000; ++$x) {
}
echo microtime(TRUE) - $timer;
?>
Die Zeilen 7, 10 und 13 werden je nach Test entfernt um nur eine Situation zu testen.Zunächst wird eine zu untersuchende Zeichenfolge bestehend aus den Kleinbuchstaben a bis z erstellt. Diese Zeichenfolge besteht aus ingesamt 260 Zeichen, die zufällig angeordnet werden, um etwaige PHP-Cache-Mechanismen zu umgehen. Danach wird ein zufälliges Zeichen aus dem Zeichenvorrat ausgewählt, nach dem im Anschluss gesucht werden soll.
Dann wird 100.000 Mal nach diesem Zeichen (dieser Zeichenfolge) gesucht und die dabei vergangene Zeit gemessen.
Dieser Test wurde 5 Mal hintereinander für jede Funktion ausgeführt und die gemessenen Zeiten gemittelt, um etwaige „Ausreißer“ nach oben oder unten zu glätten.
Alle Testläufe wurden auf einem virtuellen Windows 2000 Server mit Apache 2.2.11 und PHP 5.2.8 durchgefüht.
Die Ergebnisse

| Funktion | Gesamtlaufzeit | pro Durchlauf | relative Performance |
|---|
| preg_match | 0,20465045 s | 2,0465045 µs | + 421,55% |
| strpbrk | 0,07036624 s | 0,7036624 µs | + 144,94% |
| strpos | 0,04854722 s | 0,4854722 µs | 100,00% |
Bei der Suche nach einem einzelnen Zeichen spielt strpos() seine Stärken aus und ist deutlich schneller als seine Konkurrenten. strpbrk() braucht rund 1,5-mal so lang und preg_match() sogar beachtliche 4,2-mal.

| Funktion | Gesamtlaufzeit | pro Durchlauf | relative Performance |
|---|
| preg_match | 0,853334379 s | 8,53334379 µs | + 1192,82% |
| strpbrk | 0,071539068 s | 0,71539068 µs | 100,00% |
Bei der Suche nach drei beliebigen Zeichen ist strpbrk() um den eindrucksvollen Faktor 12 schneller als preg_match().

| Funktion | Gesamtlaufzeit | pro Durchlauf | relative Performance |
|---|
| preg_match | 0,494307613 s | 4,94307613 µs | + 595,96% |
| strpos | 0,082943153 s | 0,82943153 µs | 100,00% |
Auch bei diesem Testlauf können die regulären Ausdrücke nicht punkten. Im Schnitt ist preg_match() rund 6-mal langsamer gegenüber strpos().
Anmerken muss man hier, dass die zu suchende Zeichenfolge evtl. auch nicht in der zu untersuchenden Zeichenfolge vorkommen kann, weil die zu suchende Zeichenfolge zufällig erstellt wird.
Fazit
Für einfache Existenzprüfungen (also ob ein Zeichen in einem String vorkommt) sind jegliche PCRE-kompatiblen Funktionen mehr als ungeeignet. Sie sind deutlich langsamer als die eingebauten String-Funktionen von PHP.
Dieser Performanceunterschied lässt sich auf den Overhead durch die Regex-Engine von PHP zurückführen. Zudem benötigen die PCRE-Funktionen kurzzeitig mehr Speicher bei der Ausführung auf dem Server, was für diesen Test aber nicht von Bedeutung war.
Dementsprechend sollte man für einfache Aufgaben auf die Funktionen strpbrk(), strpos(), strstr(), etc. zurückgreifen. Der Dedicated- oder Shared-Server wird es einem danken!
Bei meiner Arbeit am Blog stoße ich immer wieder auf Probleme, die eine adäquate Lösung verlangen. Im Web gibt es zahlreiche Funktionen, die diese Probleme lösen (sollen). Doch nicht immer sind diese Funktionen auch nach meinem Geschmack. Deshalb nutze ich meist nur die grundlegenden Ideen dieser Funktionen und schreibe dann einfach meine eigenen.
Daher möchte ich nun einmal die Thumbnail-Funktion vorstellen, die ich hier benutze und die mir schon sehr gut gefällt.
<?php
function create_thumbnail($src_file, $max_x = 60, $max_y = 60) {
if(extension_loaded('gd') === FALSE)
return FALSE;
list($src_x, $src_y, $type) = getimagesize($src_file);
switch($type) {
case IMAGETYPE_JPEG:
$src_img = imagecreatefromjpeg($src_file);
break;
case IMAGETYPE_PNG:
$src_img = imagecreatefrompng($src_file);
break;
case IMAGETYPE_GIF:
$src_img = imagecreatefromgif($src_file);
break;
default:
return FALSE;
}
if(is_resource($src_img) === FALSE)
return FALSE;
$ratio = min($max_x/$src_x, $max_y/$src_y);
$thumb_x = round($src_x * $ratio);
$thumb_y = round($src_y * $ratio);
if($ratio < 1) {
$thumb_img = imagecreatetruecolor($thumb_x, $thumb_y);
imagecopyresampled($thumb_img, $src_img, 0, 0, 0, 0, $thumb_x, $thumb_y, $src_x, $src_y);
} else {
$thumb_img =& $src_img;
}
$thumb_file = '<Thumbnailordner>/'.$src_file;
switch($type) {
case IMAGETYPE_JPEG:
imagejpeg($thumb_img, $thumb_file, 50);
break;
case IMAGETYPE_PNG:
imagepng($thumb_img, $thumb_file);
break;
case IMAGETYPE_GIF:
imagegif($thumb_img, $thumb_file);
break;
default:
imagejpeg($thumb_img, $thumb_file, 50);
}
@imagedestroy($src_img);
if($ratio < 1)
@imagedestroy($thumb_img);
return TRUE;
}
?>
Die Schnittstelle gestaltet sich sehr einfach. Übergeben werden nur Pfad und Dateiname der Ursprungsdatei und die maximale Ausdehnung in X- und Y-Richtung der Thumbnail-Datei. Die Ursprungsdatei wird dann proportional verkleinert, je nachdem welche Dimension überschritten wurde.
Die angegebene Funktion ist sicher nicht perfekt. Für Verbesserungsvorschläge bin ich immer offen und werde sie wenn nötig auch gerne einbauen.
Die Funktion steht, wie alle anderen Blog-Inhalte auch, unter einer Creative Commons-Lizenz.