Skip to content

callable parameter with any number of arguments #8214

@ondrejmirtes

Description

@ondrejmirtes

Feature request

From #6813

I recently tried to create something where this would have been quite useful: a higher-order function to take functions that throw exceptions, catch (and log) them, and then return a WP_Error object instead.

/**
 * Wraps a function so that, when run, any exceptions are caught, logged, and converted to a generic WP_Error.
 *
 * Works with functions of up to 5 parameters. (Add more PX generic types to this docblock to add support for more params.)
 *
 * @template P1
 * @template P2
 * @template P3
 * @template P4
 * @template P5
 * @template R
 *
 * @param (Closure(P1, P2, P3, P4, P5): R) $fn Function that could throw.
 *
 * @return (Closure(P1, P2, P3, P4, P5): (R|WP_Error))
 */
function handleExceptionsAsWpErrors(Closure $fn, LoggerInterface $logger): Closure {
	return static function (...$params) use ($fn, $logger) {
		try {
			return $fn(...$params);
		} catch (Throwable $e) {
			$logger->error($e->getMessage());
			return new WP_Error('arn_internal_server_error');
		}
	};
}

In this case, I was hoping that I could get around the issue of having variable numbers of params by hardcoding generics for a reasonable max number of parameters. My hope was that, that for Closures with less than 5 params, the unused PX template types would default to never, and Closure(never): R would be considered identical to Closure(): R.

Of course, it didn't actually work when I tried it, and I suspect that not even setting explicit defaults for the template types (once #1835 lands) would alter the behavior here.

But should it work? Is a parameter of type never the same thing as no parameter at all? Or would that conceptually be more like a void parameter, if such a thing even makes sense?


It's also worth noting that TypeScript has a Parameters utility type. Presumably, if PHPStan had something like this, the kind of static typing I'm looking for would be straightforward, using syntax that could look a little like this:

/**
 * @template R
 *
 * @param (Closure(...): R) $fn
 *
 * @return (Closure(...parameters<$fn>): (R|WP_Error))
 */

What do you think of these ideas?

Originally posted by @ZebulanStanphill in #6813 (comment)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions