【问题标题】:PHP: check if object/array is a referencePHP:检查对象/数组是否为引用
【发布时间】:2011-03-10 01:17:25
【问题描述】:

很抱歉,已经晚了,我想不出办法……有人可以帮忙吗?

$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' => &
...

这意味着“用户”是一个参考。

我想做这样的事情:

foreach($room as $key => $val) {
    if(is_reference($val)) unset($room[$key]);
}

主要目标是复制没有任何引用的数组。

这可能吗?

谢谢。

【问题讨论】:

  • 第一条评论显示了如何做到这一点:php.net/manual/en/language.references.spot.php
  • 你想要 $room 没有用户密钥,对吧?可以有其他参考还是只有用户?
  • 是的。问题是我有一个大数组,里面有很多交叉引用。我想得到它的一部分,但没有参考。所以简而言之,密钥可能是可变的。我现在有点懒惰,我不想追溯所有当前和未来的参考。
  • 顺便说一句..我会尝试 pritaeas 发布的示例...
  • 数组中更深层次的引用呢,需要剔除吗?

标签: php arrays reference pass-by-reference


【解决方案1】:

您可以通过复制数组来测试多维数组中的引用,然后依次更改和测试每个条目:

$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
  • 分析 pritaeas 给出的链接,它的结果与这个几乎相同的解决方案,但更扩展(我仍然更喜欢这种简化的编译)。
【解决方案2】:

我能做到的最好的办法是测试两个变量以确定一个是否是对另一个的引用:

$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);
  • debug_zval_dump() 在传递引用/传递值方面相当奇怪,并且文档中有一个完整的块专门用于该主题。但是,除非您使用已弃用的按引用传递形式,否则 debug_zval_dump() 似乎适用于副本(引用计数为 1)而不是变量本身......这就像旧的传递方法被遗忘的痕迹- 参考
【解决方案3】:

可能是递归的。

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

【讨论】:

  • 除了is_reference 不存在。这就是他所追求的;)
  • 是的,我的错,我发布了它并意识到试图找到解决方案,但 Chris Smith 的 Dirty 方法似乎是唯一的方法
【解决方案4】:
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;
}
//-------------------------------------------------------------------------------------------

这适用于 HUDGE 对象和数组。

【讨论】:

    【解决方案5】:

    如果你想摆脱递归元素:

    <?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] => ) 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      相关资源
      最近更新 更多