Як отримати доступ до властивостей об’єкта з такими іменами, як цілі числа?


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: Я також переглянув резюме, але насправді виявляється, що це не обман: це має значення, якщо назва властивості починається з числа або є всі числа !
Джон

@ Джон: Мммм, цікаво ... повинен був зробити тест, перш ніж я здогадуюсь. Дякую що дали мені знати!
Фелікс Клінг,

Відповіді:


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!

Факт No3: Але ні, якщо назва власності - це всі цифри!

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

Живий приклад .

Досить інтуїтивно, ви не згодні?

Що ти можеш зробити

Варіант No1: зробіть це вручну

Найпрактичніший підхід - просто повернути об’єкт, який вас цікавить, назад у масив, що дозволить отримати доступ до властивостей:

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

Однак я не впевнений, що це кращий варіант, оскільки він без необхідності передасть у масиви всі властивості, які вас не цікавлять, а також ті, що вас цікавлять.

Варіант No3: грати розумно

Альтернативою попереднього варіанту є використання вбудованих функцій 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 і voilá, отримавши доступ до нього як масив ( $objCastAsArray['date']) працював як шарм.
Armfoot

1
Факт № 0 : Перекидання масивів у об’єкти спочатку не повинно мати ніякого смердючого сенсу. Факт №1 до Факту №3: непотрібний.
Pacerier

4
@Pacerier: Я згоден, що це дещо сумнівне, але в деяких ситуаціях це може цілком мати сенс. У будь-якому випадку, оскільки в посібнику зафіксовано працювати так, наші особисті думки насправді не мають значення.
Джон

Альтернативою варіанту $o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
No3

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


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 роки, і це все ще допомагає людям, дякую! Хоча ваша відповідь вирішує мою проблему, вона не має пояснень. Хтось може пояснити причину цього?
Арбітр

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 до Array та Array до stdClass


1

Остаточна альтернатива вичерпній відповіді Джона:

Просто використовуйте json_decode (), а другий параметр має значення true .

$array = json_decode($url, true);

Потім це повертає асоціативний масив, а не об’єкт, тому немає необхідності перетворювати фактично.

Це може не підходити для будь-якої програми, але це дійсно допомогло мені легко вказати властивість первинного об'єкта.

Рішення було знайдено в цьому підручнику - http://nitschinger.at/Handling-JSON-like-a-boss-in-PHP/

З повагою


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]

натомість


Я не можу змінити це. Вихідні дані повертаються з програми, у якої я не маю контролю.
avinash shah
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.