1

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().

ComFreek
  • 29,044
  • 18
  • 104
  • 156
  • Make your private function public and test it on a specific data if it works you must not be passing the correctly value to your callback – Michael Sole Jun 14 '13 at 18:38
  • @MichaelSole I get the same effect when using *splitByLength* outside the class. – ComFreek Jun 14 '13 at 18:40

3 Answers3

5

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);
});
nice ass
  • 16,471
  • 7
  • 50
  • 89
  • 1
    Hats off! I was fiddling with the code and reached the conclusion: the extra parameter passed to the map function could not be a reference. Didn't know about ArrayObject, very interesting. Another possible solution could be using a static member variable, but the ArrayObject solution is much simpler. – Diego Pino Jun 16 '13 at 21:04
  • Thanks, it works perfectly now! But I'm still asking myself why the normal array does not lose its value while *walking* over the array. The last call to the callback function actually dumps (all correctly) gathered values. I think that it must has something to do with the internal implementation. (*You get your bounty in 23 hours ;).*) – ComFreek Jun 16 '13 at 21:11
  • Because the normal array is the first argument, which is a reference (notice the `&` the description in the docs) – nice ass Jun 16 '13 at 21:15
2

While the third argument sent to the callback function cannot be a reference itself, it can contain references.

A different approach (to using objects or wrapping your callback in a closure) is to pass an array to the callback, containing a reference to the variable you wish to update. The wrapping array gets passed by-value, but all it contains is the referenced value that you want to change.

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

    private static function splitByLength($item, $key, $extra_args) {
        $resArr = &$extra_args[0];
        // …
    }
}
salathe
  • 51,324
  • 12
  • 104
  • 132
1

Pretty sure this is wrong:

array_walk($phrases, array('self', 'splitByLength'), $sortedPhrases);

So, Try this:

$sortedPhrases = array_walk($phrases, 'splitByLength');

And return your values in $sortedPhrases and don't use pass by reference (personal thing)

pattyd
  • 5,927
  • 11
  • 38
  • 57
Michael Sole
  • 124
  • 4
  • PHP yields `array_walk() expects parameter 2 to be a valid callback` because this is not a valid callback, of course. – ComFreek Jun 14 '13 at 18:44