PHP: Was ist stdClass?
stdClass ist eine leere Klasse. Keine Eigenschaften, keine Methoden, kein
Konstruktor. Man begegnet ihr meistens, ohne sie angefordert zu haben — als
Rückgabewert von json_decode(). Und dann hat man ein paar Fragen.
Was es ist
new stdClass() : stdClass
Methoden : []
Elternklasse : false
Das war’s. Eine Klasse ohne Inhalt, die man mit Eigenschaften befüllen kann.
Und gleich das häufigste Missverständnis aus dem Weg geräumt: stdClass ist
nicht die Basisklasse aller PHP-Objekte. Der Name legt das nahe — “standard
class” —, aber es stimmt nicht:
stdClass ist Basis von ArrayObject? false
PHP hat, anders als Java oder C#, keine gemeinsame Wurzelklasse. Wenn du
prüfen willst, ob etwas ein Objekt ist, nimm is_object() und nicht
instanceof stdClass.
Woher man sie bekommt
Drei Wege, und den ersten hat man selten selbst gewählt:
<?php
$a = json_decode('{"host":"localhost"}'); // stdClass
$b = (object) ['host' => 'localhost']; // stdClass
$c = new stdClass();
$c->host = 'localhost';
?>
Bei json_decode() ist das der Standard. Mit true als zweitem Parameter
bekommst du stattdessen Arrays — und das ist meistens die bessere Wahl, dazu
gleich mehr.
Die Sache mit den dynamischen Eigenschaften
Seit PHP 8.2 ist es deprecated, Eigenschaften auf ein Objekt zu schreiben, die
nicht in der Klasse deklariert sind. stdClass ist davon aber ausgenommen:
eigene Klasse : Deprecated: Creation of dynamic property Eigene::$neu is deprecated
stdClass : keine Meldung
Das ist der Grund, warum die Änderung Code mit json_decode()-Ergebnissen
nicht betrifft, deine selbstgeschriebenen Datencontainer-Klassen aber schon.
Für die brauchst du dann entweder das Attribut #[AllowDynamicProperties]
oder — besser — deklarierst die Eigenschaften einfach.
In PHP 9 wird aus der Deprecation ein Error. stdClass bleibt ausgenommen.
Die Falle mit den Eigenschaftsnamen
Und jetzt der Punkt, den ich für den wichtigsten in diesem Artikel halte. Ein Array kann Schlüssel haben, die als Eigenschaftsname nicht funktionieren:
<?php
$obj = (object) ['0' => 'null', 'mit leer' => 'x', 'ok' => 'y'];
?>
Die Eigenschaften sind alle da:
Array-Schlüssel : [0,"mit leer","ok"]
$obj->ok : 'y'
$obj->{0} : 'null'
$obj->{"0"} : 'null'
$obj->{"mit leer"} : 'x'
Aber $obj->0 ist ein Syntaxfehler. $obj->mit leer auch. Du kommst nur mit
geschweiften Klammern heran — und darauf muss man erst einmal kommen.
Das ist der Grund, json_decode($json, true) zu nehmen, wann immer die
Schlüssel aus fremder Hand kommen. Bei einem Array ist jeder Schlüssel
erreichbar, ganz gleich wie er aussieht:
<?php
$daten = json_decode($json, true);
echo $daten['mit leer']; // unproblematisch
?>
Bis PHP 7.2 waren diese Eigenschaften übrigens gar nicht erreichbar. Seither gehen sie wenigstens mit den geschweiften Klammern.
Hin und zurück
(array) $obj keys : [0,"mit leer","ok"]
get_object_vars keys : [0,"mit leer","ok"]
Bei stdClass liefern beide dasselbe, weil es keine Sichtbarkeit gibt. Bei
einer eigenen Klasse mit private oder protected sieht das anders aus —
dazu gibt es einen eigenen Artikel.
Was man beachten muss: Der Cast ist nicht rekursiv, json_decode() schon.
(object) auf verschachteltes Array : ->innen ist array
json_decode : ->innen ist stdClass
Fehlende Eigenschaften
$k->fehlt : NULL (Warning: Undefined property)
$k->fehlt ?? 'std' : 'std'
isset($k->fehlt) : false
property_exists : false
Wie bei Arrays: ?? für den Standardwert. property_exists() ist das
Gegenstück zu array_key_exists() und der richtige Test, wenn null als Wert
eine eigene Bedeutung hat.
Vergleichen und Kopieren
Zwei Dinge, die sich bei Objekten anders verhalten als bei Arrays.
Erstens der Vergleich:
$p == $q : true (gleicher Inhalt)
$p === $q : false (nicht dieselbe Instanz)
$p === $r : true
Bei Objekten prüft === auf Identität — sind das zwei Namen für dasselbe
Objekt? Bei Arrays vergleicht === dagegen den Inhalt. Wer das verwechselt,
schreibt einen Vergleich, der immer false liefert.
Zweitens die Übergabe:
nach aendere($z) : a = 99 <- verändert, ohne &
wieder auf 1 gesetzt, dann ersetze() : a = 1 <- unverändert
<?php
function aendere(stdClass $o): void { $o->a = 99; } // wirkt nach außen
function ersetze(stdClass $o): void { $o = (object) ['a' => 42]; } // nicht
?>
Das wird oft als “Objekte werden per Referenz übergeben” beschrieben, und das ist nicht ganz richtig. Die Variable hält einen Verweis auf das Objekt, ist aber selbst keine Referenz. Deshalb wirkt eine Änderung an einer Eigenschaft nach außen, eine Neuzuweisung des Parameters aber nicht.
Und drittens das Kopieren:
nach clone und Änderung : Original n = 99 <- mitgeändert
über serialize : Original n = 1
clone ist flach. Verschachtelte Objekte werden nicht mitkopiert, sondern
geteilt. Wenn du wirklich eine tiefe Kopie brauchst, geht das über
unserialize(serialize($obj)) — oder du schreibst ein __clone(), das die
inneren Objekte selbst klont.
Wann man es nimmt und wann nicht
stdClass ist gut für das, wofür sie gedacht ist: ein Datencontainer ohne
Verhalten, dessen Struktur man vorher nicht kennt. Eine dekodierte
API-Antwort etwa.
Sobald die Struktur feststeht, ist eine eigene Klasse die bessere Wahl:
<?php
class Konfig {
public function __construct(
public readonly string $host,
public readonly int $port,
) {}
}
?>
Was das bringt, habe ich nachgemessen — und dabei etwas gelernt. Beim Lesen eines Tippfehlers verhalten sich beide gleich:
Konfig::$tipfehler : Warning: Undefined property
stdClass::$tipfehler : Warning: Undefined property
Die typisierte Klasse hilft hier also nicht. Der Unterschied zeigt sich erst beim Schreiben:
stdClass : stillschweigend angelegt
Konfig : Deprecated: Creation of dynamic property
Das ist eine genauere Aussage als das übliche “typisierte Klassen fangen Tippfehler ab”. Sie fangen sie beim Zuweisen ab, nicht beim Lesen. In PHP 9 wird aus der Deprecation ein Error, dann ist der Unterschied deutlicher.
Was eine eigene Klasse darüber hinaus bringt: Typen, die tatsächlich geprüft
werden, ein Konstruktor, der Pflichtfelder erzwingt, readonly, sowie eine
Stelle, an der man nachlesen kann, welche Felder es überhaupt gibt. Das
letzte ist im Alltag der größte Gewinn.
Zusammenfassung
stdClassist eine leere Klasse — und nicht die Basisklasse aller Objekte.- Man bekommt sie von
json_decode()ohnetrueund vom Cast(object). - Sie ist von der 8.2er-Deprecation für dynamische Eigenschaften ausgenommen, eigene Klassen sind es nicht.
- Schlüssel wie
0oder'mit leer'werden zu Eigenschaften, die nur mit$obj->{...}erreichbar sind — bei fremden Daten lieberjson_decode($j, true). - Bei Objekten prüft
===die Identität, nicht den Inhalt. cloneist flach.- Eine eigene Klasse fängt Tippfehler beim Schreiben ab, beim Lesen nicht.
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, ...).