PHP: Wie wandelt man ein Objekt in ein Array um?
Der Cast (array) $objekt ist die Antwort, die man überall findet. Bei einem
stdClass funktioniert er auch. Bei einer eigenen Klasse bekommst du damit
Schlüssel, die du nicht mehr ansprechen kannst.
Der einfache Fall
Ein stdClass, wie ihn json_decode() liefert, lässt sich direkt casten:
<?php
$obj = new stdClass();
$obj->host = 'localhost';
$obj->port = 3306;
print_r((array) $obj);
?>
Array
(
[host] => localhost
[port] => 3306
)
Solange alle Eigenschaften public sind, ist die Sache erledigt.
Bei eigenen Klassen wird es hässlich
Jetzt dieselbe Sache mit einer richtigen Klasse:
<?php
class Server {
public string $host = 'localhost';
protected int $port = 3306;
private string $passwort = 'geheim';
}
$arr = (array) new Server();
?>
Das Array hat drei Einträge. Nur heißen sie nicht so, wie man denkt:
host (Länge 4)
\0*\0port (Länge 7)
\0Server\0passwort (Länge 16)
Da stehen NUL-Bytes in den Schlüsseln. Das Schema ist:
protected→\0+*+\0+ Nameprivate→\0+ Klassenname +\0+ Name
Die praktische Folge:
isset($arr['port']) : false
isset($arr["\0*\0port"]) : true
Du kommst an den Wert also nur heran, wenn du den NUL-Byte-Schlüssel exakt nachbaust. Und dafür musst du wissen, in welcher Klasse die Eigenschaft deklariert wurde — bei Vererbung ist das nicht unbedingt die Klasse des Objekts.
Besonders ärgerlich ist die Anzeige. print_r() stellt den Schlüssel so dar:
[port:protected] => 3306
Das sieht aus, als hieße der Schlüssel port, mit einer Zusatzinformation
dahinter. Tut er nicht. print_r() interpretiert das NUL-Byte-Schema für dich
und macht daraus etwas Lesbares. Wer daraufhin nach $arr['port'] greift,
sucht lange.
Nebenbei ist der Cast nicht der einzige Ort, an dem die Sichtbarkeit
hineinregiert. json_encode() nimmt ausschließlich die öffentlichen
Eigenschaften mit:
json_encode($server) : {"host":"localhost"}
serialize() nimmt zwar alle mit, verwendet dafür aber dieselben
NUL-Byte-Namen:
O:6:"Server":3:{s:4:"host";...s:7:"\0*\0port";...s:16:"\0Server\0passwort";...}
Die s:7: bzw. s:16: sind die Längenangaben — dieselben Zahlen wie oben.
get_object_vars() — und wo du es aufrufst
get_object_vars() liefert die Eigenschaften mit sauberen Schlüsseln. Welche
du siehst, hängt allerdings davon ab, von wo aus du die Funktion aufrufst:
von außen : ["host"]
von innen : ["host","port","passwort"]
Von außerhalb der Klasse siehst du nur public. Innerhalb der Klasse siehst du
alles — und bekommst normale Schlüssel ohne NUL-Bytes.
Damit ist der saubere Weg vorgezeichnet: Man schreibt die Umwandlung in die Klasse hinein.
<?php
class Server {
public string $host = 'localhost';
protected int $port = 3306;
private string $passwort = 'geheim';
public function toArray(): array {
return get_object_vars($this);
}
}
?>
Das hat außerdem den Vorteil, dass du entscheidest, was nach außen geht. Das Passwort willst du in dem Array vermutlich gar nicht haben.
Der Cast ist nicht rekursiv
Der nächste Stolperstein. Bei verschachtelten Objekten wandelt der Cast nur die oberste Ebene um:
(array) $aussen : ['innen'] ist stdClass <- immer noch ein Objekt
über JSON : ['innen'] ist array
Deshalb findet man als Lösung überall diesen Umweg:
<?php
$arr = json_decode(json_encode($obj), true);
?>
Der arbeitet tatsächlich rekursiv. json_decode() mit true als zweitem
Parameter liefert überall Arrays statt stdClass:
json_decode($json) : stdClass
->db ist : stdClass
json_decode($json, true) : array
['db'] ist : array
Aber der JSON-Umweg kostet dich etwas
Und zwar Typinformationen. Der Round-Trip durch JSON kennt nur die Typen, die JSON kennt:
1.0 wird zu : 1 (int)
PHP_INT_MAX bleibt : true
DateTimeImmutable : {"date":"2026-08-28 00:00:00.000000","timezone_type":3,"timezone":"UTC"}
Die erste Zeile ist die, die man in der Praxis trifft. Ein Preis 19.0 kommt
als int 19 zurück, weil JSON zwischen 19 und 19.0 nicht unterscheidet.
Solange du damit nur rechnest, geht das gut. Bei einem ===-Vergleich oder
einer Funktionssignatur mit float fällt es auf.
Und jedes Objekt, das nicht bloß ein Datencontainer ist, verliert seinen Typ.
Aus dem DateTimeImmutable wird ein Array mit drei Feldern. Das lässt sich
nicht zurückverwandeln.
Was übrigens funktioniert: PHP_INT_MAX überlebt den Umweg korrekt. Das wird
gelegentlich anders behauptet.
Was ich empfehle
stdClassausjson_decode(): Gib gleichtrueals zweiten Parameter mit, dann brauchst du gar nicht umzuwandeln.- Eigene Klasse: Schreib eine
toArray()-Methode. Zehn Zeilen, dafür vorhersagbar. - Fremde Klasse, die du nicht ändern kannst:
get_object_vars(), wenn dir die öffentlichen Eigenschaften reichen. Sonst die Reflection-API. - Den JSON-Umweg nur, wenn dir Typen egal sind.
Zusammenfassung
(array)funktioniert nur beistdClassbzw. rein öffentlichen Klassen problemlos.protectedwird zu"\0*\0name",privatezu"\0Klasse\0name".print_r()zeigt[port:protected]an — das ist nicht der Schlüsselname.get_object_vars()sieht von innen alles, von außen nurpublic.- Der Cast ist nicht rekursiv,
json_decode(..., true)schon. - Der JSON-Umweg macht aus
1.0einintund aus Objekten Arrays.
Hinweis zu Netcup (Werbung)
Der deutsche Hoster Netcup bietet unter anderem günstige und zugleich leistungsstarke Webhosting Pakete, KVM-basierte Root Server und dezidierte Server an. Mit unseren Gutscheincodes kannst du noch mehr Geld sparen (6€ bei deiner ersten Bestellung, 30% Rabatt auf alle KVM-basierten Root Server, ...).