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

2
  • 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 Commented Jun 14, 2013 at 18:38
  • @MichaelSole I get the same effect when using splitByLength outside the class. Commented Jun 14, 2013 at 18:40

3 Answers 3

5
+100

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);
});
Sign up to request clarification or add additional context in comments.

3 Comments

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.
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 ;).)
Because the normal array is the first argument, which is a reference (notice the & the description in the docs)
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];
        // …
    }
}

Comments

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)

1 Comment

PHP yields array_walk() expects parameter 2 to be a valid callback because this is not a valid callback, of course.

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.