Auf meiner Suche nach einem guten XML Parser, bin ich auf die sehr gute C++ Bibliothek pugixml gestoßen. Mit wenigen Zeilen Code, lassen sich XML Dateien parsen und verändern. pugixml ist dabei sehr schnell, da kein XML zuvor validiert wird, portabel und besteht lediglich aus zwei Sourcecode Dateien (Header und cpp Datei). Letzteres macht die Integration in eigene Projekte sehr leicht.
Ein String kann ganz einfach mit folgenden Zeilen geparsed werden:
std::string source(
"<test>"
"<eintrag id=\"1\">bla</eintrag>"
"<eintrag id=\"2\">bla2</eintrag>"
"</test>");
pugi::xml_document doc;
pugi::xml_parse_result result =
doc.load_buffer_inplace(
(void*)(source.c_str()),
source.length()
);
Um beispielsweise einen Eintrag zu suchen, kann folgender Aufruf ausgeführt werden, wobei hier XPath verwendet wird:
pugi::xpath_node_set entries =
doc.select_nodes("/test/eintrag[@id='2']");
pugi::xml_node entry = entries[0].node();
std::cout << entry[0].node().child_value(); // bla
Das node_set apps enthält dann alle gefundenen nodes, wobei über den [] Operator auf die einzelnen Elemente zugegriffen werden kann.
Einen guten Überblick bietet der QuickStart-Guide oder die sehr ausführliche und gute Dokumentation.
pugixml steht unter der MIT Lizenz zur Verfügung und nach einem ersten Einsatz kann ich die Bibliothek nur empfehlen. Aus meiner Sicht ein echter Tipp, insbesondere, weil die Einbindung einfach und unkompliziert ist. pugixml hat mich von der hohen Qualität stark an mongoose erinnert.
Häufig kommt es vor, dass Sprachdateien nicht vollständig sind. Ich habe den Effekt bei rsslounge, da ich hier von verschiedenen Personen Sprachdateien bekomme. Da ich nicht sicherstellen kann, ob auch wirklich jeder Key der Sprachdatei übersetzt wurde (außer ich prüfe manuell die gesamte Datei), bietet es sich an, einfach als Fallback-Lösung die entsprechende englische Übersetzung zu verwenden.
Zend_Translate bietet hier eine einfache Möglichkeit “Routen” zu erstellen. Das heißt man kann festlegen, welche Sprache alternativ verwendet werden soll. Dabei können sogar Ketten gebildet werden (z.B. wenn Französisch nicht gefunden wurde, dann in der deutschen Übersetzung suchen und wenn dort auch nichts gefunden wird, in der englischen). Wobei sich mir der Sinn von so einer Verkettung nicht ganz erschließt.
Das setzen einer Route ist sehr einfach. Folgendes Beispiel scanned ein übergebenes Verzeichnis nach CSV Dateien und übernimmt deren Inhalt als Übersetzung. Mit dem Key “route” wird definiert, wie die Sprachen verkettet werden sollen. In diesem Beispiel ist Englisch die Fallbacksprache für Deutsch und Französisch.
$language = new Zend_Translate(
'csv', // Typ der Quelle: hier csv Dateien
'locale', // Quell-Verzeichnis
'en', // Standard Sprache
array( // Optionen
'scan' => Zend_Translate::LOCALE_DIRECTORY,
'route' => array( 'de' => 'en', 'fr' => 'en' )
));
Der sauberste Weg ist es natürlich alle Schlüssel auf zu übersetzen. Beim Aufspüren von fehlenden Übersetzungen hilft Zend_Translate ebenfalls.
Ein Projekt, das schon länger auf der ToDo Liste steht, habe ich nun heute etwas genauer in Augenschein genommen:
Bei HeadJS handelt es sich um ein kleines JavaScript Skript, welches in der Lage ist, weitere JavaScript Dateien nachzuladen:
head.js(
"/path/to/jquery.js",
"/google/analytics.js",
"/js/site.js",
function() {
// all done
}
);
Der Vorteil ist die Tatsache, dass die weiteren Skriptdateien kontrolliert parallelisiert heruntergeladen werden. Wird dieser Aufruf am Ende der Website durchgeführt (also vor dem schließenden body Tag), so kann die HTML Seite, sowie die Stylesheets ungehindert geladen und gerendert werden, bevor mit der Verarbeitung von JavaScript Dateien begonnen wird. Das Ergebnis: die Seite wird schneller angezeigt. Mit 2,3 kB ist das Skript auch schön kompakt und klein.
Weitere Features sind eine bessere HTML5 Kompatibilität, automatisch gesetzte CSS Klassen (z.B. für bestimmte Browser) und noch einige weitere Schmankerl. Es lohnt sich also das kleine Skript genauer anzusehen. Im Einsatz habe ich es bei rsslounge (ab Version 1.7, die ich in wenigen Tagen online stellen will).
Zur headJS Webseite: http://headjs.com/
Jetzt ist es schon wieder einige Zeit her, dass ich hier etwas geschrieben habe und ich denke ich gebe mal ein Lebenszeichen von mir. Natürlich gibt es zahlreiche interessante Themen in meiner Queue zum bloggen, aber wie immer findet man nicht die Zeit oder nötige Ruhe dazu.
Die Problemstellung
Nun aber zum Thema: für ein aktuelles Projekt benötige ich, von meiner PHP Webapplikation heraus, den Zugriff auf einige Hardwarekomponenten. Zudem will ich einige Fremdbibliotheken verwenden, die sehr rechenintensive Aufgaben übernehmen und auch nur in C++ zur Verfügung stehen. Die Frage ist also: wie kann ich diese Komponenten an mein PHP Programm ankoppeln?
Für Java gibt es eine ganz gute Server Bridge, die innerhalb der Java Virtual Machine einfach einen kleinen Server startet, der XML Anfragen entgegen nimmt, stellvertretend ausführt und das Ergebnis, auch wieder über eine XML Kommunikationsschnittstelle, zur Verfügung stellt. Die nötige PHP Klasse wird ebenfalls durch den Java Server direkt zur Verfügung gestellt. Eine ähnliche Bridge Lösung bringt auch der Zend Server bereits out of the box mit.
Für C++ habe ich so eine Lösung nicht gefunden und als weiteren Ansatz überlegt, eine PHP Extension zu entwickeln. Sowohl für Linux, als auch für Windows, gibt es hier Tutorials (siehe “Extension-Entwicklung unter WAP“, “Wrapping C++ Classes in a PHP Extension” oder “Extension Writing Part I: Introduction to PHP and Zend“). Allerdings ist, neben den Tutorials, der ganze Vorgang nicht gut dokumentiert und scheinbar muss das Kompilat der Extension exakt dem des verwendeten PHP Kompilats entsprechen. Insgesamt also eher eine wackelige Angelegenheit.
Lösungsansatz
Meine Lösung ist hingegen pragmatisch und verfolgt eine eher losere Kopplung: Meine C++ Komponenten werden in ein eigenständiges Programm ausgelagert, das selbst, über einen gewöhnlichen HTTP Server, die benötigten Dienste zur Verfügung stellt. Dabei orientiere ich mich an dem REST Architektur Prinzip und binde jeweils eine Teil-Funktionalität an eine feste URL. Die Parameter und Rückgabewerte werden mittels JSON kodiert. Von PHP Seite aus, wird der Aufruf der C++ Komponenten sauber gekapselt, so dass für das PHP Programm der Eindruck entsteht, dass eine gewöhnliche Funktion aufgerufen wird.
Welche Vor- und Nachteile hat das Vorgehen? Zum einen ist die in C++ implementierte Funktionalität sauber gekapselt und von der Webapplikation getrennt. Dadurch wird die Software leichter wartbar. Durch eine saubere Schnittstellendefinition, kann die C++ Serverkomponente auch unabhängig vom Wissen über die PHP Applikation gepflegt werden. Zum anderen entsteht aber ein zusätzlicher Aufwand (mit zusätzlichen Fehlerquellen). Die Schnittstelle muss definiert und implementiert werden, ein Fehlerhandling muss durchgeführt werden und eine zusätzliche Serverapplikation muss gestartet werden, was hinsichtlich der Sicherheit berücksichtigt werden muss. Dennoch halte ich die Lösung für die eleganteste, denn sie nutzt genau die Vorteile der Vernetzung und der losen Kopplung aus. So könnte die C++ Komponente bei höherer Last auch leicht auf einen eigenen Server umgezogen oder unabhängig von der Webapplikation betrieben werden.
Implementierung
In der Theorie hört sich natürlich immer alles ganz nett an, aber wie sieht das mit dem HTTP Server aus? Hier gibt es eine hervorragende C++ Bibliothek, mit der sich ohne großen Aufwand eine solcher HTTP Server realisieren lässt. mongoose heißt das Wunderding, steht unter der MIT Lizenz unter Google Code zur Verfügung. Es unterstützt Windows, Linux und Mac OS X, bietet eine CGI Schnittstelle, SSL Verschlüsselung, ACL, eine saubere API und ist nicht größer als 60 kB. Die Dokumentation ist zwar recht knapp, aber dank Beispiele findet man sich schnell zurecht und auch die API Beschreibung ist vollkommen ausreichend.
Will man einen eigenen Server starten, so muss zuerst ein neuer Context erzeugt werden:
struct mg_context* ctx; ctx = mg_start();
Mit mg_set_options, werden die Servereinstellungen geändert. Folgende zwei Zeilen deaktivieren das Directory Listing und setzten 8080 als Port.
mg_set_option(ctx, "dir_list", "no"); mg_set_option(ctx, "ports", "8080");
Aus Sicherheitsgründen erlaube ich nur einen Zugriff vom selben Rechner aus, also erlaube nur die IP Adresse 127.0.0.1:
mg_set_option(ctx, "acl", "-0.0.0.0,+127.0.0.1");
Mit mg_set_uri_callback kann ein Pointer auf eine eigene Funktion an eine URL gebunden werden. Diese wird aufgerufen, wenn der Client die URL aufruft, wobei die URL auch Wildcards enthalten kann.
mg_set_uri_callback(ctx, 'dosomething', &Server::dosomething, NULL);
Als dritter Parameter darf ein Pointer (void*) auf ein beliebiges Objekt übergeben werden. Die Callback Methode bekommt dazu alle notwendigen Parameter, wie die aktuelle Verbindung, den aktuellen Request und das zuvor übergebene Objekt (hier NULL) übergeben.
void Server::capture(struct mg_connection *conn,
const struct mg_request_info *request_info,
void *user_data) {
char* param = mg_get_var(conn, "param");
mg_printf(conn, "%s", "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
}
Mit mg_printf kann dann eine Antwort an den Client gesendet werden. Hier als einfaches Beispiel einfach nur ein OK (HTTP 200). In meinem Szenario wird hier der generierte JSON Text zurück gegeben. Mit mg_get_var können übergebene Parameter (POST, GET) ausgelesen werden. Wichtig: um memory leaks zu vermeiden, müssen diese mit mg_free wieder freigegeben werden.
Natürlich ist das nur die eine Hälfte. PHP muss jetzt auch Funktionen, auf dem Server, aufrufen können. Dazu kommt der Zend_Http_Client zum Einsatz. Dieser ist im Zend Framework enthalten, mit etwas Frickelei kann er aber auch aus dem Gesamtpaket heraus gepickt werden. Da ich aber das Zend Framework für die ganze Anwendung nutze, ist das Aufrufen der URL und das Decodieren des JSON Strings in einen Action Helper ausgelagert, der gleichzeitig eine einfache Fehlerbehandlung durchführt:
class JsonHttpHelper extends Zend_Controller_Action_Helper_Abstract {
public function get($url, $params = array()) {
try {
$client = new Zend_Http_Client($url);
foreach($params as $param=>$value)
$client->setParameterPost($param, $value);
$response = $client->request('POST');
$res = Zend_Json::decode($response->getBody());
if($response->isSuccessful()) {
return $res;
} else {
return array("error" => "request not successfully: ".$res["error"]);
}
} catch(Exception $e) {
return array("error" => $e->getMessage());
}
}
}
Bisher bin ich mit der Lösung sehr zufrieden. Besonders mongoose kann ich nur wärmstens empfehlen. Auch von der Verarbeitungsgeschwindigkeit ist das Ergebnis zufriedenstellend und kann sich sehen lassen. Um den Rahmen nicht zu sprengen, habe ich die serverseitige Generierung und Verarbeitung von JSON außen vor gelassen. Hier gibt es aber auch zahlreiche Bibliotheken, so dass die Suche nicht schwer fallen dürfte.
Ich habe in diesem Blog ja bereits einmal darüber geschrieben, wie man mit Hilfe des Zend Frameworks fremde Seiten verarbeiten kann. Einen zweiten, sehr eleganten Weg, wie man fremde HTML Seiten verarbeiten kann, bietet die Bibliothek phpQuery. Diese greift zwar auch auf das Zend Framework zurück, bringt davon aber nur zwei Teilkomponenten mit und ist dadurch viel schlanker.
phpQuery greift die Vorgehensweise von jQuery auf und bietet eine zentrale Funktion an, mit deren Hilfe DOM Elemente in einem Dokument selektiert werden können. Beispielsweise liefert die folgende Funktion alle ‘ul’ Elemente.
$uls = pq('ul');
Mit phpQuery hat man natürlich auch die Möglichkeit, wie bei jQuery auch DOM Elemente zu manipulieren, zu löschen oder neue einzufügen.
Will man also beispielsweise alle Suchergebnisse von Google abgreifen, so reicht folgender Code aus, der dann alle gefundenen URLs ausgibt:
require('phpQuery/phpQuery.php');
$q = "phpquery";
$doc = phpQuery::newDocument(
file_get_contents('http://www.google.com/search?q='.urlencode($q))
);
foreach(pq('.g', $doc) as $entry) {
$url = pq($entry)->find('.r a')->attr('href');
$title = pq($entry)->find('.r a')->html();
echo '<a href="'.$url.'">'.$title.'</a><br />';
}
Mir gefällt der Ansatz, die Funktionalität von jQuery teilweise in die Welt von PHP zu übertragen. Jeder, der in irgendeiner Form HTML in PHP verarbeiten muss, sollte sich diese Bibliothek auf jeden Fall genauer ansehen.
Heute feiert der populäre phphatesme Blog seinen 500. Beitrag und präsentiert sich im Zuge des Jubiläums in einem neuen Design. Mir gefällt das neue Layout sehr gut, es ist schlicht und übersichtlich. Natürlich gibt es auch ein Gewinnspiel mit interessanten Bücher, Eintrittskarten und Zeitungsabos als Preise.
Wer meinen Blog liest, kennt phphatesme bereits von meinem Gastbeitrag über rsslounge. Jedem anderen empfehle ich einen Blick auf den Blog zu werfen, der täglich interessante Themen aus der Softwareentwicklung mit PHP aufgreift.
Achtung: für jQuery simple carousel gibt es jetzt eine eigene Projektseite auf google code: http://code.google.com/p/simple-carousel/
Eine einfache und ansprechende Möglichkeit mehrere Informationen auf Webseiten zu präsentieren, bietet ein “carousel” Effekt. Besonders wenn eine Liste von Eigenschaften veranschaulicht, Screenshots oder Features oder Fotografien präsentiert werden sollen, bietet es sich an, die einzelnen Bilder nacheinander einzuschieben und wieder verschwinden zu lassen (Beispiel: rsslounge). Neben einer sehr guten Nutzung des zur Verfügung stehenden Platzes, fällt durch die Bewegung die Information auf und lenkt die Aufmerksamkeit auf wichtige Kernbotschaften.
Slide Effekte und Carousel Skripte gibt es im Internet viele, überzeugen konnte mich keines. Meistens sind sie zu umfangreich oder es ist spezielles CSS notwendig, das dann unüberschaubar eingebunden bzw. die eigene Struktur daran angepasst werden muss. Nachdem jQuery sehr komfortabel ist und ich die meisten meiner Projekte auf jQuery aufbauen, habe ich mich entschlossen selbst ein einfaches jQuery Plugin zu schreiben. Dieses kommt ohne spezielles CSS aus und ist mit 1,8 kB auch schön kompakt.
Das Skript erwartet folgende HTML Struktur:
<ul id="carousel"> <li>erster Frame (z.B. ein Bild)</li> <li>zweiter Frame (z.B. ein Bild)</li> <li>dritter Frame (z.B. ein Bild)</li> <li>vierter Frame (z.B. ein Bild)</li> ... </ul>
Um nun aus der Liste ein carousel zu machen, reicht beispielsweise folgender Aufruf aus:
$("#carousel").simplecarousel({
slidespeed: 700,
auto: 5000,
width: 480,
height: 280
});
In obigen Fall ist das carousel 480×280 Pixel groß, wechselt automatisch alle 5 Sekunden zum nächsten Frame und der Wechsel (Geschwindigkeit des slide Effektes) dauert 0,7 Sekunden.
Es werden folgende Parameter unterstützt:
- width: (int) Breite
- height: (int) Höhe
- next: (jQuery Objekt/Selector) wird auf dieses Objekt geklickt, wird der nächste Frame aufgerufen
- prev: (jQuery Objekt/Selector) wird auf dieses Objekt geklickt, wird der vorhergehende Frame aufgerufen
- vertical: (boolean) true = vertikales carousel; false = horizontales carousel
- auto: (int/boolean) false = kein automatischer Wechsel; Wert in Millisekunden = pause bis automatisch zum nächsten Frame gewechselt wird
- slidespeed: (int) Geschwindigkeit des Wechsels in Millisekunden
- visible: (int) Anzahl an Frames die gleichzeitig sichtbar sein sollen
- fade: (int) wird dieser Wert angegeben, so wird anstatt zu sliden das Frame ausgeblendet und das neue wieder eingeblendet. Die Geschwindigkeit des fade Effektes ist dann dieser Wert (siehe aditu.de)

