2

I am using an answer from How to get random value out of an array to write a function that returns a random item from the array. I modified it to pass by reference and return a reference.

Unfortunately, it doesn't appear to work. Any modifications to the returned object do not persist. What am I doing wrong?

I'm on PHP 5.4 if that makes a difference (Don't ask).

function &random_value(&$array, $default=null)
{
    $k = mt_rand(0, count($array) - 1);
    $return = isset($array[$k])? $array[$k]: $default;
    return $return;
}

Usage...

$companies = array();
$companies[] = array("name" => "Acme Co",  "employees"=> array( "John", "Jane" ));
$companies[] = array("name" => "Inotech",  "employees"=> array( "Bill", "Michael" ));

$x = &random_value($companies);
$x["employees"][] = "Donald";
var_dump($companies);

Output...

array(2) {
  [0] =>
  array(2) {
    'name' =>
    string(7) "Acme Co"
    'employees' =>
    array(2) {
      [0] =>
      string(4) "John"
      [1] =>
      string(4) "Jane"
    }
  }
  [1] =>
  array(2) {
    'name' =>
    string(7) "Inotech"
    'employees' =>
    array(2) {
      [0] =>
      string(4) "Bill"
      [1] =>
      string(7) "Michael"
    }
  }
}

I even copied and pasted the examples from the documentation and none of those worked either. They all output null.

6
  • Client requirement! I can only tell them so many times in so many ways that being on 5.4 is a terrible idea. Commented Sep 21, 2015 at 18:57
  • I know, you said don't ask so of course I had to. Commented Sep 21, 2015 at 19:00
  • Maybe the client will respond to scary graphs. Commented Sep 21, 2015 at 19:01
  • Why are you using references? Are you just trying to return a random array from $companies? Commented Sep 21, 2015 at 19:04
  • @bishop Needs more red! I actually showed them that and it hasn't changed their mind yet. Commented Sep 21, 2015 at 19:13

2 Answers 2

3

The ternary operator creates an implicit copy, which breaks the reference chain. Use an explicit if... else:

function &random_value(&$array, $default=null)
{
    $k = mt_rand(0, count($array) - 1);
    if (isset($array[$k])) {
        return $array[$k];
    } else {
        return $default;
    }
}

As to why, the docs now state:

Note: Please note that the ternary operator is an expression, and that it doesn't evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued in later PHP versions.

See also this bug where the ternary operator actually returns by reference in a foreach context, when it shouldn't.

Sign up to request clarification or add additional context in comments.

6 Comments

I just tried that. I returns the same incorrect result.
It works for me too! I will have to read up on why the ternary operator does that. I wouldn't expect that to happen (obviously!). Thanks!
Hmm. Your function still returns the whole array. He only wants one element doesn't he?
It's how the zval copy-on-write processing happens in the engine, at least up through 5.6. See also this post. I'm not sure what if anything changed in 7. (I doubt it.)
@Jaime: returns only one element for me and 3v4l. Note that each element is an array of two keys: name and employees.
|
3

I'm having trouble wrapping my head around a better function aspect than @bishop as I have just consumed an enormous lunch, however this works:

$x =& $companies[array_rand($companies)];
$x["employees"][] = "Donald";
var_dump($companies);

1 Comment

I would recommend this approach also, but I suppose the mitigating factor is the use of $default for null values, eg: $companies = [ [], ['name' => 'Foo', 'employees' => []] ];

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.