{"id":205,"date":"2018-11-15T02:51:35","date_gmt":"2018-11-15T02:51:35","guid":{"rendered":"https:\/\/miklcct.com\/wordpress\/?p=205"},"modified":"2018-12-28T08:16:21","modified_gmt":"2018-12-28T08:16:21","slug":"type-safe-callables-in-php-by-interfaces-with-__invoke","status":"publish","type":"post","link":"https:\/\/miklcct.com\/wordpress\/2018\/11\/15\/type-safe-callables-in-php-by-interfaces-with-__invoke\/","title":{"rendered":"Type-safe callables in PHP by interfaces with __invoke()"},"content":{"rendered":"<p>The type system in PHP has become safer each version since PHP 7.0, by using a strict type declaration on top of the PHP file. As a newly-developed feature, it doesn&#8217;t come with the billion-dollar mistake of allowing <code>NULL<\/code> for variables declared with a class type, unless you allow it specifically.<\/p>\n<p>However, if a function need to accept a callable which accepts parameters of a specific type and returns a specific type, you cannot do it with <code>callable<\/code> type declaration because there is no way to add type information into the <code>callable<\/code> declaration.<\/p>\n<p>Luckily, there is a magic function called <code>__invoke()<\/code>. It can turn any object into a callable, and as a member function, you can add type declaration into it, which make the object only callable with the specified parameters.<\/p>\n<p>For example, if you are writing an exception handler factory which need a third-party function to convert a <code>Throwable<\/code> into a <code>ResponseInterface<\/code> to produce a handler, instead of writing this:<\/p>\n<pre>\r\nfunction make_exception_handler(callable $get_response) : callable {\r\n    return function (Throwable $e) use ($get_response) {\r\n        \/\/ log the exception\r\n        $response = $get_response($e);\r\n        \/\/ output the response\r\n    };\r\n}\r\n<\/pre>\n<p>You can write the following instead:<\/p>\n<pre>\r\ninterface ExceptionResponseFactoryInterface {\r\n    public function __invoke(Throwable $exception) : ResponseInterface;\r\n}\r\n\r\nfunction make_exception_handler(ExceptionResponseFactoryInterface $get_response) : callable {\r\n    return function (Throwable $e) use ($get_response) {\r\n        \/\/ log the exception\r\n        $response = $get_response($e);\r\n        \/\/ output the response\r\n    };\r\n}\r\n<\/pre>\n<p>As you can see, there is absolutely no change in the function body, but the expectation is now clearly indicated in the function signature: get a <code>Throwable<\/code> and return a <code>ResponseInterface<\/code>. Now the code can be statically type-analysed by an IDE.<\/p>\n<p>However, a caveat is that, you can&#8217;t directly pass anonymous functions into the function, instead, you must make an object implementing the interface instead. For example:<\/p>\n<pre>\r\nset_exception_handler(\r\n    make_exception_handler(\r\n        new class implements ExceptionResponseFactoryInterface {\r\n            public function __invoke(Throwable $exception) : ResponseInterface {\r\n                \/\/ do stuff here\r\n                return new Response();\r\n            }\r\n        }\r\n    )\r\n);\r\n<\/pre>\n<p>Or use a generic wrapper class to convert non-type-safe legacy code:<\/p>\n<pre>\r\nclass CallableExceptionResponseFactory implements ExceptionResponseFactoryInterface {\r\n    private $callable;\r\n    public function __construct(callable $callable) {\r\n        $this->callable = $callable;\r\n    }\r\n    public function __invoke(Throwable $exception) : ResponseInterface {\r\n        return ($this->callable)($exception);\r\n    }\r\n}\r\n\r\nset_exception_handler(\r\n    make_exception_handler(\r\n        new CallableExceptionResponseFactory('get_response_from_exception')\r\n    )\r\n);\r\n<\/pre>\n<p>By using the above method, you can do type-safe functional programming in PHP, despite not having type signatures for callables.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The type system in PHP has become safer each version since PHP 7.0, by using a strict type declaration on top of the PHP file. As a newly-developed feature, it doesn&#8217;t come with the billion-dollar mistake of allowing NULL for variables declared&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[31],"class_list":["post-205","post","type-post","status-publish","format-standard","hentry","category-programming","tag-php"],"_links":{"self":[{"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/posts\/205","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/comments?post=205"}],"version-history":[{"count":2,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/posts\/205\/revisions"}],"predecessor-version":[{"id":208,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/posts\/205\/revisions\/208"}],"wp:attachment":[{"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/media?parent=205"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/categories?post=205"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/miklcct.com\/wordpress\/wp-json\/wp\/v2\/tags?post=205"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}