15

Sorry to ask, its late and I can't figure a way to do it... anyone can help?

$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 shows:

...
'users' => &
...

Which means "users" is a reference.

I would like to do something like this:

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

The main goal is to copy the array WITHOUT any references.

Is that possible?

Thank you.

lepe
  • 24,677
  • 9
  • 99
  • 108
  • 2
    The first comment shows how it can be done: http://www.php.net/manual/en/language.references.spot.php – pritaeas Jun 30 '10 at 09:59
  • You want $room without the users key, right? Can there be other references or would it only be users? – Gordon Jun 30 '10 at 10:05
  • Yea. The problem is that I have a large size array with many cross-references inside of it. And I want to get a part of it but without the references. So in short, the key may be variable. I'm kind of lazy now and I don't want to track back all the current and future references. – lepe Jun 30 '10 at 10:18
  • BTW.. I will try the example that pritaeas posted... – lepe Jun 30 '10 at 10:19
  • What about references at a deeper level within the array, do those need to be culled? – salathe Jun 30 '10 at 12:31
  • I already filed a Request report so someday in the future we could have something like var_ref_count(). I looked at the C source code and it seems it is not difficult to implement as debug_zval_dump() already reports the reference count. Let's see what the PHP devs say. Meanwhile I think I will go with Chris answer. Thank everyone for helping. – lepe Jul 01 '10 at 01:40
  • Is there any reason why you can't parse the output of the existing `debug_zval_dump` function, for the simple case you've presented? – salathe Jul 01 '10 at 08:10
  • Because of 2 reasons: it is not elegant and it is not efficient. Parsing strings always comes with a cost (and even more if you use regexp functions). If there could be a more direct way to do it (as a php core function) of course it would be better. However until that happens and if you don't care so much about performance then parsing is a good option to solve this problem. Its up to the developer. In my case I would prefer not to have that cost in the performance and I would rather keep track of those references in other way. – lepe Jul 02 '10 at 00:56

5 Answers5

7

You can test for references in a multi-dimensional array by making a copy of the array, and then altering and testing each entry in turn:

$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);

With your example data, $room['furniture'] and $roomCopy['furniture'] will be separate arrays (as $roomCopy is a copy of $room), so adding a new key to one won't affect the other. But, $room['users'] and $roomCopy['users'] will be references to the same $users array (as it's the reference that's copied, not the array), so when we add a key to $roomCopy['users'] it is visible in $room['users'].

Chris
  • 10,337
  • 1
  • 38
  • 46
  • 1
    Kind of dirty solution but creative... +1 – lepe Jun 30 '10 at 10:15
  • 1
    Analyzing the link given by pritaeas, it results to be almost the same solution as this one but more extended (I still prefer this reduced compilation). – lepe Jun 30 '10 at 10:26
3

The best I can manage is a test of two variables to determine if one is a reference to the other:

$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);

but I doubt if it's much help

Really dirty method (not well tested either):

$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));

To use this last method in your code, you'd need to do something like:

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

because $val is never a reference as it's a copy of the original array element; and using &$val makes it always a reference

Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • For simple values (string,int,etc.) your first method could work (as it is basically what Chris posted), but not for arrays. The second method its interesting the use of debug_zval_dump "refcount". But IMHO it would be almost the same as parsing out the "&" from var_dump result, with the difference that with this method it is possible to obtain the number of references. BTW, passing references, as argument, into functions is deprecated. It should be: debug_zval_dump($xVal); – lepe Jul 01 '10 at 00:46
  • debug_zval_dump() is rather weird as regards pass-by-reference/pass-by-value, and there's a whole block in the documentation dedicated to that topic. However, unless you use the deprecated form of pass-by-reference, debug_zval_dump() seems to work on a copy (with a refcount of 1) rather than the variable itself... it's like a forgotten vestige of the old method of pass-by-reference – Mark Baker Jul 01 '10 at 07:42
1

something recursive maybe.

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;
}
RobertPitt
  • 56,863
  • 21
  • 114
  • 161
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;
}
//-------------------------------------------------------------------------------------------

This works with HUDGE objects and arrays.

TomX
  • 11
  • 1
0

if you want to get rid of recursive elements:

<?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);
?>

output:

stdClass Object ( [a] => 3 [b] => stdClass Object *RECURSION* ) 
stdClass Object ( [a] => 3 [b] => ) 
diyism
  • 12,477
  • 5
  • 46
  • 46