rfc:callable-interfaces
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
rfc:callable-interfaces [2016/04/06 10:40] – created ocramius | rfc:callable-interfaces [2017/09/22 13:28] (current) – external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== PHP RFC: Typesafe callable | + | ====== PHP RFC: Callable interfaces |
* Version: 0.1 | * Version: 0.1 | ||
- | * Date: 2016-04-04 | + | * Date: 2016-04-06 |
* Author: Ben Scholzen, mail@dasprids.de | * Author: Ben Scholzen, mail@dasprids.de | ||
- | * Status: | + | |
- | * First Published at: http:// | + | |
+ | * First Published at: http:// | ||
===== Introduction ===== | ===== Introduction ===== | ||
- | Currently, when you typehint | + | Currently, when you type-hint |
- | This RFC tries to solve this problem by introducing | + | This RFC tries to solve this problem by allowing callables to follow |
+ | This RFC is inspired by https:// | ||
===== Proposal ===== | ===== Proposal ===== | ||
- | This RFC proposes the definition of user-defined callables. Since " | + | |
+ | PHP already | ||
+ | |||
+ | **_****_****invoke** already works quite well: with this proposal, generic *callable* arrays, functions and objects will be usable as if they implemented a matching interface: | ||
<code php> | <code php> | ||
- | callable FilterCallable(string | + | interface RegisterUser { |
+ | public function __invoke(Username | ||
+ | } | ||
</ | </ | ||
- | With such a callable in place, the user can now typehint against it, like they would against | + | We can now implicitly implement this interface by just defining |
+ | |||
+ | |||
+ | As a function: | ||
<code php> | <code php> | ||
- | function | + | function |
- | { | + | // ... domain logic here ... |
- | return | + | |
+ | return | ||
} | } | ||
+ | </ | ||
- | foo(function (string | + | As a closure: |
+ | |||
+ | <code php> | ||
+ | $register = function (Username | ||
+ | // ... domain logic here ... | ||
+ | |||
+ | | ||
+ | }; | ||
</ | </ | ||
- | Of course, this works with any other kind of callable as well, be it a string, an array or an object. | + | As a static callable array: |
+ | |||
+ | <code php> | ||
+ | class Register { | ||
+ | public static function register(Username $username) : UserRegistration { | ||
+ | // ... domain logic here ... | ||
+ | |||
+ | return new UserRegistration($userId); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $register = [Register:: | ||
+ | </ | ||
+ | |||
+ | As an instance callable array: | ||
+ | |||
+ | <code php> | ||
+ | class Register { | ||
+ | public function register(Username $username) : UserRegistration { | ||
+ | // ... domain logic here ... | ||
+ | |||
+ | return new UserRegistration($userId); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | $register = [new Register(), ' | ||
+ | </ | ||
+ | |||
+ | We are now able to consume | ||
+ | |||
+ | <code php> | ||
+ | function runRegistration(Username $username, RegisterUser $handler) { | ||
+ | var_dump($handler($username)); | ||
+ | } | ||
+ | |||
+ | runRegistration(new Username(' | ||
+ | </ | ||
+ | |||
+ | In order for this to work, any implicitly defined callable should | ||
+ | |||
+ | In pseudo-code, this would look like following, under the hood: | ||
+ | |||
+ | <code php> | ||
+ | function passAParameterToAPhpFunction(callable $callable, $expectedParameterInterface) { | ||
+ | if (! $expectedParameterInterface-> | ||
+ | passParameter($callable); | ||
+ | |||
+ | return; | ||
+ | } | ||
+ | |||
+ | if (! $expectedParameterInterface-> | ||
+ | throw new TypeError(' | ||
+ | } | ||
+ | |||
+ | if (! is_object($callable)) { | ||
+ | $callable = wrapInCompatibleAnonymousClass($callable); | ||
+ | } | ||
+ | |||
+ | passParameter($callable); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Still Open for Discussion ===== | ||
+ | |||
+ | How will **instanceof** behave, when asked for a type-check against **callable**? | ||
+ | |||
+ | <code php> | ||
+ | interface RegisterUser { | ||
+ | public function __invoke(Username $username) : UserRegistration; | ||
+ | } | ||
+ | |||
+ | interface DeleteUserRegistration { | ||
+ | public function __invoke(Username $username) : UserRegistration; | ||
+ | } | ||
+ | |||
+ | $register = function (Username $username) : UserRegistration { | ||
+ | return new UserRegistration(...); | ||
+ | }; | ||
+ | |||
+ | var_dump($register instanceof DeleteUserRegistration); | ||
+ | </ | ||
+ | |||
+ | ===== Retired ===== | ||
+ | |||
+ | This RFC has been retired. Reason for that is that PHP currently (Version 7.0~7.1) allows applying function semantics to objects via the **_****_invoke** magic methods. Allowing the opposite would mix the domain of functions and objects in ways that are very hard to disentangle, | ||
+ | |||
+ | While it is unfortunate that migration to type-safe callables (https:// | ||
===== Backward Incompatible Changes ===== | ===== Backward Incompatible Changes ===== |
rfc/callable-interfaces.1459939240.txt.gz · Last modified: 2017/09/22 13:28 (external edit)