¿Cómo acceder a las propiedades de los objetos con nombres como números enteros?

87

Estoy usando json_decode()algo como:

$myVar = json_decode($data)

Lo que me da un resultado como este:

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

Quiero acceder al valor de la cadena en la clave [0]. Cuando intento hacer algo como:

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

Me sale este error:

Error de análisis: error de sintaxis, T_DNUMBER inesperado

Los dos números / enteros parece haber un problema.

avinash shah
fuente
1
@FelixKling: También hice CV, pero en realidad resulta que no es un engaño: ¡ hace una diferencia si el nombre de la propiedad comienza con un número o es todo números !
Jon
@Jon: Mmmh, interesante ... debería haber hecho una prueba antes, supongo. ¡Gracias por hacérmelo saber!
Felix Kling

Respuestas:

286

Actualizado para PHP 7.2

PHP 7.2 introdujo un cambio de comportamiento en la conversión de claves numéricas en conversiones de objetos y matrices , que corrige esta inconsistencia particular y hace que todos los siguientes ejemplos se comporten como se esperaba.

¡Una cosa menos por la que estar confundido!


Respuesta original (se aplica a versiones anteriores a la 7.2.0)

PHP tiene su parte de callejones oscuros en los que realmente no quieres encontrarte dentro. Las propiedades de objetos con nombres que son números es una de ellas ...

Lo que nunca te dijeron

Hecho # 1: No puede acceder fácilmente a propiedades con nombres que no son nombres de variables legales

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

Hecho # 2: Usted puede acceder a estas propiedades con la sintaxis de corchete

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

Hecho # 3: ¡ Pero no si el nombre de la propiedad es todo dígitos!

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

Ejemplo vivo .

Hecho # 4: Bueno, a menos que el objeto no provenga de una matriz en primer lugar.

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

Ejemplo vivo .

Bastante intuitivo, ¿no estás de acuerdo?

Lo que puedes hacer

Opción # 1: hazlo manualmente

El enfoque más práctico es simplemente convertir el objeto que le interesa de nuevo en una matriz, lo que le permitirá acceder a las propiedades:

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

Desafortunadamente, esto no funciona de forma recursiva. Entonces, en su caso, necesitaría hacer algo como:

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

Opción # 2: la opción nuclear

Un enfoque alternativo sería escribir una función que convierta objetos en matrices de forma recursiva:

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

Sin embargo, no estoy convencido de que esta sea una mejor opción en todos los ámbitos porque, innecesariamente, enviará a matrices todas las propiedades que no le interesan y las que le interesan.

Opción n. ° 3: jugar de manera inteligente

Una alternativa a la opción anterior es utilizar las funciones JSON integradas:

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

Las funciones JSON realizan de manera útil una conversión recursiva a una matriz sin la necesidad de definir funciones externas. Por muy deseable que parezca, tiene la desventaja "nuclear" de la opción # 2 y además la desventaja de que si hay cadenas dentro de su objeto, esas cadenas deben estar codificadas en UTF-8 (este es un requisito de json_encode).

Jon
fuente
¡Pasó para resolver mi problema también! stackoverflow.com/questions/4643894/…
Bossliaw
Jon, gracias por salvarme. Sin embargo, mi problema era diferente (supongo que en realidad está en la parte de "lo que nunca te dijeron"). El objeto DateTime recuperado de DB parece correcto, pero si accedo a cualquiera de sus propiedades, como ->dateo ->timezone, solo nullse devuelve. Me di cuenta de que si var_dumped el objeto antes de usar estas propiedades, se devuelven los valores adecuados. La clonación no soluciona esto, así que supongo que realmente tiene algo que ver con el acceso que var_dumpsí ... Entonces vi tu Opción # 1 y voilá, acceder a ella como una matriz ( $objCastAsArray['date']) funcionó como un encanto.
Armfoot
1
Hecho # 0 : Lanzar matrices en objetos no debería tener ningún sentido apestoso en primer lugar. Hecho # 1 al Hecho # 3: innecesario.
Pacerier
4
@Pacerier: Estoy de acuerdo en que es algo cuestionable, pero puede tener sentido en algunas situaciones. De todos modos, dado que está documentado en el manual para funcionar así, nuestras opiniones personales realmente no importan.
Jon
Una alternativa a la Opción # 3 que no requiere UTF-8 sería$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ
10

Solo quería agregar a la elocuente explicación de Jon la razón por la que esto falla. Todo se debe a que al crear una matriz, php convierte claves en números enteros, si puede, lo que causa problemas de búsqueda en matrices que se han convertido en objetos, simplemente porque se conserva la clave numérica. Esto es problemático porque todas las opciones de acceso a la propiedad esperan o se convierten en cadenas. Puede confirmar esto haciendo lo siguiente:

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

Que daría como resultado:

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

Entonces, el objeto tiene dos claves de propiedad, una numérica (a la que no se puede acceder) y una basada en cadena. Esta es la razón por la que Jon's #Fact 4funciona, porque al establecer la propiedad con llaves significa que siempre define una clave basada en cadenas, en lugar de numérica.

Tomando la solución de Jon, pero dándole la vuelta, puede generar un objeto de su matriz que siempre tiene claves basadas en cadenas haciendo lo siguiente:

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

De ahora en adelante, puede usar cualquiera de los siguientes, porque el acceso de esta manera siempre convierte el valor dentro de la llave en una cadena:

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

El buen PHP ilógico ...

Pebbl
fuente
1

Si un objeto comienza con @like:

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

Tienes que usar:

print_r($parent_object->attributes());

porque $parent_object->{'@attributes'}o $parent_object['@attributes']no funcionará.

Zydnar
fuente
3 años después y esto sigue ayudando a la gente, ¡gracias! Si bien su respuesta soluciona mi problema, carece de una explicación. ¿Alguien puede explicar la razón detrás de esto?
Árbitro
1

Había copiado esta función de la red. Si funciona como dice ("Función para convertir objetos stdClass en matrices multidimensionales"), intente lo siguiente:

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

?>
  • primero pasa tu matriz a la objectToArrayfunción
  • luego toma el valor de retorno
  • eco [highlighting][448364][Data][0]

Fuente: PHP stdClass a Array y Array a stdClass

Ruwantha
fuente
1

Una alternativa final a la respuesta integral de Jon:

Simplemente use json_decode () con el segundo parámetro establecido en verdadero .

$array = json_decode($url, true);

Esto luego devuelve una matriz asociativa en lugar de un objeto, por lo que no es necesario convertir después del hecho.

Puede que esto no sea adecuado para todas las aplicaciones, pero realmente me ayudó a hacer referencia fácilmente a una propiedad del objeto original.

Se encontró una solución en este tutorial: http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Saludos

Bluekable
fuente
1

Para PHP 7

Acceso a las propiedades del objeto que tienen números como nombre de propiedad. Se necesita principalmente después de lanzar una matriz a un objeto.

    $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
fuente
0

Me temo que no está permitido nombrar objetos que comiencen con números. Cambie el nombre del primero a "448364" comenzando con una letra.

El segundo es una matriz, a las que se debe acceder mediante corchetes así:

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

en lugar

Gustav
fuente
No puedo cambiarlo. La salida se devuelve desde una aplicación de la que no tengo control.
avinash shah