Search code examples
phparraysreferencetraversal

Empty array after passing array by reference to array_walk()


I have made this little class:

class Analyzer {
    public static function analyze($phrases) {
        $sortedPhrases = array();
        array_walk($phrases, array('self', 'splitByLength'), $sortedPhrases);
        var_dump($sortedPhrases);
    }

    private static function splitByLength($item, $key, &$resArr) {
        // line stolen from here: http://stackoverflow.com/a/4786840/603003
        // thanks to arnaud576875 <http://stackoverflow.com/users/576875/arnaud576875>
        $len = count( preg_split('#\PL+#u', $item, -1, PREG_SPLIT_NO_EMPTY) );
        if (!isset($resArr[$len])) {
            $resArr[$len] = array();
        }
        $resArr[$len][] = $item;

        var_dump($resArr);
    }
}

$phrases = array(
    "I can't believe the great content",
    "I can't understand the superior information",
    "I can't comprehend the amazing data",
    "I cannot analyze the amazing data",
    "I haven't written to the amazing data"
);
Analyzer::analyze($phrases);

Executing the script results in the output below:

array (size=1)
  7 => 
    array (size=1)
      0 => string 'I can't believe the great content' (length=33)

...

array (size=3)
  7 => 
    array (size=3)
      0 => string 'I can't believe the great content' (length=33)
      1 => string 'I can't understand the superior information' (length=43)
      2 => string 'I can't comprehend the amazing data' (length=35)
  6 => 
    array (size=1)
      0 => string 'I cannot analyze the amazing data' (length=33)
  8 => 
    array (size=1)
      0 => string 'I haven't written to the amazing data' (length=37)

array (size=0)
  empty

All outputs are actually correct except the last one which comes from Analyzer::analyze(). It seems that the variable $sortedPhrases is somehow cleared after array_walk().


Solution

  • Take a better look at array_walk's documentation page.

    userdata

    If the optional userdata parameter is supplied, it will be passed as the third parameter to the callback funcname.

    That's the third parameter. It's not a reference, it's just a value that gets passed to your callback function.


    One (of many) solutions to your problem is to use an object instead (objects are always passed by reference):

    class Analyzer {
        public static function analyze($phrases) {
            $arrObj = new ArrayObject();
            array_walk($phrases, array('self', 'splitByLength'), $arrObj);
            var_dump($arrObj->getArrayCopy());
        }
    
        private static function splitByLength($item, $key, $arrObj) {
            // line stolen from here: http://stackoverflow.com/a/4786840/603003
            // thanks to arnaud576875 <http://stackoverflow.com/users/576875/arnaud576875>
            $len = count( preg_split('#\PL+#u', $item, -1, PREG_SPLIT_NO_EMPTY) );
            if (!isset($arrObj[$len])) {
                $arrObj[$len] = array();
            }
            $arrObj[$len][] = $item;
    
            var_dump($arrObj->getArrayCopy());
        }
    }
    

    (it doesn't have to be an ArrayObject, it can be a stdClass object with an array property, or create your own class if you want...)


    Or you can wrap your call within an anonymous function if you really want to work with a reference:

    $static = get_called_class();
    array_walk($phrases, function($item, $key) use($static, &$sortedPhrases){
      $static::splitByLength($item, $key, $sortedPhrases);
    });