Push into referenced associative, multidimensional array returned by class method

Viewed 26

As part of my ongoing efforts to simplify the legacy codebase for a CodeIgniter3 application, I'm currently running into a problem. In short, I've dealt with an error statement earlier stating:

can't use method return value in write context

which might ring a bell for some readers. Nonetheless, I haven't seen this error since but I suspect that something is still going wrong. In short, I'm trying to push an associative array into another associative, multidimensional array which is the result of a method that returns a reference.

I've set up a system to easily alter the contents of a JSON, which is returned by reference through this method:

/**
 * Function :   items
 * Target   :   Retrieves a reference to the items, decoded
 * 
 * @author  :   Angev
 * @since   :   2.0
 * @version :   1.0
 * 
 * @return      Referenced link to index 'items'.
 */
public function & items()
{
    $list = (is_array($this->data)) ? $this->data : json_decode($this->data, true);
    return $list['items'];

}

In short, the method $this->items() is part of a Model named 'CheckList'. The Checklist corresponds with a data-table in my database, which includes a 'data' column that represents the data belonging to a certain checklist: the JSON I'm trying to alter and which you can see being returned in this method. Another way of seeing the output of $this->items() is that it should return a reference to $this->data['items'].

This goes all and well, I've used this method many times during development - as a shorthand to accessing ['list'] - and it always returns exactly what I need it to return: a multidimensional array filled with unique indexes (strings) that contain the data belonging to each item of the checklist.

The problem however, arises in a method named update_checklist() in particular the following section:

  $this->items()[$uid] = [
                           'parent_id' => $parent['id'],
                           ... ,
                         ];

I'd expect the method to add an index to the array returned by $this->items(), but it doesn't. I'm not quite sure what goes wrong in this context, since I have earlier seen the error message written at the top of this question, but haven't seen it since.

However, no index is added to the array and whenever I do an immediate var_dump($this->items()) afterwards. It just shows the state of the array as it was before the execution of update_checklist().

In search of an answer, I've also tried wrapping the callback in parentheses, but to no avail:

  ( $this->items() )[$uid] = . . .

To temporary fix the problem, I've resorted to a more direct alteration of the ['items'] array by doing the following:

$this->data = json_decode($this->data, true);      
$this->data['items'][$uid] = [
                               'parent_id' => $parent['id'],
                               ... ,
                             ];

Nonetheless, even though the code above works, I'm left wondering what the flaw is in my logic concerning the method reference return of $this->items() and why I cannot use this method when pushing into the referenced array.

How can I write the required changes to make $this->items()[] function as intended? Or I'd be interested in more clarity into the theory behind this structure and why it can't work.

1 Answers

As always, once you start formulating a question, you stumble upon the flaws in your logic. I've read over this question with a colleague and while discussing the solution just magically presented itself. I'll include the answer for future reference to anyone having the same problem.

/**
 * Function :   items
 * Target   :   Retrieves a reference to the items, decoded
 * 
 * @author  :   Angev
 * @since   :   2.0
 * @version :   1.0
 * 
 * @return      Referenced link to index 'items'.
 */
public function & items()
{
    $list = (is_array($this->data)) ? $this->data : json_decode($this->data, true);
    return $list['items'];

}

The problem lies withing the items() method. This method surely returns a reference, but the reference is made to the preliminary variable $list, which in turn has no direct reference to $this->data. So instead of refering to $this->data['items'], the method returns a reference to $list, which is essentially a copy of $this->data, no no real reference.

To fix the problem, the following code was used:

public function & items()
{
    if(!is_array($this->data) ) $this->data = json_decode($this->data, true);
    return $this->data['items'];

}

As expected, the method now returns a reference to the actual data object.

So in short, what I've learned is that if you let a method return a reference, you need to make sure that whatever the method returns is actually a reference instead of a copy of the data you're trying to reference to.

I'll leave this question open for now to allow others to share any knowledge of insights in this matter.

Related