Make WordPress Core

Opened 6 years ago

Last modified 6 years ago

#46451 new defect (bug)

wp_dropdown_users() does not pass all applicable arguments on to get_users()

Reported by: pbiron's profile pbiron Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Users Keywords:
Focuses: Cc:

Description

The docs for wp_dropdown_users() say of the $args param:

Array or string of arguments to generate a drop-down of users.
See WP_User_Query::prepare_query() for additional available arguments.

However, not every argument accepted by WP_User_Query::prepare_query() is passed on to get_users(): there is a specific whitelist, and some very useful args are not included in that whitelist (e.g., meta_query).

Instead, the arguments used by wp_dropdown_users() for generating the HTML markup of the dropdown should simply be removed from $args and anything left over should be passed to get_users().

Not only would this bring the behavior of wp_dropdown_users() into conformance with its docs, it would have the added benefit of future proofing it: if a future release adds additional args to get_users(), the whitelist in wp_dropdown_users() wouldn't have to be adjusted to accept them.

Change History (3)

#1 follow-up: @pbiron
6 years ago

There are a couple of ways this could be achieved, I list a few below...and I'm sure there are others as well.

Generate a whitelist from a blacklist

<?php
$blacklist = array(
        'show_option_all',
        'show_option_none',
        'hide_if_only_one_author',
        'multi',
        'show',
        'echo',
        'selected',
        'name',
        'class',
        'id',
        'include_selected',
        'option_none_value',
);
$whitelist = array_diff( array_keys( $r ), $blacklist );
$query_args = wp_array_slice_assoc( $r, $whitelist );
...
$users = get_users( $query_args );

Using array_filter() with a named callback that strips the blacklist

<?php
$query_args = array_filter( $r, '_wp_dropdown_users_filter_get_users_args', ARRAY_FILTER_USE_KEY );
...
$users = get_users( $query_args );

where _wp_dropdown_users_filter_get_users_args() is defined as:

<?php
function _wp_dropdown_users_filter_get_users_args( $key ) {
        $blacklist = array(
                'show_option_all',
                'show_option_none',
                'hide_if_only_one_author',
                'multi',
                'show',
                'echo',
                'selected',
                'name',
                'class',
                'id',
                'include_selected',
                'option_none_value',
        );

        return ! in_array( $key, $blacklist );
}

Using array_filter() with an anonymous callback that strips the blacklist

<?php
$blacklist = array(
        'show_option_all',
        'show_option_none',
        'hide_if_only_one_author',
        'multi',
        'show',
        'echo',
        'selected',
        'name',
        'class',
        'id',
        'include_selected',
        'option_none_value',
);
// I think the "use( $blacklist )" syntax was only available as of PHP 5.3
// but with WP 5.2 slated to bump the minimum PHP version to 5.6 that won't be a problem
$query_args = array_filter( $r, function( $key ) use ( $blacklist ) {
        return ! in_array( $key, $blacklist );
}, ARRAY_FILTER_USE_KEY );
...
$users = get_users( $query_args );

#2 in reply to: ↑ 1 @pbiron
6 years ago

Replying to pbiron:

There are a couple of ways this could be achieved, I list a few below...and I'm sure there are others as well.

maybe the cleanest way would be to modify wp_array_slice_assoc() to accept an $invert param (which would be useful in and of itself):

<?php
function wp_array_slice_assoc( $array, $keys, $invert = false ) {
        $slice = array();
        if ( ! $invert ) {
                foreach ( $keys as $key ) {
                        if ( isset( $array[ $key ] ) ) {
                                $slice[ $key ] = $array[ $key ];
                        }
                }
        }
        else {
                foreach ( array_keys( $array ) as $key ) {
                        if ( ! in_array( $key, $keys ) ) {
                                $slice[ $key ] = $array[ $key ];
                        }
                }
        }

        return $slice;
}

and then just use:

<?php
$blacklist = array(
        'show_option_all',
        'show_option_none',
        'hide_if_only_one_author',
        'multi',
        'show',
        'echo',
        'selected',
        'name',
        'class',
        'id',
        'include_selected',
        'option_none_value',
);
$query_args = wp_array_slice_assoc( $r, $blacklist, true );
...
$users = get_users( $query_args );

#3 @pbiron
6 years ago

What the xxxx was I thinking, the easiest way is:

<?php
$blacklist = array(
        'show_option_all',
        'show_option_none',
        'hide_if_only_one_author',
        'multi',
        'show',
        'echo',
        'selected',
        'name',
        'class',
        'id',
        'include_selected',
        'option_none_value',
);
$query_args = array_diff_key( $r, array_flip( $blacklist ) );
...
$users = get_users( $query_args );
Note: See TracTickets for help on using tickets.