如何使用名称(如整数)访问对象属性?


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,但事实证明这不是骗人的:如果属性名称以数字开头全部为数字,则会有所不同
2012年

@Jon:嗯,很有意思...在我猜之前应该已经进行了测试。谢谢你让我知道!
Felix Kling

Answers:


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!

事实三:但是如果属性名称是全数字,那就不是

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

但是,我并不认为这是一个更好的选择,因为它会不必要地将所有您感兴趣的属性以及您感兴趣的属性强制转换为数组。

选择#3:发挥聪明

上一个选项的替代方法是使用内置的JSON函数:

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

JSON函数有助于对数组进行递归转换,而无需定义任何外部函数。无论看上去多么理想,它都具有选项#2的“ nuke”缺点,另外还有一个缺点,即如果您的对象中有任何字符串,则这些字符串必须以UTF-8编码(这是的要求json_encode)。


碰巧也解决了我的问题!stackoverflow.com/questions/4643894/...
Bossliaw

乔恩,谢谢你救了我。不过,我的问题有所不同(我想这确实是在“他们从未告诉过您”部分)。从数据库检索到的DateTime对象似乎还可以,但是如果我访问其任何属性,例如->date->timezone,则仅null返回。我注意到,如果我在使用这些属性之前var_dumped对象,则会返回正确的值。克隆并不能解决这个问题,所以我想它确实与访问有关var_dump……...然后我看到了您的Option#1和voilá,将其作为数组($objCastAsArray['date'])进行访问就像一种魅力。
Armfoot

1
事实#0:首先将数组转换为对象应该没有臭味。事实1至事实3:不需要。
和平者

4
@Pacerier:我同意这有点可疑,但在某些情况下完全有道理。无论如何,由于手册中已记录了它的工作原理,因此我们的个人意见并不重要。
2015年

不需要UTF-8的选项#3的替代方法是$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
OscarJ,2016年

10

只是想在Jon的雄辩性解释中加上失败的原因。这是因为在创建数组时,php将键转换为整数(如果可以),这会导致对已转换为对象的数组进行查找问题,这仅仅是因为保留了数字键。这是有问题的,因为所有属性访问选项都期望或转换为字符串。您可以通过执行以下操作来确认这一点:

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

将输出:

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

因此,该对象具有两个属性键,一个是数字键(无法访问),另一个是基于字符串的键。这就是Jon #Fact 4工作的原因,因为通过使用花括号设置属性意味着您总是定义基于字符串的键,而不是数字键。

采取Jon的解决方案,但将其付诸实践,您可以通过执行以下操作从数组中生成一个始终具有基于字符串的键的对象:

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

从现在开始,您可以使用以下两种方式之一,因为以这种方式进行的访问始终会将花括号内的值转换为字符串:

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

好的旧的不合逻辑的PHP ...


1

如果对象以@like 开头:

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 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.