PHP Named Arguments and First-Class Callable Syntax in WordPress Plugins

PHP 8.0 named arguments and PHP 8.1 first-class callable syntax (strlen(...)) are two underused features that improve readability and safety in WordPress plugin code. Named arguments eliminate the need to look up parameter order in function signatures and make optional parameters self-documenting. First-class callables create a Closure from any callable without wrapping it in an anonymous function, reducing boilerplate in hook registrations and array operations.

The examples below show named arguments in WordPress core functions, first-class callables in hook registrations and array pipelines, and how to combine both features for cleaner plugin code.

 10,
    'post_type'   => 'product',
] );

// wp_insert_post positional — easy to mix up args
$id = wp_insert_post( [
    'post_title'  => 'My Post',
    'post_status' => 'publish',
] );

// Named args shine in functions with many optional parameters:
$query = new WP_Query(
    post_type:         'product',
    posts_per_page:    12,
    meta_key:          '_price',
    orderby:           'meta_value_num',
    order:             'ASC',
    no_found_rows:     true,
    update_post_meta_cache: true,
);

// str_contains, array_slice — named args remove need for doc lookup
$excerpt = substr( string: $content, offset: 0, length: 160 );
$page    = array_slice( array: $items, offset: $start, length: $per_page, preserve_keys: false );

// ── 2. First-class callable syntax ────────────────────────────────────────
// Before: wrapping in anonymous function is noise
add_filter( 'the_title', function( $t ) { return esc_html( $t ); } );
add_filter( 'the_excerpt', function( $e ) { return wp_kses_post( $e ); } );

// After: first-class callable — creates a Closure directly
add_filter( 'the_title',   esc_html(...)   );   // PHP 8.1+
add_filter( 'the_excerpt', wp_kses_post(...) );

// ── 3. First-class callables in array operations ──────────────────────────
$post_ids = get_posts( [ 'fields' => 'ids', 'numberposts' => 20 ] );

// Before:
$titles = array_map( function( $id ) { return get_the_title( $id ); }, $post_ids );

// After: first-class callable
$titles   = array_map( get_the_title(...), $post_ids );
$filtered = array_filter( $post_ids, is_int(...)  );     // filter to ints
$trimmed  = array_map( trim(...), $raw_strings );         // trim all strings

// ── 4. First-class callables in pipelines ─────────────────────────────────
$pipeline = [
    trim(...),
    strtolower(...),
    sanitize_title(...),
];

$slug = array_reduce(
    $pipeline,
    fn( $carry, $fn ) => $fn( $carry ),
    '  My Plugin Name  '
);
// Result: 'my-plugin-name'

// ── 5. Named args make variadic hook wrappers self-documenting ────────────
function my_add_filter( string $hook, callable $callback, int $priority = 10, int $accepted_args = 1 ): void {
    add_filter( tag: $hook, callback: $callback, priority: $priority, accepted_args: $accepted_args );
}

my_add_filter(
    hook:          'woocommerce_product_get_price',
    callback:      apply_discount(...),
    priority:      20,
    accepted_args: 2,
);

NOTE: First-class callables (fn(...)) create a bound Closure at the point of use — if the function is redefined later (e.g., by a plugin), the closure still references the original; this is correct behaviour for hooks but can be surprising in test stubs; use a regular anonymous function wrapper when you need late binding.