3

I'm trying to add a button (its function is beyond the scope of this question, suffice it to say that it needs the refund id as an argument) to each refund line in a WooCommerce order. I found that those lines are created in woocommerce\includes\admin\meta-boxes\views\html-order-refund.php which can't be overridden. However there is an action:

do_action( 'woocommerce_admin_order_item_values', null, $refund, $refund->get_id() );

This seemed perfect for my purpose, so I tried adding the following code to functions.php to test if it could work:

add_filter('woocommerce_admin_order_item_values', 'test_refund_id');
function test_refund_id($nullvar, $refund, $refund_id) {
    if ( !empty($refund->refunded_by) ){
        echo '<td>Refund ID: '.$refund_id.'</td>';  
    }
}

I used if ( !empty($refund->refunded_by) ) because woocommerce_admin_order_item_values also gets called in html-order-shipping.php, html-order-item.php and html-order-fee.php, with an "order item" object (instead of a "refund" object) as the second argument, so there's no refunded_by property and my instruction should be ignored.

Much to my disappointment, though, I get the following error:

Fatal error: Uncaught ArgumentCountError: Too few arguments to function test_refund_id(), 1 passed in \wp-includes\class-wp-hook.php on line 290 and exactly 3 expected in \wp-content\themes\mytheme\functions.php:1087

I know that, as a matter of fact, three arguments are passed in the line I quoted above (namely a null, the refund object and the refund id), but I went and checked the script the error was referring to (\wp-includes\class-wp-hook.php) around line 290, and I found this in the apply_filters() function:

        // Avoid the array_slice if possible.
        if ( $the_['accepted_args'] == 0 ) {
            $value = call_user_func( $the_['function'] );
        } elseif ( $the_['accepted_args'] >= $num_args ) {
            $value = call_user_func_array( $the_['function'], $args );
        } else {
            $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int) $the_['accepted_args'] ) );
        }

Just for testing purposes I tried to temporarily change the last "else" clause, replacing the "sliced" arguments array with the original one, thereby forcing the script to keep all the arguments anyway, so array_slice( $args, 0, (int) $the_['accepted_args'] ) became simply $args. Surprise, surprise, now everything works smoothly!

enter image description here

Now, I'm well aware that's of course not a solution since I can't edit a core WP script. I would just want to understand WHY apply_filters() thinks that the accepted arguments are less than the passed ones and therefore it trims the argument array down to just the first one.

Thanks a lot in advance to anyone who'd like to shed some light on the matter.

  • You have to use add_action instead of add_filter. – Chetan Vaghela Dec 10 '19 at 12:09
3

Two inherent problems with your script as posted. I'm not certain this will solve your problem, but it's too big to address in the comments.

First, you need to use add_action(), not add_filter(). That by itself is not a huge deal because add_action() is just a wrapper for add_filter(), but you should still use the correct one.

The other problem may be your passed arguments.

I know that, as a matter of fact, three arguments are passed in the line I quoted above (namely a null, the refund object and the refund id),

Actually, the way you have it, you're not passing three arguments, you're only passing one. But your function as written is expecting three.

Your add_action() uses the required $tag and $function_to_add values, but leaves off the optional $priority and $accepted_args. The default of $accepted_args is "1", so anytime you're passing more than a single value with add_action() or add_filter(), you must define the $accepted_args value.

Try it as follows, noting the final value of "3" - that's the $accepted_args value that tells it to pass more than 1 argument:

add_action('woocommerce_admin_order_item_values', 'test_refund_id', 10, 3 );
function test_refund_id($nullvar, $refund, $refund_id) {
    if ( !empty($refund->refunded_by) ){
        echo '<td>Refund ID: '.$refund_id.'</td>';  
    }
}
  • Thanks a lot, it's working now! Didn't know about the $accepted_args parameter until today and I was so stupid not to check the codex for it. – Lupus Dec 10 '19 at 13:25
5

There's a fundamental misunderstanding here about how filters and actions work, here is the flaw in your thinking:

I know that, as a matter of fact, three arguments are passed in

A closer inspection of add_action and add_filter reveals:

add_action( string $tag, callable $function_to_add, int $priority = 10, int $accepted_args = 1 )

Notice the $accepted_args parameter. Because it isn't present in the call to add_filter in your question, it defaults to 1. To get 3 all parameters you need to specify 3. This is because not all filters need all parameters, and it can be useful to use other functions that can't accept all the parameters when only the first is needed. E.g. PHP internal math functions

Additional notes:

  • you used add_filter, but it was an action that got called, add_action should be used for actions, and add_filter for filters
  • 3rd party plugins are offtopic, the only reason your Q could be answered here is because it can be answered without knowledge of WooCommerce. The specifics about how that plugins internals work are not on topic here
  • You need to add escaping to $refund_id, the lack of escaping is a security hole. esc_html should do the job
  • Never modify WP Core internals, a quick look at the documentation almost always gives the answer
  • Totally missed the $accepted_args parameter so far. So silly of me, thanks for the clarification. As for your additional notes: - I knew about WC being offtopic but as you said this had to do with WordPress itself; - I also know about escaping, that was just quick testing code on a development server - As I said I just modified the WP core script to double check if that was the issue, of course I undid the edit right away. I just like to test things when I can't figure them out :) – Lupus Dec 10 '19 at 13:29
  • nifty, if that resolves the issue for you, consider marking it as the correct answer by clicking the checkmark under the vote counters – Tom J Nowell Dec 10 '19 at 23:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.