rfc:allow_casting_closures_into_single-method_interface_implementations

This is an old revision of the document!


Title: Allow casting closures into single-method interface implementations

Introduction

This RFC proposes a new method for the Closure class called castTo. This method would allow developers to create an instance of a class that implements a specified interface and uses the closure as the implementation.

Proposal

A new method, castTo, would be added to the Closure class with the following signature (generics added for extra clarity):

/**
 * @template T
 * @param class-string<T> $interface
 * @return T
 */
public function castTo(string $interface): object;

When called on a closure, this method would:

  1. Verify that the specified interface exists and has exactly one method.
  2. Verify that the closure and the method on the interface have compatible signatures.
  3. Create a new instance of an anonymous class that implements the specified interface.
  4. Use the closure as the implementation for the interface's method in the newly created instance.

Example usage:

interface MyInterface {
    public function doSomething(int $x, int $y): int;
}
 
$closure = function (int $x, int $y): int {
    return $x + $y;
};
 
$instance = $closure->castTo(MyInterface::class);
$result = $instance->doSomething(1, 2);
 
echo $result; // Output: 3

This feature could be useful in several scenarios where developers need to create lightweight, dynamic, or anonymous class instances that implement a specific interface. Some of the possible use cases include:

  • Testing and mocking: This feature could help create mock objects on-the-fly for unit testing, especially when testing the interaction between objects and their dependencies. By using a closure to implement the interface, developers can quickly create lightweight, tailored mock objects without having to define additional classes.
  • Adapters: In situations where the developer needs to adapt an existing object to a specific interface, this feature can provide a quick and convenient way to create an adapter using a closure without the need for a separate adapter class. This can be particularly helpful when working with external libraries or APIs.
  • Single-method interfaces: For single-method interfaces, this feature could provide a more concise way to create instances that implement the interface, without the overhead of defining a full class. This can lead to more readable and maintainable code when dealing with simple, single-method interfaces.
  • Rapid prototyping: During the development phase, this feature can be helpful for quickly creating instances implementing specific interfaces, allowing developers to focus on the core business logic instead of writing boilerplate code.
  • Dynamic behavior: In scenarios where the implementation of an interface needs to be changed at runtime, this feature can help create instances with dynamic behavior. By passing different closures to the createInstanceWithInterface method, developers can quickly create instances with varying behavior while still adhering to the required interface.

Here is an example that combines this proposal with the first-class callable syntax to provide a fallback TranslatorInterface implementation based on strtr() when needed:

interface TranslatorInterface {
    public function translate(string $message, array $parameters = []): string;
}
 
class SomeClass {
    private TranslatorInterface $translator;
 
    public function __construct(
        TranslatorInterface $translator = null,
    ) {
        $this->translator = $translator ?? strtr(...)->castTo(TranslatorInterface::class);
    }
}

Backward Incompatible Changes

This proposal introduces no backward incompatible changes, as it only adds a new method to the Closure class.

Open Issues

  • naming of the method
  • exact behavior when checks fail
  • behavior of the reflection API on resulting objects

Unaffected PHP Functionality

No existing PHP functionality would be affected by this proposal.

Future Scope

None anticipated yet.

Proposed PHP Version

This RFC targets PHP version 8.3.

RFC Impact

This RFC impacts the PHP engine, specifically the Closure class.

Vote

The vote will require a 2/3 majority to be accepted. Voting will start on [Vote start date] and end on [Vote end date].

Patches and Tests

TBD

Rejected Features

None.

rfc/allow_casting_closures_into_single-method_interface_implementations.1681392892.txt.gz · Last modified: 2023/04/13 13:34 by nicolasgrekas