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.

Avinash Shah
fonte
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).

Jon
fonte
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 ...

Seixo
fonte
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.

Zydnar
fonte
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

Ruwantha
fonte
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

Bluekable
fonte
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
umesh kadam
fonte
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

Gustav
fonte
Eu não posso mudar isso. A saída é retornada de um aplicativo sobre o qual não tenho controle.
Avinash Shah