Comment accéder aux propriétés des objets avec des noms comme des entiers?

87

J'utilise json_decode()quelque chose comme:

$myVar = json_decode($data)

Ce qui me donne une sortie comme celle-ci:

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

Je veux accéder à la valeur de chaîne dans la clé [0]. Quand j'essaye de faire quelque chose comme:

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

J'obtiens cette erreur:

Erreur d'analyse: erreur de syntaxe, T_DNUMBER inattendu

Les deux nombres / entiers semblent poser problème.

avinash shah
la source
1
@FelixKling: J'ai aussi CVed, mais il s'avère que ce n'est pas dupe: cela fait une différence si le nom de la propriété commence par un nombre ou est composé de nombres !
Jon
@Jon: Mmmh, intéressant ... j'aurais dû faire un test avant je suppose. Merci de me le faire savoir!
Felix Kling

Réponses:

286

Mis à jour pour PHP 7.2

PHP 7.2 a introduit un changement de comportement dans la conversion des clés numériques dans les casts d'objets et de tableaux , ce qui corrige cette incohérence particulière et fait que tous les exemples suivants se comportent comme prévu.

Une chose de moins à confondre!


Réponse d'origine (s'applique aux versions antérieures à 7.2.0)

PHP a son lot de ruelles sombres dans lesquelles vous ne voulez vraiment pas vous retrouver. Les propriétés d'objet avec des noms qui sont des nombres en font partie ...

Ce qu'ils ne t'ont jamais dit

Fait n ° 1: vous ne pouvez pas accéder facilement aux propriétés avec des noms qui ne sont pas des noms de variables légaux

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

Fait n ° 2: Vous pouvez accéder à ces propriétés avec la syntaxe d'accolades

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

Fait n ° 3: Mais pas si le nom de la propriété est composé de chiffres!

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

Exemple en direct .

Fait # 4: Eh bien, à moins que l'objet ne provienne d'un tableau en premier lieu.

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

Exemple en direct .

Assez intuitif, n'êtes-vous pas d'accord?

Ce que tu peux faire

Option 1: faites-le manuellement

L'approche la plus pratique est simplement de reconstituer l'objet qui vous intéresse dans un tableau, ce qui vous permettra d'accéder aux propriétés:

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

Malheureusement, cela ne fonctionne pas de manière récursive. Donc, dans votre cas, vous devez faire quelque chose comme:

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

Option n ° 2: l'option nucléaire

Une approche alternative serait d'écrire une fonction qui convertit les objets en tableaux de manière récursive:

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

Cependant, je ne suis pas convaincu que ce soit une meilleure option dans tous les domaines, car il sera inutilement converti en tableaux toutes les propriétés qui ne vous intéressent pas ainsi que celles qui vous intéressent.

Option 3: jouer intelligemment

Une alternative à l'option précédente consiste à utiliser les fonctions JSON intégrées:

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

Les fonctions JSON effectuent utilement une conversion récursive en tableau sans avoir besoin de définir de fonctions externes. Aussi souhaitable que cela puisse paraître, il présente l'inconvénient "nuke" de l'option n ° 2 et en plus l'inconvénient que s'il y a des chaînes dans votre objet, ces chaînes doivent être encodées en UTF-8 (c'est une exigence de json_encode).

Jon
la source
C'est arrivé pour résoudre mon problème aussi! stackoverflow.com/questions/4643894/…
Bossliaw
Jon, merci de m'avoir sauvé. Mon problème était différent cependant (je suppose que c'est vraiment dans la partie "ce qu'ils ne vous ont jamais dit"). L'objet DateTime récupéré à partir de la base de données semble OK, mais si j'accède à l'une de ses propriétés, telles que ->dateou ->timezone, seule nullest retournée. J'ai remarqué que si je var_dumped l'objet avant d'utiliser ces propriétés, les valeurs appropriées sont renvoyées. Le clonage ne résout pas cela, donc je suppose que cela a vraiment quelque chose à voir avec l'accès qui le var_dumpfait ... Ensuite, j'ai vu votre option n ° 1 et voilá, y accéder en tant que tableau ( $objCastAsArray['date']) fonctionnait comme un charme.
Armfoot
1
Fait n ° 0 : la conversion de tableaux en objets ne devrait pas avoir de sens puant en premier lieu. Fait n ° 1 à fait n ° 3: inutile.
Pacerier
4
@Pacerier: Je conviens que c'est un peu discutable, mais cela peut tout à fait avoir un sens dans certaines situations. Quoi qu'il en soit, comme il est documenté dans le manuel pour fonctionner comme ça, nos opinions personnelles n'ont pas vraiment d'importance.
Jon
Une alternative à l'option n ° 3 qui ne nécessite pas UTF-8 serait$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ
10

Je voulais juste ajouter à l'explication éloquente de Jon la raison pour laquelle cela échoue. Tout cela parce que lors de la création d'un tableau, php convertit les clés en entiers - s'il le peut - ce qui pose des problèmes de recherche sur les tableaux qui ont été convertis en objets, simplement parce que la clé numérique est conservée. Ceci est problématique car toutes les options d'accès aux propriétés s'attendent ou se convertissent en chaînes. Vous pouvez le confirmer en procédant comme suit:

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

Ce qui produirait:

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

Ainsi, l'objet a deux clés de propriété, une numérique (inaccessible) et une basée sur une chaîne. C'est la raison pour laquelle Jon #Fact 4fonctionne, car en définissant la propriété à l'aide d'accolades, cela signifie que vous définissez toujours une clé basée sur une chaîne, plutôt que numérique.

En prenant la solution de Jon, mais en la tournant sur sa tête, vous pouvez générer un objet à partir de votre tableau qui a toujours des clés basées sur des chaînes en procédant comme suit:

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

À partir de maintenant, vous pouvez utiliser l'un des éléments suivants, car l'accès de cette manière convertit toujours la valeur à l'intérieur de l'accolade en chaîne:

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

Bon vieux PHP illogique ...

Pebbl
la source
1

Si un objet commence par @comme:

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

Vous devez utiliser:

print_r($parent_object->attributes());

parce que $parent_object->{'@attributes'}ou $parent_object['@attributes']ne fonctionnera pas.

Zydnar
la source
3 ans plus tard et cela aide encore les gens, merci! Bien que votre réponse corrige mon problème, elle manque d'explication. Quelqu'un est-il en mesure d'expliquer la raison de cela?
Arbiter
1

J'avais copié cette fonction sur le net. Si cela fonctionne comme indiqué ("Fonction de conversion d'objets stdClass en tableaux multidimensionnels"), essayez ce qui suit:

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

?>
  • passez d'abord votre tableau à objectToArrayfonctionner
  • puis prenez la valeur de retour
  • écho [highlighting][448364][Data][0]

Source: PHP stdClass vers Array et Array vers stdClass

Ruwantha
la source
1

Une dernière alternative à la réponse complète de Jon:

Utilisez simplement json_decode () avec le deuxième paramètre défini sur true .

$array = json_decode($url, true);

Cela renvoie alors un tableau associatif plutôt qu'un objet, donc pas besoin de convertir après coup.

Cela peut ne pas convenir à toutes les applications, mais cela m'a vraiment aidé à référencer facilement une propriété de l'objet oroginal.

La solution a été trouvée dans ce tutoriel - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Cordialement

Bluekable
la source
1

Pour PHP 7

Accès aux propriétés d'objets ayant des nombres comme nom de propriété. Principalement nécessaire après la conversion du tableau en objet.

    $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
la source
0

J'ai peur que vous ne soyez pas autorisé à nommer des objets en commençant par des chiffres. Renommez le premier "448364" en commençant par une lettre.

Le second est un tableau, ceux-ci sont accessibles par des crochets comme ceci:

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

au lieu

Gustav
la source
Je ne peux pas le changer. La sortie est renvoyée à partir d'une application dont je n'ai aucun contrôle.
avinash shah