PHP:如何检查对象/数组是否为引用

15

抱歉提问,现在已经很晚了,我无法想出方法来解决...有人可以帮忙吗?

$users = array(
    array(
        "name" => "John",
        "age"   => "20"
    ),
    array(
        "name" => "Betty",
        "age"   => "22"
    )
);

$room = array(
    "furniture" => array("table","bed","chair"),
    "objects"   => array("tv","radio","book","lamp"),
    "users" => &$users
);

var_dump $room 显示:

...
'users' => &
...
这意味着"users"是一个引用。
我想要做这样的事情:
foreach($room as $key => $val) {
    if(is_reference($val)) unset($room[$key]);
}

主要目标是复制数组而不带有任何引用。

这种情况是否可能?

谢谢。


2
第一条评论展示了如何实现:http://www.php.net/manual/en/language.references.spot.php - pritaeas
你想要没有用户键的 $room,对吗?还可能有其他引用吗,还是只有用户? - Gordon
顺便说一句,我会尝试pritaeas发布的示例... - lepe
数组内部更深层次的引用呢?它们需要被剔除吗? - salathe
我已经提交了一个请求报告,以便将来我们可以拥有类似var_ref_count()的东西。我查看了C源代码,似乎实现起来并不困难,因为debug_zval_dump()已经报告了引用计数。让我们看看PHP开发人员会说什么。同时,我想我会采用Chris的答案。感谢大家的帮助。 - lepe
显示剩余3条评论
5个回答

7
您可以通过复制数组,然后逐个更改和测试每个条目来测试多维数组中的引用:
$roomCopy = $room;
foreach ($room as $key => $val) {
  $roomCopy[$key]['_test'] = true;
  if (isset($room[$key]['_test'])) {
    // It's a reference
    unset($room[$key]);
  }
}
unset($roomCopy);

使用您的示例数据,$room['furniture']$roomCopy['furniture']将是不同的数组(因为$roomCopy$room的副本),因此在一个数组中添加新键不会影响另一个数组。但是,$room['users']$roomCopy['users']将引用相同的$users数组(因为复制的是引用而不是数组),因此当我们向$roomCopy['users']添加新键时,它将在$room['users']中可见。

1
有点不太规范的解决方案,但很有创意... +1 - lepe
1
分析pritaeas提供的链接,结果几乎是与这个解决方案相同但更加详细(我仍然更喜欢这种简化编译)。 - lepe

3
我能做到的最好的是测试两个变量以确定一个是否是对另一个的引用:
$x = "something";
$y = &$x;
$z = "something else";

function testReference(&$xVal,&$yVal) {
    $temp = $xVal;
    $xVal = "I am a reference";
    if ($yVal == "I am a reference")  { echo "is reference<br />"; }  else  { echo "is not reference<br />"; }
    $xVal = $temp;
}

testReference($x,$y);
testReference($y,$x);

testReference($x,$z);
testReference($z,$x);

testReference($y,$z);
testReference($z,$y);

但我怀疑它是否有太大的帮助

真正肮脏的方法(也没有经过充分测试):

$x = "something";
$y = &$x;
$z = "something else";

function isReference(&$xVal) {
    ob_start();
    debug_zval_dump(&$xVal);
    $dump = ob_get_clean();
    preg_match('/refcount\((\d*)\)/',$dump,$matches);
    if ($matches[1] > 4) { return true; } else { return false; }
}

var_dump(isReference($x));
var_dump(isReference($y));
var_dump(isReference($z));

要在你的代码中使用这种最后一种方法,你需要像这样做:
foreach($room as $key => $val) {
    if(isReference($room[$key])) unset($room[$key]);
}

由于$val是原始数组元素的副本,因此它从来不是一个引用;而使用&$val使其始终成为引用


对于简单值(字符串,整数等),您的第一种方法可能有效(因为它基本上是Chris发布的内容),但不适用于数组。第二种方法使用debug_zval_dump“refcount”很有趣。但是,在我看来,这几乎与从var_dump结果中解析“&”相同,不同之处在于使用此方法可以获得引用数量。 顺便说一句,将引用作为参数传递到函数中已被弃用。应该是:debug_zval_dump($xVal); - lepe
debug_zval_dump() 在传递引用/传递值方面有些奇怪,文档中有一个完整的块专门讲解这个主题。然而,除非您使用已弃用的传递引用形式,否则 debug_zval_dump() 似乎在副本上工作(具有 refcount 为 1),而不是变量本身... 它就像是旧的传递引用方法的被遗忘的遗迹。 - Mark Baker

1
function var_reference_count(&$xVal) {
    $ao = is_array($xVal)||is_object($xVal);

    if($ao) { $temp= $xVal;    $xVal=array();    }

    ob_start();        
     debug_zval_dump(&$xVal);
    $dump = ob_get_clean();

    if($ao) $xVal=$temp;

    preg_match('/refcount\((\d*)\)/',$dump,$matches);
    return $matches[1] - 3;
}
//-------------------------------------------------------------------------------------------

这适用于大型对象和数组。


1

可能是一些递归的东西。

function removeReferences($inbound)
{
    foreach($inbound as $key => $context)
    {
        if(is_array($context))
        {
            $inbound[$key] = removeReferences($context)
        }elseif(is_object($context) && is_reference($context))
        {
            unset($inbound[$key]); //Remove the entity from the array.
        }
    }
    return $inbound;
}

4
除了is_reference不存在,这就是他想要的 ;) - Chris
是的,我的错。我发了那个帖子后意识到需要找解决方案,但克里斯·史密斯的肮脏方法似乎是唯一的出路。 - RobertPitt

0

如果你想要摆脱递归元素:

<?php
$arr=(object)(NULL); $arr->a=3; $arr->b=&$arr;
//$arr=array('a'=>3, 'b'=>&$arr);
print_r($arr);

$arr_clean=eval('return '.strtr(var_export($arr, true), array('stdClass::__set_state'=>'(object)')).';');
print_r($arr_clean);
?>

输出:

stdClass Object ( [a] => 3 [b] => stdClass Object *RECURSION* ) 
stdClass Object ( [a] => 3 [b] => ) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接