Как получить доступ к свойствам объекта с такими именами, как целые числа?

87

Я использую json_decode()что-то вроде:

$myVar = json_decode($data)

Что дает мне такой вывод:

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

Я хочу получить доступ к строковому значению в ключе [0]. Когда я пытаюсь сделать что-то вроде:

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

Я получаю такую ​​ошибку:

Ошибка синтаксического анализа: синтаксическая ошибка, неожиданный T_DNUMBER

Две цифры / целые числа там кажутся проблемой.

Авинаш шах
источник
1
@FelixKling: Я тоже CVED, но на самом деле оказывается, что это не боян: это делает разницу , если имя свойства начинается с числом или это весь номер !
Джон
@Jon: Ммм, интересно ... я должен был сделать тест раньше, чем я предполагаю. Спасибо, что дали мне знать!
Феликс Клинг,

Ответы:

286

Обновлено для PHP 7.2

PHP 7.2 представил изменение поведения при преобразовании числовых ключей в приведения объектов и массивов , которое исправляет это конкретное несоответствие и заставляет все следующие примеры вести себя так, как ожидалось.

Одной проблемой меньше!


Исходный ответ (относится к версиям до 7.2.0)

У PHP есть своя доля темных переулков, внутри которых вы действительно не хотите оказаться. Свойства объекта с именами, которые являются числами, являются одним из них ...

Что они никогда не говорили тебе

Факт №1: вы не можете легко получить доступ к свойствам с именами, которые не являются допустимыми именами переменных.

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

Факт №2: Вы можете получить доступ к таким свойствам с помощью синтаксиса фигурных скобок.

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

Факт №3: Но только не, если имя свойства состоит только из цифр!

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

Живой пример .

Факт №4: Ну, если только объект изначально не был получен из массива.

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

Живой пример .

Довольно интуитивно понятно, согласны?

Что ты можешь сделать

Вариант 1: сделать вручную

Наиболее практичный подход - просто вернуть интересующий вас объект в массив, что позволит вам получить доступ к свойствам:

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

К сожалению, это не работает рекурсивно. Итак, в вашем случае вам нужно будет сделать что-то вроде:

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

Вариант 2: ядерный вариант

Альтернативным подходом было бы написать функцию, рекурсивно преобразующую объекты в массивы:

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 3: сыграть с умом

Альтернативой предыдущему варианту является использование встроенных функций JSON:

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

Функции JSON помогают выполнять рекурсивное преобразование в массив без необходимости определять какие-либо внешние функции. Как бы желательно это ни выглядело, у него есть недостаток «ядерной бомбы» варианта №2 и, кроме того, недостаток, заключающийся в том, что если внутри вашего объекта есть какие-либо строки, эти строки должны быть закодированы в UTF-8 (это требование json_encode).

Джон
источник
Это тоже решило мою проблему! stackoverflow.com/questions/4643894/…
Bossliaw
Джон, спасибо, что спас меня. Однако моя проблема заключалась в другом (я думаю, это действительно было в части «то, что они никогда не говорили вам»). Объект DateTime, полученный из БД, кажется нормальным, но если я получаю доступ к любому из его свойств, например ->dateили ->timezone, nullвозвращается только . Я заметил, что если я var_dumped объект перед использованием этих свойств, возвращаются правильные значения. Клонирование этого не исправляет, поэтому я предполагаю, что это действительно имеет какое-то отношение к доступу, который var_dumpделает ... Затем я увидел ваш вариант №1 и вуаля, доступ к нему как к массиву ( $objCastAsArray['date']) работал как шарм.
Armfoot
1
Факт № 0 : преобразование массивов в объекты не должно иметь никакого вонючего смысла. От Факт №1 к Факт №3: не нужны.
Pacerier
4
@Pacerier: Я согласен, что это несколько сомнительно, но в некоторых ситуациях это может иметь смысл. Во всяком случае, так как это документировано в руководстве к работе , как это, наши личные мнения не имеет большого значения.
Джон
Альтернативой Варианту № 3, который не требует UTF-8, будет$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ
10

Просто хотел добавить к красноречивому объяснению Джона причину, по которой это не удается. Это все потому, что при создании массива php преобразует ключи в целые числа - если это возможно - что вызывает проблемы поиска в массивах, которые были преобразованы в объекты, просто потому, что числовой ключ сохраняется. Это проблематично, потому что все параметры доступа к свойствам ожидают или преобразуются в строки. Вы можете подтвердить это, выполнив следующие действия:

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

Что выведет:

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

Таким образом, у объекта есть два ключа свойств, один числовой (к которому нельзя получить доступ) и один на основе строки. Это причина того, почему Джон #Fact 4работает, потому что установка свойства с помощью фигурных скобок означает, что вы всегда определяете строковый ключ, а не числовой.

Взяв решение Джона, но перевернув его с ног на голову, вы можете сгенерировать объект из вашего массива, который всегда имеет строковые ключи, выполнив следующие действия:

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

С этого момента вы можете использовать любое из следующих действий, поскольку при таком доступе значение внутри фигурной скобки всегда преобразуется в строку:

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

Старый добрый нелогичный PHP ...

Pebbl
источник
1

Если объект начинается с @подобного:

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

Вы должны использовать:

print_r($parent_object->attributes());

потому что $parent_object->{'@attributes'}или $parent_object['@attributes']не будет работать.

Зыднар
источник
3 года спустя, и это все еще помогает людям, спасибо! Хотя ваш ответ решает мою проблему, ему не хватает объяснения. Кто-нибудь может объяснить причину этого?
Arbiter
1

Я скопировал эту функцию из сети. Если все работает так, как написано («Функция преобразования объектов stdClass в многомерные массивы»), попробуйте следующее:

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

?>
  • сначала передайте свой массив objectToArrayфункции
  • затем возьмите возвращаемое значение
  • эхо [highlighting][448364][Data][0]

Источник: PHP stdClass в массив и массив в stdClass

Ruwantha
источник
1

Последняя альтернатива исчерпывающему ответу Джона:

Просто используйте json_decode () со вторым параметром, установленным в значение true .

$array = json_decode($url, true);

Затем это возвращает ассоциативный массив, а не объект, поэтому нет необходимости преобразовывать постфактум.

Это может не подходить для каждого приложения, но действительно помогло мне легко ссылаться на свойство оригинального объекта.

Решение было найдено в этом руководстве - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

С уважением

Bluekable
источник
1

Для PHP 7

Доступ к свойствам объекта, имеющим номера в качестве имени свойства. В основном требуется после преобразования массива в объект.

    $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

Боюсь, вам не разрешено называть объекты, начинающиеся с цифр. Переименуйте первый в «448364», начиная с буквы.

Второй - массив, к ним нужно обращаться с помощью скобок:

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

вместо

Густав
источник
Я не могу это изменить. Результат возвращается из приложения, которое я не контролирую.
авинаш шах