2

I'm working with a JSON string. I'm converting it to an associative array to find specific values and change those values when a certain key is found (['content']). The depth of the array is always unknown and will always vary.

Here is the function I wrote. It takes an array as an argument and passes it by reference so that the variable itself is modified rather than a copy of it scoped locally to that function.

$json_array = json_decode($json_string, true);

function replace_data(&$json_array, $data='REPLACE TEST')
{
   foreach($json_array as $key => $value) {
       if ($key == 'content' && !is_array($value)) {
           $json_array[$key] = $data;

       } else {
           if (is_array($value)) {
               replace_data($value, $data);
           }
       }
   }

}


replace_data($json_array, "test test test");
var_dump($json_array);

What I'm expecting to happen is every time a key ['content'] is found at no matter what depth, it replaces with that value specified in the $data argument.

But, when I var_dump($json_array) Those values are unchanged.

What am I missing?

user658182
  • 2,148
  • 5
  • 21
  • 36
  • 2
    Needs more references. – Don't Panic Dec 23 '16 at 16:38
  • Not sure what you mean. The PHP manual page states that "Function definitions alone are enough to correctly pass the argument by reference." Where would I add more references? http://php.net/manual/en/language.references.pass.php – user658182 Dec 23 '16 at 16:45
  • References usually complicate the code and better to avoid them. – Timurib Dec 23 '16 at 16:51
  • Sorry for the brief comment. I was sort of trying for a subtle "more cowbell" reference. Hopefully my answer will show what I'm talking about. – Don't Panic Dec 23 '16 at 16:58
  • 1
    My alternate _opinion_ as to avoiding references because they complicate the code is, it's a good idea not to _overuse_ them, but that's true about anything, really. They're generally a perfectly valid way to accomplish things with your code, and your usage here seems reasonable to me. No offense meant, @Timurib. Like I said, just my opinion. – Don't Panic Dec 23 '16 at 17:13
  • You are right, i was too peremptory. References are handy in many tasks. – Timurib Dec 23 '16 at 20:17

3 Answers3

6

With array_walk_recursive:

function replace_data($json_array, $data = 'REPLACE TEST') {
    array_walk_recursive($json_array, function (&$value, $key) use ($data) {
        if (!is_array($value) && $key === 'content') {
            // $value passed by reference
            $value = $data;
        }
    });
    return $json_array;
}

And without references:

function replace_data($json_array, $data = 'REPLACE TEST') {
    foreach ($json_array as $key => $value) {
        if (is_array($value)) {
            $json_array[$key] = replace_data($value, $data);
        } elseif ($key === 'content') {
            $json_array[$key] = $data;
        }
    }
    return $json_array;
}
Timurib
  • 2,735
  • 16
  • 29
1

To expand on my comment, you need another reference here:

foreach($json_array as $key => &$value) {

That way, a reference to the original value is passed when you make the recursive call, rather than the local copy created with the foreach loop.

From the PHP manual entry for foreach:

In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.

You'll also see in the manual that it recommends unsetting the reference to $value after the loop. Even though it probably won't cause any problems if you don't do that in your function, it's best to be in the habit of always unsetting references created in foreach loops like that. It can cause some strange looking problems if you don't.

Community
  • 1
  • 1
Don't Panic
  • 41,125
  • 10
  • 61
  • 80
0

From PHP7.4, "arrow function" syntax offers a clean and short approach.

As leafnodes are iterated, if the key is content, then replace the text, otherwise do not change the value.

There are no returns being used. All mutations are applied directly to the passed in variables prefixed with &.

function replace_data(&$array, $replaceWith = 'REPLACE TEST')
{
   array_walk_recursive(
        $array,
        fn(&$v, $k) => $v = ($k === 'content' ? $replaceWith : $v)
    );
}

replace_data($json_array, "test test test");
var_export($json_array);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136