views:

351

answers:

4

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.

+1  A: 

Reflection API's isPassedByReference might be interested here. For example, here is how I used it in a project:

$refMethod = new ReflectionMethod($class_name,  '__construct');
$params = $refMethod->getParameters();

$re_args = array();

foreach($params as $key => $param)
{
    if ($param->isPassedByReference())
    {
        // it is passed by reference
    }
    else
    {
        // it is not...
    }
}
Sarfraz
Hmm, how would you apply this to the OPs usecase?
Gordon
That may work in a function context. In this case are just arrays with references... so I think it might be not so useful in this case but interesting.
lepe
+3  A: 

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 Smith
Kind of dirty solution but creative... +1
lepe
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
A: 

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
Except `is_reference` doesn't exist. That's what he's after ;)
Chris Smith
yea my bad, i posted that and realised at tried to find a solution but Chris Smith's Dirty method seems the only way
RobertPitt
A: 

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
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 "
lepe
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
Oh I see. Interesting...
lepe