ELEMENTS OF
 FUNCTIONAL PROGRAMMING
 IN PHP
JAREK JAKUBOWSKI PYTHON DEVELOPER SCALA ENTHUSIAST PHP DEVELOPER /jarek.jakubowski
FUNCTIONAL PHP WHAT IS FUNCTIONAL PROGRAMMING? 1. Functions as primary citizens:
 - save them as variables
 - pass them as arguments
 - return them from another 
 function 2. Lambda calculus:
 - closures (PHP >= 5.3)
 - generators (PHP >= 5.5)
FUNCTIONAL PHP WHAT IS FUNCTIONAL PROGRAMMING? $functionAsVariable = function (callable $functionAsArgument): callable { return function () use ($functionAsArgument) { return $functionAsArgument(); }; }; $foo = 'bar'; return $functionAsVariable(function () use ($foo): string { return $foo; })();
FUNCTIONAL PHP YOU HAVE PROBABLY USED IT ALREADY const functionAsVariable = arg => functionAsArgument => functionAsArgument(arg); let foo = 'bar'; return functionAsVariable(foo)( (arg) => arg ); Modern JS (ES6):
FUNCTIONAL PHP YOU HAVE PROBABLY USED IT ALREADY $.ajax({ url: "demo_test.txt", success: function (result) { $("#div1").html(result); }, error: function (xhr) { alert("An error occured: " + xhr.status + " " + xhr.statusText); } }); Old JS (jQuery):
FUNCTIONAL PHP YOU CAN USE IT ALREADY use ReactPromisePromise; (new Promise(file_get_contents('foo.txt'))) ->then(function (string $result): string { echo 'well done! '.$result; }) ->otherwise(function (Throwable $error) { throw $error; }); Adequate code in PHP:
FUNCTIONAL PHP PROS (PHP) ▸ LESS ERRORS
 YOU HAVE TO HANDLE EVERY CASE
 Fatal error: Call to a member function on null ▸ METHOD CHAINING ▸ CONCURRENT (ASYNC) CODE ▸ IMMUTABILITY ▸ ES / DDD MADE EASY
FUNCTIONAL PHP CONS (PHP) ▸ LESS READABLE CODE 
 (PHP SYNTAX DOESN’T FIT BEST) ▸ NOT OBVIOUS FOR PHP DEVS
 (while JS Devs use it everyday) ▸ PERFORMANCE
 (but do we really care about it?)
TOOLS FUNCTIONAL PHP PhpSlang is a PHP library aiming to fill the gaps 
 between PHP and classical functional languages. PHPSLANG REACTPHP ReactPHP is a low-level library for event-driven programming in PHP. phpslang.io reactphp.org
TOOLS FUNCTIONAL PHP Asynchronous & Fault-tolerant PHP Framework 
 for Distributed Applications. KRAKEN FRAMEWORK IMMUTABLE.PHP Immutable collections, with filter, map, join, sort, slice, and other methods. 
 Well-suited for functional programming and memory-intensive applications. 
 Runs especially fast in PHP7. https://github.com/jkoudys/immutable.php kraken-php.com
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops 5. Use async calls wherever you can 6. Avoid mutating the State
HOW? FUNCTIONAL PHP 1. Forget about null checks
 
 
 
 
 

HOW? public function displayLocalNumber(string $email): string { $user = $this->userRepository->findByEmail($email); if ( ! is_null($user) && ! is_null($user->getProfile()) && ! is_null($user->getProfile()->getAddress()) && ! is_null($user->getProfile()->getAddress()->getLocalNumber()) ) { return 'Local number is: ' . $user->getProfile()->getAddress()->getLocalNumber(); } else { return 'No sufficient data'; } } FUNCTIONAL PHP 1. Forget about null checks
HOW? public function displayLocalNumber(string $email): string { $user = $this->userRepository->findByEmail($email); if (null !== $user) { if (null !== $profile = $user->getProfile()) { if (null !== $address = $profile->getAddress()) { if (null !== $localNumber = $address->getLocalNumber()) { return 'Local number is: ' . $localNumber; } } } } return 'No sufficient data'; } FUNCTIONAL PHP 1. Forget about null checks
HOW? use PhpSlangOptionOption; public function displayLocalNumber(string $email): string { return Option::of($this->userRepository->findByEmail($email)) ->flatMap(function(User $user): Option { return Option::of($user->getProfile()); }) ->flatMap(function(Profile $profile): Option { return Option::of($profile->getAddress()); }) ->flatMap(function(Address $address): Option { return Option::of($address->getLocalNumber()); }) ->map(function(int $localNumber): string { return 'Local number is: ' . $localNumber; }) ->getOrElse('No sufficient data'); } FUNCTIONAL PHP 1. Forget about null checks
 Use Option instead (Option can contain nothing or something)
HOW? use PhpSlangOptionOption; public function displayLocalNumber(string $email): string { return $this->userRepository->findByEmail($email) ->flatMap(function(User $user): Option { return $user->getProfile(); }) ->flatMap(function(Profile $profile): Option { return $profile->getAddress(); }) ->flatMap(function(Address $address): Option { return $address->getLocalNumber(); }) ->map(function(int $localNumber): string { return 'Local number is: ' . $localNumber; }) ->getOrElse('No sufficient data'); } FUNCTIONAL PHP 1. Forget about null checks
 Return Option instead (Option can contain nothing or something)
OPTION FUNCTIONAL PHP Option can contain nothing or something class Some extends Option {} class None extends Option {} /** * @var Option<Some, None> */
OPTION FUNCTIONAL PHP 1. Static constructor Option::of(mixed $value) 2. Map methods: 1. map(function(mixed $value): mixed {}): Option
 Option<string> => string
 Option<Option<string>> => Option<string> 2. flatMap(function(mixed $value): Option {}): Option
 Option<string> => string
 Option<Option<string>> => string 3. Return methods: 1. getOrElse(mixed $returnWhenNull): mixed 2. getOrCall(callable $callWhenNull): mixed
OPTION FUNCTIONAL PHP Best practices: 1. Return Option whenever function might return null 2. Use map(Closure $expression) when $expression returns unpacked value
 map(function(): mixed): Option 3. Use flatMap(Closure $expression) when $expression returns value packed in Option
 flatMap(function(): Option): Option 4. Don’t forget to call getOrCall() / getOrElse() at end! - only at this point all defined expressions are evaluated
MAP() VS FLATMAP() use PhpSlangOptionOption; public function getUser(string $email): Option<User> { return Option::of($this->userRepository->findByEmail($email)); } public function getUserNameOption(string $email): Option<string> { return $this->getUser($email) ->map(function (User $user): Option { return Option::of($user->getName()); }) ->getOrElse(new Some($email)); } public function getUserNameFlat(string $email): string { return $this->getUser($email) ->flatMap(function (User $user): Option { return Option::of($user->getName()); }) ->getOrElse($email); } FUNCTIONAL PHP
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about throwing Exceptions
HOW? public function getUserName(string $email): ?string { try { return $this->findUser($email)->getName(); } catch (InvalidArgumentException $e) { return null; } } private function findUser(string $email): User { $user = $this->userRepository->findBy(['email' => $email]); if (!$user instanceof User) { throw new InvalidArgumentException('User not found.'); } return $user; } FUNCTIONAL PHP 2. Forget about throwing Exceptions
HOW? public function getUserName(string $email): ?string { try { return $this->findUser($email)->getName(); } catch (InvalidArgumentException $e) { return null; } } private function findUser(string $email): User { $user = $this->userRepository->findBy(['email' => $email]); if (!$user instanceof User) { throw new InvalidArgumentException('User not found.'); } return $user; } FUNCTIONAL PHP 2. Forget about throwing Exceptions
HOW? public function getUserName(string $email): Option { return $this->findUser($email) ->right(function (User $user): Some { return new Some($user->getName()); }) ->left(function (): None { return new None(); }) ->get(); } private function findUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 2. Forget about throwing Exceptions: Use Either instead
EITHER FUNCTIONAL PHP abstract class Either { abstract public function left(Closure $expression): Either; abstract public function right(Closure $expression): Either; abstract public function flatLeft(Closure $expression): Either; abstract public function flatRight(Closure $expression): Either; public function get(): mixed {}; }
 class Right extends Either {} class Left extends Either {}
EITHER FUNCTIONAL PHP Best practices: 1. Use Right of expected value, Left for unexpected/error 2. Use left(Closure $expression) / right(Closure $expression) when $expression returns unpacked value
 right(function(): mixed): Either 3. Use flatLeft(Closure $expression) / flatRight(Closure $expression) when $expression returns value packed in either Right or Left
 flatRight(function(): Either): Either 4. flatRight() for chaining, right() on success, left() on error 5. Don’t forget to call get() at end! - only at this point all defined expressions are evaluated
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about throwing Exceptions 3. Forget about variables
HOW? public function getUserName(string $email): Option { return $this->findUser($email) ->flatRight(function (User $user) { return new Some($user->getName()); }) ->flatLeft(function () { return new None(); }) ->get(); } private function getUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 3. Forget about variables
 Avoid declaring them (I’ve already done it)
HOW? public function getUserName(string $email): Option { return $this->findUser($email) ->flatRight(function (User $user) { return new Some($user->getName()); }) ->flatLeft(function () { return new None(); }) ->get(); } private function getUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 3. Forget about variables: Avoid declaring them (I’ve already done it)
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops

HOW? $data = [1,2,3,4,5]; $result = []; foreach ($data as $key => $value) { $result[$key] = $value * 2; } $data = array_map(function (int $value): int { return $value * 2; }, $data); $data = array_filter($data, function (int $value): bool { return $value % 2 === 0; }); $data = array_reduce($data, function (int $prev, int $value): int { return $prev + $value; }, 0); return $data; FUNCTIONAL PHP 4. Forget about loops
HOW? use PhpSlangCollectionParallelListCollection; return ParallelListCollection::of([1,2,3,4,5]) ->map(function (int $value): int { return $value * 2; }) ->filter(function (int $value): bool { return $value % 2 === 0; }) ->fold(0, function (int $prev, int $value): int { return $prev + $value; }); FUNCTIONAL PHP 4. Forget about loops
 Use Collection instead
COLLECTIONS FUNCTIONAL PHP Libraries which provide them: 1. Doctrine collections (incomplete functionality) 2. PhpSlang 3. Immutable.php Collections provide unified API for PHP’s array_* functions and sometimes they are even faster than native PHP.
 
 Methods: 1. filter() (array_filter) 2. map() (array_map) 3. reduce() / fold() (array_reduce) 4. sort() (usort)
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops 5. Use async calls wherever you can
HOW? use function ClueReactBlockawaitAll; /* use try-catch because ReactPHP's awaitAll uses Exceptions to escape loop and PhpSlang's Future monad is not ready yet */ try { $argumentsArray = awaitAll([ $this->getStatus($valuesArray[‘status']), $this->getIdea($valuesArray['idea']), $this->getUserFromToken(), $valuesArray['reason'] ?? null ], LoopFactory::create()); return new Right($argumentsArray); } catch (NotFoundHttpException $e) { return new Left($e); } FUNCTIONAL PHP 5. Use async calls wherever you can
HOW? FUNCTIONAL PHP 1. Forget about null checks 2. Forget about loops 3. Forget about throwing Exceptions 4. Forget about variables 5. Use async calls wherever you can 6. Avoid mutating the State
$today = new DateTime(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format('l')}"; FUNCTIONAL PHP HOW? 6. Avoid mutating the State
$today = new DateTime(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format('l')}"; // prints "Yesterday was Thursday and today is Thursday" FUNCTIONAL PHP HOW? 6. Avoid mutating the State
$today = new DateTimeImmutable(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format(‘l')}"; // prints "Yesterday was Thursday and today is Friday" FUNCTIONAL PHP HOW? 6. Avoid mutating the State Use Immutable Classes
FUNCTIONAL PHP
REAL WORLD EXAMPLE FUNCTIONAL PHP
HOW IS IT CALLED IN OTHER LANGUAGES? FUNCTIONAL PHP JS (ELM) PHP (PHPSLANG) SCALA (JAVASLANG) Maybe (Just, Nothing) Option (Some, None) Option (Some, None) Either (Right, Left) Either (Right, Left) Either (Right, Left) Promise Promise (Future?) Future async, await await, awaitAll Await.result
QUESTIONS ? FUNCTIONAL PHP
THANK YOU
FURTHER READING FUNCTIONAL PHP ▸ Functional Programming basics ▸ http://maciejpirog.github.io/fishy/ ▸ http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html ▸ https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-1-1f15e387e536 ▸ Missing parts of PHP (maybe in 7.2…) ▸ https://wiki.php.net/rfc/generics (https://www.bountysource.com/issues/20553561-add-generics-support) ▸ https://wiki.php.net/rfc/immutability ▸ https://wiki.php.net/rfc/pipe-operator ▸ PhpSlang http://phpslang.io/ ▸ https://php-slang.github.io/php-slang-docs/static/Introduction/Thinking_Functional.html ▸ https://php-slang.github.io/php-slang-docs/static/Usage/Essentials/Option.html ▸ ReactPHP http://reactphp.org/ ▸ https://github.com/clue/php-block-react

Elements of Functional Programming in PHP

  • 1.
  • 2.
    JAREK JAKUBOWSKI PYTHON DEVELOPER SCALAENTHUSIAST PHP DEVELOPER /jarek.jakubowski
  • 3.
    FUNCTIONAL PHP WHAT ISFUNCTIONAL PROGRAMMING? 1. Functions as primary citizens:
 - save them as variables
 - pass them as arguments
 - return them from another 
 function 2. Lambda calculus:
 - closures (PHP >= 5.3)
 - generators (PHP >= 5.5)
  • 4.
    FUNCTIONAL PHP WHAT ISFUNCTIONAL PROGRAMMING? $functionAsVariable = function (callable $functionAsArgument): callable { return function () use ($functionAsArgument) { return $functionAsArgument(); }; }; $foo = 'bar'; return $functionAsVariable(function () use ($foo): string { return $foo; })();
  • 5.
    FUNCTIONAL PHP YOU HAVEPROBABLY USED IT ALREADY const functionAsVariable = arg => functionAsArgument => functionAsArgument(arg); let foo = 'bar'; return functionAsVariable(foo)( (arg) => arg ); Modern JS (ES6):
  • 6.
    FUNCTIONAL PHP YOU HAVEPROBABLY USED IT ALREADY $.ajax({ url: "demo_test.txt", success: function (result) { $("#div1").html(result); }, error: function (xhr) { alert("An error occured: " + xhr.status + " " + xhr.statusText); } }); Old JS (jQuery):
  • 7.
    FUNCTIONAL PHP YOU CANUSE IT ALREADY use ReactPromisePromise; (new Promise(file_get_contents('foo.txt'))) ->then(function (string $result): string { echo 'well done! '.$result; }) ->otherwise(function (Throwable $error) { throw $error; }); Adequate code in PHP:
  • 8.
    FUNCTIONAL PHP PROS (PHP) ▸LESS ERRORS
 YOU HAVE TO HANDLE EVERY CASE
 Fatal error: Call to a member function on null ▸ METHOD CHAINING ▸ CONCURRENT (ASYNC) CODE ▸ IMMUTABILITY ▸ ES / DDD MADE EASY
  • 9.
    FUNCTIONAL PHP CONS (PHP) ▸LESS READABLE CODE 
 (PHP SYNTAX DOESN’T FIT BEST) ▸ NOT OBVIOUS FOR PHP DEVS
 (while JS Devs use it everyday) ▸ PERFORMANCE
 (but do we really care about it?)
  • 10.
    TOOLS FUNCTIONAL PHP PhpSlang isa PHP library aiming to fill the gaps 
 between PHP and classical functional languages. PHPSLANG REACTPHP ReactPHP is a low-level library for event-driven programming in PHP. phpslang.io reactphp.org
  • 11.
    TOOLS FUNCTIONAL PHP Asynchronous &Fault-tolerant PHP Framework 
 for Distributed Applications. KRAKEN FRAMEWORK IMMUTABLE.PHP Immutable collections, with filter, map, join, sort, slice, and other methods. 
 Well-suited for functional programming and memory-intensive applications. 
 Runs especially fast in PHP7. https://github.com/jkoudys/immutable.php kraken-php.com
  • 12.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops 5. Use async calls wherever you can 6. Avoid mutating the State
  • 13.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks
 
 
 
 
 

  • 14.
    HOW? public function displayLocalNumber(string$email): string { $user = $this->userRepository->findByEmail($email); if ( ! is_null($user) && ! is_null($user->getProfile()) && ! is_null($user->getProfile()->getAddress()) && ! is_null($user->getProfile()->getAddress()->getLocalNumber()) ) { return 'Local number is: ' . $user->getProfile()->getAddress()->getLocalNumber(); } else { return 'No sufficient data'; } } FUNCTIONAL PHP 1. Forget about null checks
  • 15.
    HOW? public function displayLocalNumber(string$email): string { $user = $this->userRepository->findByEmail($email); if (null !== $user) { if (null !== $profile = $user->getProfile()) { if (null !== $address = $profile->getAddress()) { if (null !== $localNumber = $address->getLocalNumber()) { return 'Local number is: ' . $localNumber; } } } } return 'No sufficient data'; } FUNCTIONAL PHP 1. Forget about null checks
  • 16.
    HOW? use PhpSlangOptionOption; public functiondisplayLocalNumber(string $email): string { return Option::of($this->userRepository->findByEmail($email)) ->flatMap(function(User $user): Option { return Option::of($user->getProfile()); }) ->flatMap(function(Profile $profile): Option { return Option::of($profile->getAddress()); }) ->flatMap(function(Address $address): Option { return Option::of($address->getLocalNumber()); }) ->map(function(int $localNumber): string { return 'Local number is: ' . $localNumber; }) ->getOrElse('No sufficient data'); } FUNCTIONAL PHP 1. Forget about null checks
 Use Option instead (Option can contain nothing or something)
  • 17.
    HOW? use PhpSlangOptionOption; public functiondisplayLocalNumber(string $email): string { return $this->userRepository->findByEmail($email) ->flatMap(function(User $user): Option { return $user->getProfile(); }) ->flatMap(function(Profile $profile): Option { return $profile->getAddress(); }) ->flatMap(function(Address $address): Option { return $address->getLocalNumber(); }) ->map(function(int $localNumber): string { return 'Local number is: ' . $localNumber; }) ->getOrElse('No sufficient data'); } FUNCTIONAL PHP 1. Forget about null checks
 Return Option instead (Option can contain nothing or something)
  • 18.
    OPTION FUNCTIONAL PHP Option cancontain nothing or something class Some extends Option {} class None extends Option {} /** * @var Option<Some, None> */
  • 19.
    OPTION FUNCTIONAL PHP 1. Staticconstructor Option::of(mixed $value) 2. Map methods: 1. map(function(mixed $value): mixed {}): Option
 Option<string> => string
 Option<Option<string>> => Option<string> 2. flatMap(function(mixed $value): Option {}): Option
 Option<string> => string
 Option<Option<string>> => string 3. Return methods: 1. getOrElse(mixed $returnWhenNull): mixed 2. getOrCall(callable $callWhenNull): mixed
  • 20.
    OPTION FUNCTIONAL PHP Best practices: 1.Return Option whenever function might return null 2. Use map(Closure $expression) when $expression returns unpacked value
 map(function(): mixed): Option 3. Use flatMap(Closure $expression) when $expression returns value packed in Option
 flatMap(function(): Option): Option 4. Don’t forget to call getOrCall() / getOrElse() at end! - only at this point all defined expressions are evaluated
  • 21.
    MAP() VS FLATMAP() usePhpSlangOptionOption; public function getUser(string $email): Option<User> { return Option::of($this->userRepository->findByEmail($email)); } public function getUserNameOption(string $email): Option<string> { return $this->getUser($email) ->map(function (User $user): Option { return Option::of($user->getName()); }) ->getOrElse(new Some($email)); } public function getUserNameFlat(string $email): string { return $this->getUser($email) ->flatMap(function (User $user): Option { return Option::of($user->getName()); }) ->getOrElse($email); } FUNCTIONAL PHP
  • 22.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about throwing Exceptions
  • 23.
    HOW? public function getUserName(string$email): ?string { try { return $this->findUser($email)->getName(); } catch (InvalidArgumentException $e) { return null; } } private function findUser(string $email): User { $user = $this->userRepository->findBy(['email' => $email]); if (!$user instanceof User) { throw new InvalidArgumentException('User not found.'); } return $user; } FUNCTIONAL PHP 2. Forget about throwing Exceptions
  • 24.
    HOW? public function getUserName(string$email): ?string { try { return $this->findUser($email)->getName(); } catch (InvalidArgumentException $e) { return null; } } private function findUser(string $email): User { $user = $this->userRepository->findBy(['email' => $email]); if (!$user instanceof User) { throw new InvalidArgumentException('User not found.'); } return $user; } FUNCTIONAL PHP 2. Forget about throwing Exceptions
  • 25.
    HOW? public function getUserName(string$email): Option { return $this->findUser($email) ->right(function (User $user): Some { return new Some($user->getName()); }) ->left(function (): None { return new None(); }) ->get(); } private function findUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 2. Forget about throwing Exceptions: Use Either instead
  • 26.
    EITHER FUNCTIONAL PHP abstract classEither { abstract public function left(Closure $expression): Either; abstract public function right(Closure $expression): Either; abstract public function flatLeft(Closure $expression): Either; abstract public function flatRight(Closure $expression): Either; public function get(): mixed {}; }
 class Right extends Either {} class Left extends Either {}
  • 27.
    EITHER FUNCTIONAL PHP Best practices: 1.Use Right of expected value, Left for unexpected/error 2. Use left(Closure $expression) / right(Closure $expression) when $expression returns unpacked value
 right(function(): mixed): Either 3. Use flatLeft(Closure $expression) / flatRight(Closure $expression) when $expression returns value packed in either Right or Left
 flatRight(function(): Either): Either 4. flatRight() for chaining, right() on success, left() on error 5. Don’t forget to call get() at end! - only at this point all defined expressions are evaluated
  • 28.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about throwing Exceptions 3. Forget about variables
  • 29.
    HOW? public function getUserName(string$email): Option { return $this->findUser($email) ->flatRight(function (User $user) { return new Some($user->getName()); }) ->flatLeft(function () { return new None(); }) ->get(); } private function getUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 3. Forget about variables
 Avoid declaring them (I’ve already done it)
  • 30.
    HOW? public function getUserName(string$email): Option { return $this->findUser($email) ->flatRight(function (User $user) { return new Some($user->getName()); }) ->flatLeft(function () { return new None(); }) ->get(); } private function getUser(string $email): Either { return Option::of($this->userRepository->findBy(['email' => $email])) ->map(function (User $user) { return new Right($user); }) ->getOrElse(function () { return new Left('User not found.'); }); } FUNCTIONAL PHP 3. Forget about variables: Avoid declaring them (I’ve already done it)
  • 31.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops

  • 32.
    HOW? $data = [1,2,3,4,5]; $result= []; foreach ($data as $key => $value) { $result[$key] = $value * 2; } $data = array_map(function (int $value): int { return $value * 2; }, $data); $data = array_filter($data, function (int $value): bool { return $value % 2 === 0; }); $data = array_reduce($data, function (int $prev, int $value): int { return $prev + $value; }, 0); return $data; FUNCTIONAL PHP 4. Forget about loops
  • 33.
    HOW? use PhpSlangCollectionParallelListCollection; return ParallelListCollection::of([1,2,3,4,5]) ->map(function(int $value): int { return $value * 2; }) ->filter(function (int $value): bool { return $value % 2 === 0; }) ->fold(0, function (int $prev, int $value): int { return $prev + $value; }); FUNCTIONAL PHP 4. Forget about loops
 Use Collection instead
  • 34.
    COLLECTIONS FUNCTIONAL PHP Libraries whichprovide them: 1. Doctrine collections (incomplete functionality) 2. PhpSlang 3. Immutable.php Collections provide unified API for PHP’s array_* functions and sometimes they are even faster than native PHP.
 
 Methods: 1. filter() (array_filter) 2. map() (array_map) 3. reduce() / fold() (array_reduce) 4. sort() (usort)
  • 35.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about throwing Exceptions 3. Forget about variables 4. Forget about loops 5. Use async calls wherever you can
  • 36.
    HOW? use function ClueReactBlockawaitAll; /*use try-catch because ReactPHP's awaitAll uses Exceptions to escape loop and PhpSlang's Future monad is not ready yet */ try { $argumentsArray = awaitAll([ $this->getStatus($valuesArray[‘status']), $this->getIdea($valuesArray['idea']), $this->getUserFromToken(), $valuesArray['reason'] ?? null ], LoopFactory::create()); return new Right($argumentsArray); } catch (NotFoundHttpException $e) { return new Left($e); } FUNCTIONAL PHP 5. Use async calls wherever you can
  • 37.
    HOW? FUNCTIONAL PHP 1. Forgetabout null checks 2. Forget about loops 3. Forget about throwing Exceptions 4. Forget about variables 5. Use async calls wherever you can 6. Avoid mutating the State
  • 38.
    $today = newDateTime(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format('l')}"; FUNCTIONAL PHP HOW? 6. Avoid mutating the State
  • 39.
    $today = newDateTime(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format('l')}"; // prints "Yesterday was Thursday and today is Thursday" FUNCTIONAL PHP HOW? 6. Avoid mutating the State
  • 40.
    $today = newDateTimeImmutable(); $yesterday = $today->modify('-1 day'); echo "Yesterday was {$yesterday->format('l')} and today is {$today->format(‘l')}"; // prints "Yesterday was Thursday and today is Friday" FUNCTIONAL PHP HOW? 6. Avoid mutating the State Use Immutable Classes
  • 41.
  • 42.
  • 43.
    HOW IS ITCALLED IN OTHER LANGUAGES? FUNCTIONAL PHP JS (ELM) PHP (PHPSLANG) SCALA (JAVASLANG) Maybe (Just, Nothing) Option (Some, None) Option (Some, None) Either (Right, Left) Either (Right, Left) Either (Right, Left) Promise Promise (Future?) Future async, await await, awaitAll Await.result
  • 44.
  • 45.
  • 46.
    FURTHER READING FUNCTIONAL PHP ▸Functional Programming basics ▸ http://maciejpirog.github.io/fishy/ ▸ http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html ▸ https://medium.com/@cscalfani/so-you-want-to-be-a-functional-programmer-part-1-1f15e387e536 ▸ Missing parts of PHP (maybe in 7.2…) ▸ https://wiki.php.net/rfc/generics (https://www.bountysource.com/issues/20553561-add-generics-support) ▸ https://wiki.php.net/rfc/immutability ▸ https://wiki.php.net/rfc/pipe-operator ▸ PhpSlang http://phpslang.io/ ▸ https://php-slang.github.io/php-slang-docs/static/Introduction/Thinking_Functional.html ▸ https://php-slang.github.io/php-slang-docs/static/Usage/Essentials/Option.html ▸ ReactPHP http://reactphp.org/ ▸ https://github.com/clue/php-block-react