====== PHP RFC: Short Closures ====== * Version: 0.1 * Date: 2017-06-24 * Author: MichaƂ Brzuchalski, * Status: Draft * First Published at: http://wiki.php.net/rfc/short-closures ===== Introduction ===== Callables syntactically have few ways of passing them around. This RFC is supposed to introduce unified short way of creating closures from callables and reduce ''callable'' type hint checks at runtime by using closures. ===== Proposal ===== ==== Current callable syntax ==== Currently there are 6 available ways of declaring callables and they differ while using function or method. namespace Util { function writeln(string $message) : void { echo $message . \PHP_EOL; } class Terminal { public static function println(string $message) : void { echo $message . \PHP_EOL; } public function writeln(string $message) : void { echo $message . \PHP_EOL; } } class Console extends Terminal { public function __invoke(string $message) : void { echo $message . \PHP_EOL; } } } namespace { function helloWorld(callable $callable) : void { $callable('Hello World!'); } } === Simple callback === A simple callback is possible to pass using their name. helloWorld('Util\\writeln'); call_user_func('Util\writeln', 'Hello World!'); === Static class method call === Static class method call first way is quite similar and requires method name after ''::''. helloWorld('Util\\Terminal::println'); call_user_func('Util\\Terminal::println'), 'Hello World!'); === Static class method call using array === Static class method call can be also passed using array construct with a class name at 0 index and method name at 1 index. helloWorld([Util\Terminal::class, 'println']); call_user_func([Util\\Terminal::class, 'println']), 'Hello World!'); === Object method call === Object method call is passed using array construct with two params - which is object instance variable and method name. $terminal = new \Util\Terminal(); helloWorld([$terminal, 'writeln']); call_user_func([$terminal, 'writeln'], 'Hello World!'); === Relative static class method call === Relative static class method call is passed using array with two params - which is class name and method name with base class name delimited by ''::''. $console = new Util\Console(); call_user_func([Util\Console::class, 'parent::writeln'], 'Hello World!'); === Invokable objects === Objects implementing '%%__invoke%%' can be used as callables using an instance variable. $console = new Util\Console(); helloWorld($console); call_user_func($console, 'Hello World!'); ==== Drawbacks of current solution ==== - The syntax is based on passing function names as a string or array with an instance and method name as a string etc. which is very hard to refactor using for eg. IDE or detecting using regular expressions. - Passed callables are different type variables, like: 'string', 'array' or specified object instance. - Passed callables may not be valid callables which are validated at call time and can pass ''callable'' type hint without warning. ==== Proposed solution ==== The way to unify passing callables is creating closures using the same syntax as invoking functions without their argument list and parenthesis around braces and call them all the same way, which benefits are short closure from callable syntax which shortens creation of closures for most of the cases. === Simple callback === Short closure from callable function would look like: $writeln = {Util\writeln}; // is a simplification for $writeln = Closure::fromCallable('Util\writeln'); === Static class method call === Static class method short closure that would look like: $println = {Util\Terminal::println}; // instead of $println = Closure::fromCallable([Util\Terminal::class, 'writeln']); // and $println = Closure::fromCallable('Util\Terminal::writeln'); === Object method call === Object instance method short closure that would look like: $writeln = {$terminal->writeln}; // instead of $writeln = Closure::fromCallable([$terminal, 'writeln']); === Invokable objects === Invokable objects short closure that would be: $console = new Util\Console(); $callback = {$console}; // instead of $callback = Closure::fromCallable($console); ==== Benefits ==== - Every callable closure is valid for all the time when it's passed around. - No need to validate ''callable'' type hint against given argument every time. - Common syntax for most of the callable types. - Syntax similar to function/method call. - No need to pass function/method/class names as strings which are not IDE friendly for refactoring. - Easy to parse by static analysis tools because of consistent syntax. ===== Backward Incompatible Changes ===== None ===== Proposed PHP Version(s) ===== The proposed PHP versions that the feature will be included is "next PHP 7.x" or "PHP 8.0". ===== RFC Impact ===== ==== To SAPIs ==== None. ==== To Existing Extensions ==== No. ==== To Opcache ==== Isn't verified yet. ==== New Constants ==== None. ==== php.ini Defaults ==== None. ===== Open Issues ===== None. ===== Unaffected PHP Functionality ===== None. ===== Future Scope ===== There might be additional reflection inprovement which gives ability to retrieve information about closure origin. ===== Proposed Voting Choices ===== As this is a language change, a 2/3 majority is required. The vote would be a straight Yes/No vote for accepting the RFC and merging the patch. ===== Patches and Tests ===== Not implemented ===== References ===== > Links to external references, discussions or RFCs