senäh

17senäh und so…

PHP, Server & Config, Zend Framework
07. Jun 2012
Kommentare: 0

SOAP: Aufbau von PHP-Strukturen für die korrekte Umwandlung in XML

Kategorien: PHP, Server & Config, Zend Framework | 07. Jun 2012 | Kommentare: 0

SOAP, so ein Protokoll, was mal erfunden wurde, um die Entwicklung von und Anbindung an Webservices zu erleichtern. Ob es in einer Zeit, wo Facebook, Google und Twitter auf JSON-basierte APIs setzen, das richtige Werkzeug ist, will ich hier nicht diskutieren. Nichtsdestotrotz habe ich bei der Recherche für meine Bachelorarbeit ein paar Erkenntnisse gewonnen, die ich gern teilen möchte. Dabei handelt es sich weniger um SOAP als solches, sondern viel mehr um die Wandlung von PHP-Typen nach XML.

Worum geht’s?

SOAP ist ein Protokoll zum Austausch von XML-Daten. XML weil plattformübergreifend und sprachunabhängig. Wenn man eine PHP-Applikation entwickelt, bei der solche Nachrichten ausgetauscht werden, will man sich aber nicht um das XML-Gerüst kümmern müssen. Generell möchte man ungern mit XML-Objekten o.ä. arbeiten. Schön wäre doch, wenn ich als Entwickler stinknormale PHP-Strukturen, also Klassen und Arrays, verwenden kann, und mir um die Konvertierung nach XML keinen Kopf machen muss.

Genau für diesen Zweck gibt es SOAP-Toolkits. Da ich gern mit dem Zend Framework arbeite, setze ich hier auf eine Komponente namens Zend_Soap. Diese Bibliothek lässt sich sowohl für einen Server, als auch für einen Client verwenden.Tatsächlich ist sie nichts anderes als eine Abstraktionsschicht der SOAP-Implementierung von PHP. Etwas ähnliches ollte ggf. auch für andere Frameworks zu finden sein.

Konventionen für die Konvertierung (yeah!)

Lange Rede, kaum ein Sinn: durch SOAP-Toolkits können wir in PHP arbeiten und ohne Aufwand XML-Output generieren. Schön. Dabei gibt es jedoch einige Konventionen, an die man sich halten muss, um die gewünschte XML-Struktur zu generieren.

Grundsätzlich ähneln sich Strukturen von XML und PHP. So lassen sich Verschachtelungen wunderbar abbilden. Die ersten Probleme entstehen, sobald man auch Attribute einfügen möchte. Und was in PHP leider gar nicht geht, ist das Hinzufügen von mehreren Elementen mit dem gleichen Namen. Was ich damit genau meine, zeige ich noch in den entsprechenden Beispielen.

Szenario 1: Verschachtelungen

Wie bereits erwähnt ist eine Verschachtelung verhältnismäßig intuitiv umzusetzen. Nehmen wir folgenden XML-String:

<auto>
	<rad>
		<schraube />
	</rad>
</auto>

In PHP recht leicht abzubilden über ein Array. Ich bevorzuge zwar eigentlich Klassen, allerdings bleibe ich der Einfachheit halber mal bei Arrays. Die Prinzipien sind nachher 1:1 übertragbar.

$arr = array(
    'auto' => array(
        'rad' => array(
            'schraube'
        )
    )
);

Das war einfach, oder?

Szenario 2: Verschachtelungen mit Attributen

Das Einfügen von Attributen ist auch nicht weiter kompliziert. Sie sind einfach weitere Elemente des Arrays, als wären sie XML-Elemente. Die Unterscheidung erfolgt dann durch das WSDL-Dokument, das die Typisierung vorgeben sollte.

Ein XML-Snippet a la

<auto marke="audi" baujahr="2006">
	<rad>
		<schraube />
	</rad>
</auto>

würde in PHP diese Gestalt annehmen:

$arr = array(
    'auto' => array(
        'marke' => 'audi',
        'baujahr' => 2006,
        'rad' => array(
            'schraube'
        )
    )
);

Szenario 3: Verschachtelungen mit Strings

Hin und wieder hat ein XML-Element ja auch einen Wert, bzw. einen Inhalt, der nicht aus weiteren Elementen besteht, z.B.:

<auto marke="audi" baujahr="2006">
	<rad>
		<schraube>Ich hatte nie eine Mutter</schraube>
	</rad>
</auto>

Die Umsetzung ist auch hier wieder denkbar einfach. Der Text muss lediglich als Wert des Schlüssels zugewiesen werden.

$arr = array(
    'auto' => array(
        'marke' => 'audi',
        'baujahr' => 2006,
        'rad' => array(
            'schraube' => 'Ich hatte nie eine Mutter'
        )
    )
);

Der aufmerksame Blogleser wird sich aber nun schon die erste sophisticatete Frage stellen…

Szenario 4: Verschachtelungen mit Attributen und String

Das erste größere Problem tritt in Erscheinung, wenn ein Knoten sowohl Attribute als auch einen String enthält.

<auto marke="audi" baujahr="2006">
	<rad>
		<schraube nummer="1">Ich hatte nie eine Mutter</schraube>
	</rad>
</auto>

Was genau ist das Problem? Die Streber unter euch würden unter heftigsten Verrenkungen mit der ausgestreckten Hand in der Luft um Aufmerksamkeit buhlen um zu antworten: während Attribute nach einem Array als Wert von <schraube/> schreien, müsste der String der Wert selbst sein. Array vs. String.

Die Lösung liegt darin den String ebenfalls als Element eines Arrays zu implementieren. Der Schlüssel muss dabei zwingend ein Underscore bzw. Unterstrich bzw. _ sein.

$arr = array(
    'auto' => array(
        'marke' => 'audi',
        'baujahr' => 2006,
        'rad' => array(
            'schraube' => array(
                '_' => 'Ich hatte nie eine Mutter',
                'nummer' => 1
            )
        )
    )
);

Szenario 5: Mehrere Elemente auf der gleichen Ebene mit identischem Namen

Nun zum König der PHP-XML-Konvertierungsprobleme: gleichnamige Elemente auf einer identischen Verschachtelungsebene. Ein etwas entschlacktes und auf das wesentliche Problem reduziertes Beispiel:

<auto>
	<rad position="vl" />
	<rad position="vr" />
	<rad position="hl" />
	<rad position="hr" />
</auto>

Konkret handelt es sich um die <rad/>-Elemente. Würden diese nicht denselben Namen tragen, sondern wären durchnummeriert, wäre die Lösung sowas hier:

$arr = array(
    'auto' => array(
        'rad1' => array(
            'position' => 'vl'
        ),
        'rad2' => array(
            'position' => 'vr'
        ),
        'rad3' => array(
            'position' => 'hl'
        ),
        'rad4' => array(
            'position' => 'hr'
        )
    )
);

Leider ist dem nicht so. Und dieser Fall ist in XML auch alles andere als unüblich. PHP sieht etwas derartiges jedoch nicht vor. Der anzuwendende Trick ist hierbei aber auch recht intuitiv. Man nimmt einen Schlüssel, der den Elementnamen widerspiegelt (in diesem Fall also rad). Alle zugehörigen Elemente werden in der Folge als numerisch indiziertes Array aufgelistet.

$arr = array(
    'auto' => array(
        'rad' => array(
            0 => array(
                'position' => 'vl',
            ),
            1 => array(
                'position' => 'vr',
            ),
            2 => array(
                'position' => 'hl',
            ),
            3 => array(
                'position' => 'hr',
            )
        )
    )
);

Hausaufgabe

Wenn ihr alles verstanden habt, was ich zu schildern versuchte, dürft ihr euch jetzt mal hier ran versuchen:

<auto marke="audi" baujahr="2006">
	<rad position="vl">
		<schraube nummer="1">Ich hatte nie eine Mutter</schraube>
		<schraube nummer="2">Deine Mutter hatte keine Mutter</schraube>
		<schraube nummer="3">Meine Mutter ist die Größte</schraube>
		<schraube nummer="4">Wir haben alle dieselbe Mutter, ihr Spacken</schraube>
		<schraube nummer="5">Müsste es nicht "die gleiche" heißen?</schraube>
	</rad>
	<rad position="vr">
		<schraube nummer="1">Ich hatte nie eine Mutter</schraube>
		<schraube nummer="2">Deine Mutter hatte keine Mutter</schraube>
		<schraube nummer="3">Meine Mutter ist die Größte</schraube>
		<schraube nummer="4">Wir haben alle dieselbe Mutter, ihr Spacken</schraube>
		<schraube nummer="5">Müsste es nicht "die gleiche" heißen?</schraube>
	</rad>
	<rad position="hl">
		<schraube nummer="1">Ich hatte nie eine Mutter</schraube>
		<schraube nummer="2">Deine Mutter hatte keine Mutter</schraube>
		<schraube nummer="3">Meine Mutter ist die Größte</schraube>
		<schraube nummer="4">Wir haben alle dieselbe Mutter, ihr Spacken</schraube>
		<schraube nummer="5">Müsste es nicht "die gleiche" heißen?</schraube>
	</rad>
	<rad position="hr">
		<schraube nummer="1">Ich hatte nie eine Mutter</schraube>
		<schraube nummer="2">Deine Mutter hatte keine Mutter</schraube>
		<schraube nummer="3">Meine Mutter ist die Größte</schraube>
		<schraube nummer="4">Wir haben alle dieselbe Mutter, ihr Spacken</schraube>
		<schraube nummer="5">Müsste es nicht "die gleiche" heißen?</schraube>
	</rad>
</auto>

Wer cheaten will:

$arr = array(
    'auto' => array(
        'marke' => 'audi',
        'baujahr' => 2006,
        'rad' => array(
            0 => array(
                'position' => 'vl',
                'schraube' => array(
                    0 => array(
                        'nummer' => 1,
                        '_' => 'Ich hatte nie eine Mutter'
                    ),
                    1 => array(
                        'nummer' => 2,
                        '_' => 'Deine Mutter hatte keine Mutter'
                    ),
                    2 => array(
                        'nummer' => 3,
                        '_' => 'Meine Mutter ist die Größte'
                    ),
                    3 => array(
                        'nummer' => 4,
                        '_' => 'Wir haben alle dieselbe Mutter, ihr Spacken'
                    ),
                    4 => array(
                        'nummer' => 5,
                        '_' => 'Müsste es nicht "die gleiche" heißen?'
                    )
                )
            ),
            1 => array(
                'position' => 'vr',
                'schraube' => array(
                    0 => array(
                        'nummer' => 1,
                        '_' => 'Ich hatte nie eine Mutter'
                    ),
                    1 => array(
                        'nummer' => 2,
                        '_' => 'Deine Mutter hatte keine Mutter'
                    ),
                    2 => array(
                        'nummer' => 3,
                        '_' => 'Meine Mutter ist die Größte'
                    ),
                    3 => array(
                        'nummer' => 4,
                        '_' => 'Wir haben alle dieselbe Mutter, ihr Spacken'
                    ),
                    4 => array(
                        'nummer' => 5,
                        '_' => 'Müsste es nicht "die gleiche" heißen?'
                    )
                )
            ),
            2 => array(
                'position' => 'hl',
                'schraube' => array(
                    0 => array(
                        'nummer' => 1,
                        '_' => 'Ich hatte nie eine Mutter'
                    ),
                    1 => array(
                        'nummer' => 2,
                        '_' => 'Deine Mutter hatte keine Mutter'
                    ),
                    2 => array(
                        'nummer' => 3,
                        '_' => 'Meine Mutter ist die Größte'
                    ),
                    3 => array(
                        'nummer' => 4,
                        '_' => 'Wir haben alle dieselbe Mutter, ihr Spacken'
                    ),
                    4 => array(
                        'nummer' => 5,
                        '_' => 'Müsste es nicht "die gleiche" heißen?'
                    )
                )
            ),
            3 => array(
                'position' => 'hr',
                'schraube' => array(
                    0 => array(
                        'nummer' => 1,
                        '_' => 'Ich hatte nie eine Mutter'
                    ),
                    1 => array(
                        'nummer' => 2,
                        '_' => 'Deine Mutter hatte keine Mutter'
                    ),
                    2 => array(
                        'nummer' => 3,
                        '_' => 'Meine Mutter ist die Größte'
                    ),
                    3 => array(
                        'nummer' => 4,
                        '_' => 'Wir haben alle dieselbe Mutter, ihr Spacken'
                    ),
                    4 => array(
                        'nummer' => 5,
                        '_' => 'Müsste es nicht "die gleiche" heißen?'
                    )
                )
            )
        )
    )
);

Appendix

Das war’s auch schon. Bei Fragen einfach die Kommentarfunktion nutzen.

Credits für das epische Artikelbild gehen übrigens an das aromatische Blog 😉

Tags:

Autor: Enno

Ich bin Enno. PHP ist mein Ding, aber auch alles Neue rund um die Themen HTML5, CSS3 & Co finde ich interessant. Ich mag es Leuten zu helfen und mein Wissen weiterzugeben. Sollte dir mein Beitrag gefallen haben, lass doch nen Kommentar da oder benutze einen der Social Buttons, um deinen Dank auszudrücken ;)