如何访问具有整数或无效属性名称的对象属性?
如何访问具有整数或无效属性名称的对象属性?
我正在使用json_decode()
,类似于:\n
$myVar = json_decode($data)
\n这会给我输出如下:\n
[highlighting] => stdClass Object ( [448364] => stdClass Object ( [Data] => Array ( [0] => 税额责任是.......
\n我想访问键[0]中的字符串值。当我尝试像这样做时:\n
print $myVar->highlighting->448364->Data->0;
\n我得到了这个错误:\n
\n解析错误:语法错误,意外的T_DNUMBER\n
\n那两个数字似乎是问题所在。
在PHP 7中,如果对象属性的名称是数字,访问该属性会出现问题。这通常发生在将数组转换为对象后。
解决这个问题的方法是使用花括号语法来访问属性。例如,可以使用$o->{1}来访问属性值。下面是一个示例代码:
$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
通过使用花括号语法,可以成功访问属性值。但是,如果直接使用$o->1或$o->(1)来访问属性,将会出现错误。因此,为了避免出现错误,应该始终使用花括号语法来访问属性。
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的不合理之处...
解决方法就是使用json_encode和json_decode进行对象和数组之间的转换,以避免出现无法访问属性的问题。
PHP 7.2引入了一个行为变化,用于转换对象和数组转换中的数字键,修复了这个特定的不一致性,并使所有以下示例都能按预期运行。一个让人困惑的事情少了!
PHP中的对象属性名称为数字是其中之一...以下是问题的原因和解决方法。
事实1:您无法轻松访问名称不是合法变量名称的属性
$a = array('123' => '123', '123foo' => '123foo'); $o = (object)$a; echo $o->123foo; // 错误
事实2:您可以使用花括号语法访问此类属性
$a = array('123' => '123', '123foo' => '123foo'); $o = (object)$a; echo $o->{'123foo'}; // 正确!
事实3:但是,如果属性名称全是数字,就不可以使用花括号语法访问
$a = array('123' => '123', '123foo' => '123foo'); $o = (object)$a; echo $o->{'123foo'}; // 正确! echo $o->{'123'}; // 错误!
事实4:好吧,除非对象一开始就不是来自数组。
$a = array('123' => '123'); $o1 = (object)$a; $o2 = new stdClass; $o2->{'123'} = '123'; // 设置属性是可以的 echo $o1->{'123'}; // 错误! echo $o2->{'123'}; // 可以... 什么鬼?
以上是原始回答,适用于早于7.2.0版本的PHP。
解决方法:
选择1:手动操作
最实用的方法是将您感兴趣的对象重新转换为数组,以便访问属性:
$a = array('123' => '123', '123foo' => '123foo'); $o = (object)$a; $a = (array)$o; echo $o->{'123'}; // 错误! echo $a['123']; // 正确!
不幸的是,这不会递归地工作。因此,在您的情况下,您需要执行类似以下的操作:
$highlighting = (array)$myVar->highlighting; $data = (array)$highlighting['448364']->Data; $value = $data['0']; // 最终获得结果!
选择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的“核弹”缺点,并且还有一个缺点,即如果对象中有任何字符串,那些字符串必须以UTF-8编码(这是 `json_encode` 的要求)。
选项3的另一种替代方法是不需要UTF-8的$o = unserialize('O:8:"StdClass"' . substr(serialize($a),1));
。
总之,尽管这种行为在某些情况下可能有些可疑,但它在某些情况下完全有意义。无论如何,在手册中有记录,工作方式如此,我们个人的意见并不重要。
关于选项3是否存在另一种访问这些数字的方法,手册中指出:“除非迭代”,否则无法访问具有数字键的属性。