Jak uzyskać dostęp do właściwości obiektów o nazwach takich jak liczby całkowite?

87

Używam json_decode()czegoś takiego:

$myVar = json_decode($data)

Co daje mi następujący wynik:

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

Chcę uzyskać dostęp do wartości ciągu w kluczu [0]. Kiedy próbuję zrobić coś takiego:

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

Otrzymuję ten błąd:

Błąd analizy: błąd składni, nieoczekiwany T_DNUMBER

Wydaje się, że problem dotyczy dwóch liczb / liczb całkowitych.

avinash shah
źródło
1
@FelixKling: Zrobiłem również CV, ale tak naprawdę okazuje się, że nie jest to oszustwo: ma znaczenie, czy nazwa nieruchomości zaczyna się od liczby, czy składa się z samych liczb !
Jon
@Jon: Mmmh, interesujące ... powinienem był zrobić test, zanim myślę. Dzięki, że dałeś mi znać!
Felix Kling

Odpowiedzi:

286

Zaktualizowano dla PHP 7.2

PHP 7.2 wprowadziło zmianę behawioralną dotyczącą konwersji kluczy numerycznych w rzutowaniu obiektów i tablic , co naprawia tę konkretną niespójność i sprawia, że ​​wszystkie poniższe przykłady zachowują się zgodnie z oczekiwaniami.

Jedna rzecz mniej, o którą należy się mylić!


Oryginalna odpowiedź (dotyczy wersji wcześniejszych niż 7.2.0)

PHP ma swój udział w ciemnych zaułkach, w których naprawdę nie chcesz się znaleźć. Właściwości obiektów o nazwach będących liczbami są jedną z nich ...

Czego ci nigdy nie powiedzieli

Fakt nr 1: Nie można łatwo uzyskać dostępu do właściwości o nazwach, które nie są prawidłowymi nazwami zmiennych

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

Fakt # 2: Możesz uzyskać dostęp do takich właściwości za pomocą składni nawiasów klamrowych

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

Fakt # 3: Ale nie, jeśli nazwa właściwości składa się z samych cyfr!

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

Przykład na żywo .

Fakt # 4: No chyba, że ​​obiekt nie pochodzi z tablicy.

$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?

Przykład na żywo .

Całkiem intuicyjny, nie zgadzasz się?

Co możesz zrobić

Opcja nr 1: zrób to ręcznie

Najbardziej praktycznym podejściem jest po prostu rzutowanie interesującego Cię obiektu z powrotem do tablicy, która pozwoli ci uzyskać dostęp do właściwości:

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

Niestety nie działa to rekurencyjnie. Więc w twoim przypadku musisz zrobić coś takiego:

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

Opcja nr 2: opcja jądrowa

Alternatywnym podejściem byłoby napisanie funkcji, która rekurencyjnie konwertuje obiekty na tablice:

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'];

Nie jestem jednak przekonany, że jest to lepsza opcja pod każdym względem, ponieważ niepotrzebnie rzutuje na tablice wszystkie właściwości, którymi nie jesteś zainteresowany, a także te, którymi jesteś.

Opcja nr 3: grając mądrze

Alternatywą dla poprzedniej opcji jest użycie wbudowanych funkcji JSON:

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

Funkcje JSON z łatwością wykonują rekursywną konwersję na tablicę bez konieczności definiowania jakichkolwiek funkcji zewnętrznych. Jakkolwiek wygląda to pożądane, ma tę wadę "nuke", jak opcja # 2, a dodatkowo wadę polegającą na tym, że jeśli wewnątrz obiektu znajdują się jakieś łańcuchy, muszą one być zakodowane w UTF-8 (jest to wymagane json_encode).

Jon
źródło
Zdarzyło się to również rozwiązać mój problem! stackoverflow.com/questions/4643894/…
Bossliaw
Jon, dzięki za uratowanie mnie. Mój problem był jednak inny (wydaje mi się, że tak naprawdę jest w części „czego ci nigdy nie powiedzieli”). Obiekt DateTime pobrany z bazy danych wydaje się być w porządku, ale jeśli uzyskam dostęp do którejkolwiek z jego właściwości, takich jak ->datelub ->timezone, nullzostanie zwrócony tylko . Zauważyłem, że jeśli var_dumped obiekt przed użyciem tych właściwości, zwracane są właściwe wartości. Klonowanie tego nie naprawia, więc myślę, że to naprawdę ma coś wspólnego z dostępem, który var_dumprobi ... Potem zobaczyłem twoją Opcję # 1 i voilá, dostęp do niej jako tablica ( $objCastAsArray['date']) działał jak urok.
Armfoot
1
Fakt # 0 : Przede wszystkim rzucanie tablic na obiekty nie powinno mieć żadnego śmierdzącego sensu. Fakt nr 1 do faktu nr 3: niepotrzebne.
Pacerier
4
@Pacerier: Zgadzam się, że jest to nieco wątpliwe, ale w niektórych sytuacjach może to mieć sens. W każdym razie, ponieważ w podręczniku jest udokumentowane, że działa w ten sposób, nasze osobiste opinie nie mają tak naprawdę znaczenia.
Jon,
Alternatywą dla opcji nr 3, która nie wymaga UTF-8, byłaby$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ
10

Chciałem tylko dodać do wymownego wyjaśnienia Jona powód, dla którego to się nie udaje. Dzieje się tak dlatego, że podczas tworzenia tablicy php konwertuje klucze na liczby całkowite - jeśli to możliwe - co powoduje problemy z wyszukiwaniem tablic, które zostały rzutowane na obiekty, po prostu dlatego, że klucz numeryczny jest zachowany. Jest to problematyczne, ponieważ wszystkie opcje dostępu do właściwości oczekują lub są konwertowane na łańcuchy. Możesz to potwierdzić, wykonując następujące czynności:

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

Który dałoby:

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

Tak więc obiekt ma dwa klucze właściwości, jeden numeryczny (do którego nie można uzyskać dostępu) i jeden oparty na łańcuchu. To jest powód, dla którego Jon's #Fact 4działa, ponieważ ustawienie właściwości za pomocą nawiasów klamrowych oznacza, że ​​zawsze definiujesz klucz oparty na ciągach, a nie numeryczny.

Biorąc rozwiązanie Jona, ale obracając je na głowie, możesz wygenerować obiekt ze swojej tablicy, który zawsze ma klucze oparte na ciągach, wykonując następujące czynności:

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

Od teraz możesz użyć jednego z poniższych, ponieważ dostęp w ten sposób zawsze konwertuje wartość wewnątrz nawiasu klamrowego na ciąg:

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

Dobry stary nielogiczny PHP ...

Pebbl
źródło
1

Jeśli obiekt zaczyna się od @:

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

Musisz użyć:

print_r($parent_object->attributes());

bo $parent_object->{'@attributes'}albo $parent_object['@attributes']nie będzie działać.

Zydnar
źródło
3 lata później, a to nadal pomaga ludziom, dzięki! Twoja odpowiedź rozwiązuje mój problem, ale nie ma wyjaśnienia. Czy ktoś jest w stanie wyjaśnić przyczynę tego?
Arbiter
1

Skopiowałem tę funkcję z sieci. Jeśli działa tak, jak wskazuje („Funkcja konwertująca obiekty stdClass na tablice wielowymiarowe”), wypróbuj następujące rozwiązania:

<?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;
        }
    }

?>
  • najpierw przekaż swoją tablicę do objectToArrayfunkcji
  • następnie weź wartość zwracaną
  • Echo [highlighting][448364][Data][0]

Źródło: PHP stdClass na Array i Array na stdClass

Ruwantha
źródło
1

Ostatnia alternatywa dla wyczerpującej odpowiedzi Jona:

Po prostu użyj json_decode () z drugim parametrem ustawionym na true .

$array = json_decode($url, true);

To następnie zwraca tablicę asocjacyjną zamiast obiektu, więc nie ma potrzeby konwersji po fakcie.

Może to nie być odpowiednie dla każdej aplikacji, ale naprawdę pomogło mi w łatwym odwołaniu się do właściwości obiektu źródłowego.

Rozwiązanie zostało znalezione w tym samouczku - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

pozdrowienia

Bluekable
źródło
1

W przypadku PHP 7

Dostęp do właściwości obiektu, których nazwa jest liczbą. Najczęściej potrzebne po rzutowaniu tablicy na obiekt.

    $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
źródło
0

Obawiam się, że nie możesz nazywać obiektów zaczynających się od liczb. Zmień nazwę pierwszego z nich na „448364”, zaczynając od litery.

Drugi to tablica, do której dostęp należy uzyskać za pomocą nawiasów kwadratowych:

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

zamiast

Gustav
źródło
Nie mogę tego zmienić. Dane wyjściowe są zwracane z aplikacji, nad którą nie mam kontroli.
avinash shah