0

I have a large data-set that I'm checking the contents of; I do this validation while creating an internal array of the data; to avoid looping over the array again later, I would like the validation to change the contents of the array. Now the problem is that I'm calling the validation routines through call_user_func and this seems to pose some problems with passing by reference. Or maybe I'm doing something else wrong.

Here's a stripped down example:

public function index( )
{
    $arr = array( 
        array('a' => 'aap', 'n' => 'noot', 'm' => 'mies'), 
        array('a' => 'ding', 'b' => 'flof', 'c' => 'bips'), 
        array( 'd' => 'do', 'e' => 're', 'c' => 'mi') 
    );

    $func = array( $this, '_user_func' );

    $errors = 0;
    $new_arr = array();
    foreach ($arr as $key => &$value) {
        $new_arr[$key] = &$value; // Simulate production-code manipulation
        //if ( !$this->_do_callback($func, $new_arr[$key], $key) ) $errors++; // No exception but array not modified afterwards
        if ( !call_user_func( $func, $new_arr[$key], $key ) ) $errors++; // Exception: Parameter 1 to TestRef::user_func() expected to be a reference, value given
    }
    unset($value);
    var_dump($new_arr);
    print_r('Errors: '.$errors);
}

private function _do_callback( $func, array &$row, $row_id )
{
    if ( is_callable( $func ) )
    {
        return call_user_func( $func, $row, $row_id );
    }
    else
    {
        throw new Exception( "Error doing callback. Callback empty or not a callable function." );
    }
}

private function _user_func( &$arr, $index = 0 )
{
    // "Validation" routine
    foreach ($arr as $key => &$value) {
        if ($key == 'b') return FALSE; // Simulate validation error for error count
        $arr[$key] = 'replaced';
    }
    unset($value);
    //var_dump($arr); // Works!
    return TRUE;
}

3 Answers 3

1

i think you're trying to redefine an existing php function which is array_walk. And especially in your case, you'll need array_walk_recursive.

Here's a rewritten (simplified ?) version of your code.

    public function index( )
    {
        $arr = array( 
            array('a' => 'aap', 'n' => 'noot', 'm' => 'mies'), 
            array('a' => 'ding', 'b' => 'flof', 'c' => 'bips'), 
            array( 'd' => 'do', 'e' => 're', 'c' => 'mi') 
        );

        $func = array( $this, '_user_func' );

        var_dump($arr); // BEFORE WALK
        array_walk_recursive($arr, $func);
        var_dump($arr); // AFTER WALK
    }

    /**
     *  Does something to a row from an array (notice the reference)
     */
    private function _user_func( &$rowValue, $rowIndex )
    {
            $rowValue = 'replaced';
    }

You can see this code in action here --> http://ideone.com/LcZKo

Sign up to request clarification or add additional context in comments.

9 Comments

Nice! I even dreamed up the index part the same way.. :) For my case array_walk would be more appropriete I think (instead of recursive), because I operate on each row, not each value of each row. I'll see if I can translate this to the real app.
The problem with this one is that in the production code, a lot of stuff happens in the outer loop before even having a $row to do the callback with. array_walk would only be possible after gathering $arr. But this would mean iterating over the whole set again, which I'm trying to avoid. So indeed: I am trying to rewrite array_walk, sort-of :) How are the PHP guys doing the callback?
Additionally, if a callback fails, I just want to count the error and continue on to the next row. I think if I return false in the callee now, the whole array_walk would stop?
Callbacks --> stackoverflow.com/questions/48947/… By the way, some aspect remain unclear for me : - "if a callback fails" : what do you mean by "fail" ? (error, exception,...) ? Where does the failure come from ? Who/what raise it ? - "If i return fals in the callee" : since you're working with references you don't have return values ? Where/how do you get them ? As you see, you'd better rephrase your question or elaborate more imho.
I don't really see how the "use" keyword could help here (which does not mean it's useless). Based on what you said before, i understand that you need to run some code against some array elements as long as some condition is met. So array_walk is not well suited since you can not exit from the "walk". Here's an attempt to do what what you seem to be looking for --> ideone.com/41gmQ Maybe you could edit this try to suit your need, thus clarifying what you really need.
|
1

Either:

Try changing your foreach loop into this:

foreach ($arr as $key => &$value) {
    $this->_do_callback($func, $value, $key); // No exception but array not modified afterwards
    //call_user_func( $func, $value, $key ); // Exception: Parameter 1 to TestRef::user_func() expected to be a reference, value given
}

unset($value); // avoid memory leak

Or:

Wrap the variable in an array prior to invoking call_user_func.

6 Comments

I tried your first suggestion; it is another way to hide the exception thrown with direct call_user_func calling; but the resulting array still has the original values.
What would wrapping the variable in an array do? (blindly doing it as you suggested yields no results :) )
It preserves the pointer properly. Excuse the mess, but I think this should do it: pastebin.com/VnKNKZGD
Hm, interesting. But it still throws "Parameter 1 to TestRef::_user_func() expected to be a reference, value given"
Passing it directly to the function does seem to work: call_user_func( $func, array(&$value), $key ); It is slightly irritating to have to do "$arr = &$arr[0];" at the start of the callee now, though..
|
0

Have you tried it like this:?

foreach ($arr as $key => &$value) {
        $this->_do_callback($func, $value, $key); // No exception but array not modified afterwards
        //call_user_func( $func, $value, $key ); // Exception: Parameter 1 to TestRef::user_func() expected to be a reference, value given
    }

1 Comment

Yes, this is another way to hide the exception thrown with direct call_user_func calling; but the resulting array still has the original values.

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.