Wie greife ich auf Objekteigenschaften mit Namen wie Ganzzahlen zu?

87

Ich benutze so json_decode()etwas wie:

$myVar = json_decode($data)

Was mir folgende Ausgabe gibt:

[highlighting] => stdClass Object
        (
            [448364] => stdClass Object
                (
                    [Data] => Array
                        (
                            [0] => Tax amount liability is ....... 

Ich möchte auf den Zeichenfolgenwert im Schlüssel [0] zugreifen. Wenn ich versuche, etwas zu tun wie:

print $myVar->highlighting->448364->Data->0;

Ich erhalte diesen Fehler:

Analysefehler: Syntaxfehler, unerwarteter T_DNUMBER

Die zwei Ziffern / ganzen Zahlen dort scheinen ein Problem zu sein.

Avinash Shah
quelle
1
@FelixKling: Ich habe auch einen Lebenslauf erstellt, aber es stellt sich tatsächlich heraus, dass es kein Betrug ist: Es macht einen Unterschied, ob der Eigenschaftsname mit einer Zahl beginnt oder nur aus Zahlen besteht !
Jon
@ Jon: Mmmh, interessant ... hätte einen Test machen sollen, bevor ich denke. Danke für die Information!
Felix Kling

Antworten:

286

Aktualisiert für PHP 7.2

PHP 7.2 führte eine Verhaltensänderung beim Konvertieren von numerischen Schlüsseln in Objekt- und Array-Casts ein , die diese besondere Inkonsistenz behebt und dafür sorgt, dass sich alle folgenden Beispiele wie erwartet verhalten.

Eine Sache weniger zu verwirren!


Ursprüngliche Antwort (gilt für Versionen vor 7.2.0)

PHP hat seinen Anteil an dunklen Gassen, in denen Sie sich wirklich nicht wiederfinden wollen. Objekteigenschaften mit Namen, die Zahlen sind, sind eine davon ...

Was sie dir nie erzählt haben

Fakt Nr. 1: Sie können nicht einfach auf Eigenschaften mit Namen zugreifen, die keine legalen Variablennamen sind

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->123foo; // error

Fakt 2: Sie können auf solche Eigenschaften mit geschweifter Klammer-Syntax zugreifen

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!

Fakt Nr. 3: Aber nicht, wenn der Eigenschaftsname nur aus Ziffern besteht!

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
echo $o->{'123foo'}; // OK!
echo $o->{'123'}; // error!

Live Beispiel .

Fakt Nr. 4: Nun, es sei denn, das Objekt stammt überhaupt nicht aus einem Array.

$a = array('123' => '123');
$o1 = (object)$a;
$o2 = new stdClass;
$o2->{'123'} = '123'; // setting property is OK

echo $o1->{'123'}; // error!
echo $o2->{'123'}; // works... WTF?

Live Beispiel .

Ziemlich intuitiv, stimmst du nicht zu?

Was du tun kannst

Option 1: manuell ausführen

Der praktischste Ansatz besteht darin, das gewünschte Objekt einfach wieder in ein Array umzuwandeln, mit dem Sie auf die Eigenschaften zugreifen können:

$a = array('123' => '123', '123foo' => '123foo');
$o = (object)$a;
$a = (array)$o;
echo $o->{'123'}; // error!
echo $a['123']; // OK!

Dies funktioniert leider nicht rekursiv. In Ihrem Fall müssten Sie also Folgendes tun:

$highlighting = (array)$myVar->highlighting;
$data = (array)$highlighting['448364']->Data;
$value = $data['0']; // at last!

Option 2: die nukleare Option

Ein alternativer Ansatz wäre das Schreiben einer Funktion, die Objekte rekursiv in Arrays konvertiert:

function recursive_cast_to_array($o) {
    $a = (array)$o;
    foreach ($a as &$value) {
        if (is_object($value)) {
            $value = recursive_cast_to_array($value);
        }
    }

    return $a;
}

$arr = recursive_cast_to_array($myVar);
$value = $arr['highlighting']['448364']['Data']['0'];

Ich bin jedoch nicht davon überzeugt, dass dies auf ganzer Linie eine bessere Option ist, da unnötigerweise alle Eigenschaften, an denen Sie nicht interessiert sind, sowie die Eigenschaften, die Sie sind, in Arrays umgewandelt werden.

Option 3: Clever spielen

Eine Alternative zur vorherigen Option ist die Verwendung der integrierten JSON-Funktionen:

$arr = json_decode(json_encode($myVar), true);
$value = $arr['highlighting']['448364']['Data']['0'];

Die JSON-Funktionen führen eine rekursive Konvertierung in ein Array durch, ohne dass externe Funktionen definiert werden müssen. So wünschenswert dies auch aussieht, es hat den "Nuke" -Nachteil von Option 2 und zusätzlich den Nachteil, dass, wenn sich in Ihrem Objekt Zeichenfolgen befinden, diese Zeichenfolgen in UTF-8 codiert werden müssen (dies ist eine Anforderung von json_encode).

Jon
quelle
Es hat auch mein Problem gelöst! stackoverflow.com/questions/4643894/…
Bossliaw
Jon, danke, dass du mich gerettet hast. Mein Problem war jedoch anders (ich denke, es liegt wirklich im Teil "Was sie dir nie erzählt haben"). Das Datetime - Objekt von DB abgerufen scheint in Ordnung , aber wenn ich auf eine ihrer Eigenschaften, wie ->dateoder ->timezonenur nullzurückgegeben wird. Ich habe festgestellt, dass die richtigen Werte zurückgegeben werden, wenn ich das Objekt vor Verwendung dieser Eigenschaften var_dumped habe. Das Klonen behebt das nicht, also hat es wohl wirklich etwas mit dem Zugriff zu tun, der dies var_dumpbewirkt ... Dann sah ich, dass Ihre Option Nr. 1 und voilá, als Array ( $objCastAsArray['date']) darauf zuzugreifen, wie ein Zauber funktionierten.
Armfoot
1
Fakt Nr. 0 : Das Gießen von Arrays in Objekte sollte überhaupt keinen stinkenden Sinn ergeben. Fakt Nr. 1 bis Fakt Nr. 3: nicht erforderlich.
Pacerier
4
@ Pacerier: Ich stimme zu, dass es etwas fragwürdig ist, aber es kann in einigen Situationen durchaus Sinn machen. Wie auch immer, da es im Handbuch dokumentiert ist, so zu arbeiten, spielen unsere persönlichen Meinungen keine Rolle.
Jon
Eine Alternative zu Option 3, die kein UTF-8 erfordert, wäre$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ
10

Ich wollte nur zu Jons beredter Erklärung den Grund hinzufügen, warum dies fehlschlägt. Dies alles liegt daran, dass PHP beim Erstellen eines Arrays - wenn möglich - Schlüssel in Ganzzahlen konvertiert, was zu Suchproblemen bei Arrays führt, die in Objekte umgewandelt wurden, einfach weil der numerische Schlüssel erhalten bleibt. Dies ist problematisch, da alle Eigenschaftszugriffsoptionen Zeichenfolgen erwarten oder in Zeichenfolgen konvertieren. Sie können dies wie folgt bestätigen:

$arr = array('123' => 'abc');
$obj = (object) $arr;
$obj->{'123'} = 'abc';
print_r( $obj );

Welches würde ausgeben:

stdClass Object ( 
  [123] => 'abc', 
  [123] => 'abc'
)

Das Objekt verfügt also über zwei Eigenschaftsschlüssel, einen numerischen (auf den nicht zugegriffen werden kann) und einen auf Zeichenfolgen basierenden. Dies ist der Grund, warum Jon #Fact 4arbeitet, denn wenn Sie die Eigenschaft mit geschweiften Klammern festlegen, definieren Sie immer einen auf Zeichenfolgen basierenden Schlüssel anstelle eines numerischen.

Wenn Sie Jons Lösung nehmen, sie aber auf den Kopf stellen, können Sie aus Ihrem Array ein Objekt generieren, das immer stringbasierte Schlüssel enthält, indem Sie folgende Schritte ausführen:

$obj = json_decode(json_encode($arr));

Von nun an können Sie eine der folgenden Optionen verwenden, da der Zugriff auf diese Weise den Wert in der geschweiften Klammer immer in eine Zeichenfolge konvertiert:

$obj->{123};
$obj->{'123'};

Gutes altes unlogisches PHP ...

Pebbl
quelle
1

Wenn ein Objekt mit @like beginnt :

SimpleXMLElement Object (
    [@attributes] => Array (
        [href] => qwertyuiop.html
        [id] => html21
        [media-type] => application/xhtml+xml
    )
)

Sie müssen verwenden:

print_r($parent_object->attributes());

weil $parent_object->{'@attributes'}oder $parent_object['@attributes']wird nicht funktionieren.

Zydnar
quelle
3 Jahre später und das hilft immer noch Menschen, danke! Während Ihre Antwort mein Problem behebt, fehlt eine Erklärung. Kann jemand den Grund dafür erklären?
Schiedsrichter
1

Ich hatte diese Funktion aus dem Netz kopiert. Versuchen Sie Folgendes, wenn es wie angegeben funktioniert ("Funktion zum Konvertieren von stdClass-Objekten in mehrdimensionale Arrays"):

<?php

    function objectToArray($d) {
        if (is_object($d)) {
            // Gets the properties of the given object
            // with get_object_vars function
            $d = get_object_vars($d);
        }

        if (is_array($d)) {
            /*
            * Return array converted to object
            * Using __FUNCTION__ (Magic constant)
            * for recursive call
            */
            return array_map(__FUNCTION__, $d);
        }
        else {
            // Return array
            return $d;
        }
    }

?>
  • Übergeben Sie zuerst Ihr Array, um zu objectToArrayfunktionieren
  • Nehmen Sie dann den Rückgabewert
  • Echo [highlighting][448364][Data][0]

Quelle: PHP stdClass to Array und Array to stdClass

Ruwantha
quelle
1

Eine letzte Alternative zu Jons umfassender Antwort:

Verwenden Sie einfach json_decode (), wobei der zweite Parameter auf true gesetzt ist .

$array = json_decode($url, true);

Dies gibt dann ein assoziatives Array anstelle eines Objekts zurück, sodass nachträglich keine Konvertierung erforderlich ist.

Dies ist möglicherweise nicht für jede Anwendung geeignet, hat mir jedoch wirklich geholfen, eine Eigenschaft des oroginalen Objekts leicht zu referenzieren.

Die Lösung wurde in diesem Tutorial gefunden - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Grüße

Bluekable
quelle
1

Für PHP 7

Zugriff auf Objekteigenschaften mit Nummern als Eigenschaftsnamen. Wird meistens nach dem Umwandeln des Arrays in ein Objekt benötigt.

    $arr = [2,3,7];
    $o = (object) $arr;

    $t = "1";
    $t2 = 1;
    $t3 = (1);

    echo $o->{1};      // 3
    echo $o->{'1'};   // 3
    echo $o->$t;        // 3
    echo $o->$t2;       // 3
    echo $o->$t3;       // 3

    echo $o->1;       // error
    echo $o->(1);      // error
umesh kadam
quelle
0

Ich fürchte, Sie dürfen keine Objekte benennen, die mit Zahlen beginnen. Benennen Sie den ersten "448364" um, beginnend mit einem Buchstaben.

Das zweite ist ein Array, auf das in Klammern wie folgt zugegriffen werden soll:

print myVar->highlighting->test_448364->Data[0]

stattdessen

Gustav
quelle
Ich kann es nicht ändern. Die Ausgabe wird von einer Anwendung zurückgegeben, auf die ich keinen Einfluss habe.
Avinash Shah