Como acessar propriedades de objetos com nomes como inteiros?


87

Estou usando json_decode()algo como:

$myVar = json_decode($data)

O que me dá uma saída como esta:

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

Desejo acessar o valor da string na chave [0]. Quando tento fazer algo como:

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

Eu recebo este erro:

Erro de análise: erro de sintaxe, T_DNUMBER inesperado

Os dois algarismos / inteiros parece haver problema.



1
@FelixKling: Eu também fiz um CV, mas na verdade não é nenhum engano: faz diferença se o nome da propriedade começa com um número ou é todo números !
— Jon

@Jon: Mmmh, interessante ... deveria ter feito um teste antes, acho. Obrigado por me avisar!
— Felix Kling

Respostas:


286

Atualizado para PHP 7.2

O PHP 7.2 introduziu uma mudança comportamental para converter chaves numéricas em object e array casts , que corrige esta inconsistência em particular e faz com que todos os exemplos a seguir se comportem como esperado.

Uma coisa a menos para se confundir!


Resposta original (aplica-se a versões anteriores a 7.2.0)

PHP tem sua cota de becos sombrios nos quais você realmente não quer se encontrar. Propriedades de objeto com nomes que são números é um deles ...

O que eles nunca te disseram

Fato nº 1: você não pode acessar propriedades com nomes que não sejam nomes de variáveis ​​legais facilmente

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

Fato nº 2: você pode acessar essas propriedades com a sintaxe de chaves

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

Fato nº 3: mas não se o nome da propriedade tiver apenas dígitos!

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

Exemplo ao vivo .

Fato # 4: Bem, a menos que o objeto não venha de um array em primeiro 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?

Exemplo ao vivo .

Muito intuitivo, não concorda?

O que você pode fazer

Opção nº 1: faça manualmente

A abordagem mais prática é simplesmente converter o objeto no qual você está interessado de volta em uma matriz, o que permitirá que você acesse as propriedades:

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

Infelizmente, isso não funciona recursivamente. Portanto, no seu caso, você precisaria fazer algo como:

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

Opção 2: a opção nuclear

Uma abordagem alternativa seria escrever uma função que converte objetos em matrizes recursivamente:

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

No entanto, não estou convencido de que essa seja a melhor opção em todos os aspectos, porque ela lançará em matrizes desnecessariamente todas as propriedades nas quais você não está interessado, assim como aquelas em que você está.

Opção nº 3: jogar com inteligência

Uma alternativa à opção anterior é usar as funções JSON integradas:

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

As funções JSON realizam de forma útil uma conversão recursiva em array sem a necessidade de definir quaisquer funções externas. Por mais desejável que pareça, ele tem a desvantagem "nuke" da opção # 2 e, adicionalmente, a desvantagem de que se houver qualquer string dentro do seu objeto, essas strings devem ser codificadas em UTF-8 (este é um requisito de json_encode).


Aconteceu para resolver meu problema também! stackoverflow.com/questions/4643894/…
— Bossliaw

Jon, obrigado por me salvar. Meu problema era diferente, porém (acho que é realmente na parte "o que eles nunca te contaram"). O objeto DateTime recuperado do banco de dados parece OK, mas se eu acessar qualquer uma de suas propriedades, como ->dateou ->timezone, somente nullserá retornado. Percebi que se var_dumped o objeto antes de usar essas propriedades, os valores apropriados são retornados. A clonagem não corrige isso, então acho que realmente tem a ver com o acesso que var_dumpfaz ... Então eu vi sua Opção # 1 e voilá, acessando-a como um array ( $objCastAsArray['date']) funcionou perfeitamente.
— Armfoot

1
Fato nº 0 : fundir matrizes em objetos não deveria fazer nenhum sentido desagradável em primeiro lugar. Fato nº 1 a Fato nº 3: desnecessário.
— Pacerier

4
@Pacerier: Concordo que é um tanto questionável, mas pode totalmente fazer sentido em algumas situações. De qualquer forma, como está documentado no manual para funcionar assim, nossas opiniões pessoais não importam muito.
— Jon

Uma alternativa para a Opção # 3 que não requer UTF-8 seria$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
— OscarJ

10

Só queria acrescentar à eloqüente explicação de Jon o motivo do fracasso. Tudo porque, ao criar um array, o php converte as chaves em inteiros - se puder - o que causa problemas de pesquisa em arrays que foram convertidos em objetos, simplesmente porque a chave numérica é preservada. Isso é problemático porque todas as opções de acesso de propriedade esperam ou são convertidas em strings. Você pode confirmar isso fazendo o seguinte:

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

O que resultaria em:

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

Portanto, o objeto tem duas chaves de propriedade, uma numérica (que não pode ser acessada) e uma baseada em string. Esta é a razão pela qual Jon's #Fact 4funciona, porque definir a propriedade usando chaves significa que você sempre define uma chave baseada em string, em vez de numérica.

Pegando a solução de Jon, mas virando-a de cabeça para baixo, você pode gerar um objeto de sua matriz que sempre tem chaves baseadas em string, fazendo o seguinte:

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

De agora em diante, você pode usar qualquer um dos seguintes porque o acesso dessa maneira sempre converte o valor dentro da chave em uma string:

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

O bom e velho PHP ilógico ...


1

Se um objeto começa com @:

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

Você tem que usar:

print_r($parent_object->attributes());

porque $parent_object->{'@attributes'}ou $parent_object['@attributes']não vai funcionar.


3 anos depois e isso ainda está ajudando as pessoas, obrigado! Embora sua resposta resolva meu problema, falta uma explicação. Alguém consegue explicar a razão por trás disso?
— Árbitro de

1

Copiei esta função da rede. Se funcionar como diz ("Função para converter objetos stdClass em matrizes multidimensionais"), tente o seguinte:

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

?>
  • primeiro passe seu array para objectToArrayfuncionar
  • então pegue o valor de retorno
  • eco [highlighting][448364][Data][0]

Fonte: PHP stdClass para Array e Array para stdClass


1

Uma alternativa final para a resposta abrangente de Jon:

Simplesmente use json_decode () com o segundo parâmetro definido como verdadeiro .

$array = json_decode($url, true);

Isso então retorna uma matriz associativa em vez de um objeto, portanto, não há necessidade de converter após o fato.

Isso pode não ser adequado para todos os aplicativos, mas realmente me ajudou a referenciar facilmente uma propriedade do objeto original.

A solução foi encontrada neste tutorial - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

Saudações


1

Para PHP 7

Acessando propriedades do objeto tendo números como nome da propriedade. Principalmente necessário após o lançamento da matriz para o 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

0

Infelizmente, você não tem permissão para nomear objetos que começam com números. Renomeie o primeiro "448364" começando com uma letra.

O segundo é uma matriz, que deve ser acessada por colchetes como:

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

em vez de


Eu não posso mudar isso. A saída é retornada de um aplicativo sobre o qual não tenho controle.
— Avinash Shah
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.