PHP Magazin

PHP, JavaScript, Open Web Technologies
X

Alles zu Facebook Hack findet Ihr in unserer neuen Serie: What the Hack?!

Treffen mit J(a)son

Web-2.0-Anwendungen mit JSON und JSON-RPC

Web 2.0 ist in – auch wenn kaum einer weiß, wo genau Web 2.0 wirklich anfängt. Abseits des ganzen Marketinghypes bieten AJAX und Co jedoch erstaunlich einleuchtende Vorteile in der Frontend-Entwicklung. Bleibt nur eine Frage: Wie kommuniziert man effektiv mit dem Server?

Wer mit AJAX arbeitet, setzt bisher zumeist auf XML: Nicht umsonst steht das X in "AJAX", dem asynchronen JavaScript in Verbindung mit XML (englisch: Asynchronous JavaScript And XML), für eben dieses. So praktisch XML für den Austausch von reinen Daten oder HTML-Fragmenten ist, so umständlich ist die Übergabe komplexer Objekte. Auch das im Gegensatz zu seiner Bezeichnung gar nicht so einfache "SOAP" (Simple Object Access Protocol) macht das Leben nur für den Client einfach, der auf ein WSDL zurückgreifen kann. Zu der aufwändigeren Entwicklung der Serverseite gesellt sich zudem das Problem, dass nur wenige Browser nativ mit einem SOAP-basierten Web Service Kontakt aufnehmen können. Für Web-2.0-Anwendungen also eher ungeeignet. Mit JSON, der "JavaScript Object Notation" (was in seiner Kurzform wie der englische Name "Jason" ausgesprochen wird), gibt es jedoch eine flexible und leistungsfähige Form zur Übertragung von Objekten mit nur marginalem Overhead, den alle JavaScript-fähigen Browser direkt unterstützen. Bevor wir uns allerdings weiter mit JSON und dem darauf aufbauenden Protokoll JSON-RPC (Remote Procedure Call) beschäftigen, brauchen wir die für AJAX gewohnte Grundlage: das XMLHttpRequest-Objekt.

Grundlagen

Wie den meisten Lesern wohl bereits bekannt sein dürfte, brauchen wir um ein derartiges Objekt zu erzeugen eine Browser-Weiche, denn für den Internet Explorer müssen wir eine Extrawurst in den Code integrieren. Da Microsoft es vorgezogen hat, das XMLHttpRequest-Objekt vor der Version 7 seines Browsers nur via ActiveX zu implementieren und sich zudem der ActiveX-Bezeichner auch noch von Version zu Version ändert, muss der in Listing 1 verschachtelte try-catch-Block zur Anwendung kommen, will man auch ältere Versionen des IE nicht vom Besuch der Seite abhalten.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">
<html>
<head>
<title>testseite</title>
<script type="text/javascript">
//<![CDATA[

// xmlHttpRequest Objektinstanz
var xhttp;

function demo_callback() {
// einfacher Dump der Antwort
if (xhttp.readyState==4) {
alert(xhttp.responseText);
}
}

function init() {

if (window.ActiveXObject){
try{
//Internet Explorer 6.x
xhttp = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
//IE 5.x
try{
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}catch(e){
xhttp = false;
}
}
} else if(window.XMLHttpRequest) {
// Fuer Mozilla, Opera und Safari
try{
xhttp = new XMLHttpRequest();
}catch(e){
xhttp = false;
}
}

}

function demo() {

if(xhttp) {


// asynchronen GET Request vorbereiten
xhttp.open('GET', 'echo.php', true);

// onReadyStateChange Handler
xhttp.onreadystatechange=demo_callback;

// leerer Body da kein POST
xhttp.send('');

}

}
// ]]>
</script>
</head>

<body onload="init(); demo();">
<p>hallo welt</p>
</body>
</html>

Mozilla/Firefox, Safari und Opera machen es einem da deutlich einfacher: für alle gilt dieselbe Syntax. Nachdem wir jetzt ein hoffentlich funktionsfähiges Grundgerüst besitzen, wenden wir uns der Übertragung der Daten und Strukturen zu. JSON selbst ist dabei eigentlich nur JavaScript-Quelltext in "komprimierter" Form. Komprimiert deshalb, weil die kürzestmögliche Schreibweise, mit der Objekte, Arrays und andere Variablen in JavaScript syntaktisch notiert werden können, hier Anwendung findet. Listing 2 zeigt ein Beispiel, wie so ein Objekt im JSON-Format aussehen kann. Das nach einem einfachen eval()-Aufruf in JavaScript erzeugte Objekt besitzt das Property result, das seinerseits ein Objekt mit den vier weiteren Properties hostnamestatusinfo und gruppen besitzt. Bei den ersten beiden sowie den Properties des neuerlichen Kind-objekts info handelt es sich um Strings, das Property gruppen definiert ein Array mit zwei Elementen.

{ "result": {
"hostname":"localhost",
"status":"Erfolgreich",
"info": {
"UID":"1",
"USERNAME":"demo",
"PASSWD":"demo"
},
"gruppen": ["1","10"]
}
}

Verwandlung im Backend

Da die meisten Backendsprachen wie z.B. PHP selbst natürlich kein JavaScript verstehen – auch wenn man dies zum Teil über Erweiterungen nachrüsten könnte – bedarf es einer Umwandlung von JSON-Quelltext in native Objekte und Variablen. Neben der Möglichkeit, dies pur in PHP zu lösen, wie es die JSON-Implementierung des Zend-Frameworks und die JSON-Klasse von Michael Migurski im PEAR-Projekt tun (Listing 3 und 4), gibt es im PECL-Repository seit einiger Zeit auch eine C-basierte Implementierung: ext/json setzt dabei auf die aktuelle JSON-Spezifikation auf und ist nach Angaben des Autors bis zu 270-mal schneller als eine in purem PHP geschriebene Lösung (Listing 5). Ab der Version 5.2.0 von PHP wird diese Erweiterung standardmäßig im Lieferumfang enthalten sein.

Wichtig zu beachten ist, dass, auch wenn alle Implementierungen grundsätzlich natürlich das gleiche machen und die Aufruf-Syntax nahezu identisch ist, die Herangehensweise der einzelnen Lösungen beim Dekodieren sich dennoch unterscheidet. Erzeugt Zend standardmäßig ein assoziatives Array, legen die PECL-Erweiterung und die PEAR-Klasse per Default ein Objekt an. UmZend_Json davon zu überzeugen, dass als Rückgabe ein Objekt gewünscht wird, muss man durch Angabe des Wunschtyps (Zend_Json::TYPE_OBJECT) als zweiten Parameter also erst etwas nachhelfen. Einen zweiten Parameter versteht indes auch die PECL-Erweiterung: Übergibt man hier abweichend von der Standardeinstellung ein boolesches true, wird anstelle eines Objekts ein Array mit assoziativen Keys generiert. Die PEAR-Implementierung spart sich solche Flexibilität beim Aufruf und erwartet stattdessen, dass eine entsprechende Einstellung (SERVICES_JSON_LOOSE_TYPE) bei der Initialisierung der Serviceklasse vorgenommen bzw. dem Konstruktor übergeben wird, liefert aber, wie bereits erwähnt, in der Standardeinstellung ein Objekt zurück.

<?php

// JSON Quelltext
$json='{"property":"hallo welt"}';

// Dekodieren
$phpObjekt= Zend_Json::decode($json, Zend_Json::TYPE_OBJECT);

// Dump
echo $phpObjekt->property;

// Neuen Wert setzen..
$phpObjekt->property='Guten Tag!';

// .. und kodieren
$encoded=Zend_Json::encode($phpObjekt);

// JSON-String ausgeben
echo "\n$encoded";
// JSON-Quelltext
$json='{"property":"hallo welt"}';

// Objekt erzeugen
$jsonService = new Service_JSON();

// Dekodieren
$phpObjekt=$jsonService->decode($json);

// Dump
echo $phpObjekt->property;

// Neuen Wert setzen..
$phpObjekt->property='Guten Tag!';

// ... und neu kodieren
$encoded=$jsonServer->encode($phpObjekt);

// JSON-String ausgeben
echo "\n$encoded";
// JSON-Quelltext
$json='{"property":"hallo welt"}';

// Dekodieren
$phpObjekt=json_decode($json);

// Dump
echo $phpObjekt->property;

// Neuen Wert setzen..
$phpObjekt->property='Guten Tag!';

// ... und neu kodieren
$encoded=json_encode($phpObjekt);

// JSON-String ausgeben
echo "\n$encoded";

Im Vollbesitz unserer geistigen Kräfte, sprich, allem was wir zur Verarbeitung an und für sich brauchen, fehlt uns jetzt nur noch eine standardisierte Kommunikation mit dem Server. Natürlich könnten wir hier auch unsere eigene Aufruflogik entwickeln, aber da proprietäre Lösungen zum einen unschön und zum anderen fast immer mit erheblichem Mehraufwand zur Überbrückung von Inkompatibilitäten verbunden sind, sollte von dieser Idee schnellstens Abstand genommen werden. Ganz besonders dann, wenn es doch bereits einen zwar noch recht jungen, dafür aber immerhin existierenden Standard gibt: JSON-RPC. Idealerweise wird über die POST-Methode ein standardisiertes JSON-Objekt an den Server übergeben und von diesem nach Ausführung der gewünschten Funktion zurückgeliefert.

 
Verwandte Themen: 

Kommentare

Ihr Kommentar zum Thema

Als Gast kommentieren:

Gastkommentare werden nach redaktioneller Prüfung freigegeben (bitte Policy beachten).

Übersicht zu diesem Beitrag

  1. Web-2.0-Anwendungen mit JSON und JSON-RPC
  2. Seite 2: JSON-RPC-Objekte