The functional programming paradigm treats program flow as an evaluation of functions. It utilizes several concepts, where the most important for us are eliminating side effects, avoiding mutable data, functions as first-class citizens and higher-order functions. The output of each function is dependent only on its input argument values, therefore, calling the same function twice has to always return the same value. It's based on declarative programming, in the sense of using expressions instead of statements.
Functional programming in PHP
Let's step aside for a moment and see how the three concepts mentioned above are related to PHP.
This is mostly a matter of a good programming style and self-discipline. Of course, PHP doesn't restrict us from violating this rule. Note that, by side effects, we also mean use cases like the following:
function sum($array) {
$sum = 0;
foreach ($array as $value) {
$sum += $value;
}
saveToDatabase($sum);
return $sum;
}
sum([5, 1, 3, 7, 9]);
Even though we have not defined the function saveToDatabase()
ourselves (for example, it comes from a framework we are using), it's still a side effect. If we execute the same function again, it will return the same value, but the end state is different. For example, it will create the record in the database twice.
This concept is simple with primitive data types, for example:
function add($first, $second) {
return $first + $second;
}
add(5, 2);
However, when working with collections, this principle requires the creation of a new collection and copying values from the old collection to the new one:
function greaterThan($collection, $threshold) {
$out = [];
foreach ($collection as $val) {
if ($val > $threshold) {
$out[] = $val;
}
}
return $out;
}
greaterThan([5, 12, 8, 9, 42], 8);
// will return: [12, 9, 42]
The preceding example shows this principle in practice.
In PHP, arrays are passed by reference for performance reasons until the first attempt to modify them. Then the interpreter will create a copy of the original array behind the scene (so called copy-on-write). However, objects are always passed as references, so we'll have to be very careful when working with them.
This concept of immutable collections (or objects in general) became very popular in JavaScript with libraries such as Immutable.js
, made by Facebook (
https://facebook.github.io/immutable-js/
), or the so-called onPush
change detection mechanism in Angular2.
Apart from making our code more predictable, when it's used appropriately, it will simplify checking for changes in large collections because, if any of its items have changed, then the entire collection is replaced by a new instance.
In order to check if two collections contain the same data, we can use the identity operator (===
three equal signs) instead of comparing the collections' items one by one.
In PHP, there are already libraries that make this task easier, for instance, Immutable.php
(
https://github.com/jkoudys/immutable.php
). Also, for example, PHP 5.5+ comes with an immutable version of DateTime
class called DateTimeImmutable
by default.
First-class citizens and higher-order functions
Now it starts to get interesting. Functions in PHP have been first-class citizens for a very long time already. Moreover, since PHP 5.3+, we can use anonymous functions, which greatly simplifies the usage of higher-order functions.
Consider a very trivial example that applies a function on every item in a collection with the built-in array_map()
function:
$input = ['apple', 'banana', 'orange', 'raspberry'];
$lengths = array_map(function($item) {
return strlen($item);
}, $input);
// $lengths = [5, 6, 6, 9];
We have used PHP's array_map()
function to iterate the array and return the length of each string. If we consider just this function call, it uses many of the concepts from multiple paradigms that we have explained above:
array_map(function($item) {
return strlen($item);
}, $input);
What this means in particular:
- Single expression
strlen($item)
and no assignments (declarative programming). - Implementation details on how the array is actually iterated are hidden from us (declarative programming).
- First-class citizens and higher-order functions (functional programming).
- Immutable data - this function call doesn't change the original, but creates a new array (functional programming).
- No side effects - everything happens inside the inner closure. If we used any variables, they would exist only inside this closure (functional programming).
Just for comparison, if we wanted to write the same example in imperative programming, it would be just one line longer:
$result = [];
foreach ($input as $value) {
$result[] = strlen($value);
}
Let's take this a little further, and say we want to get the sum of all lengths greater than 5
. First, we'll start with the most obvious imperative approach:
$input = ['apple', 'banana', 'orange', 'raspberry'];
$sum = 0;
foreach ($input as $fruit) {
$length = strlen($fruit);
if ($length > 5) {
$sum += $length;
}
}
// $sum = 21
printf("sum: %d\n", $sum);
Now, we can write the same thing using functional programming, utilizing three methods we mentioned earlier: map, filter and reduce. In PHP, these are called array_map()
, array_filter()
, and array_reduce()
respectively:
$lengths = array_map(function($fruit) {
return strlen($fruit);
}, $input);
$filtered = array_filter($lengths, function($length) {
return $length > 5;
});
$sum = array_reduce($filtered, function($a, $b) {
return $a + $b;
});
We got rid of all statements and used only expressions. The resulting code isn't short, and we had to also create three variables to hold partially processed arrays. So let's transform this into one large nested call:
$sum = array_reduce(array_filter(array_map(function($fruit) {
return strlen($fruit);
}, $input), function($length) {
return $length > 5;
}), function($a, $b) {
return $a + $b;
});
This is a little shorter; we can see the sequence of functions applied and their respective expressions in the same order. We've already encountered inconsistency in function declarations in PHP, as shown in the following code, which has been highly criticized:
array array_map(callable $callback, array $array1 [, $... ])
array array_filter(array $array, callable $callback)
mixed array_reduce(array $array, callable $callback)
These are shortened function definitions from PHP documentation. We can see that, sometimes the first argument is the iterated collection; sometimes it's the callback function. The same problem exists with string functions and their haystack-needle arguments. We can try to improve the readability a little with functional-PHP library (
https://github.com/lstrojny/functional-php
) - a collection of functions for functional programming in PHP.
The following code represents the same example as above, but uses lstrojny/functional-php
library:
use function Functional\map;
use function Functional\filter;
use function Functional\reduce_left;
$sum = reduce_left(filter(map($input, function($fruit) {
return strlen($fruit);
}), function($length) {
return $length > 5;
}), function($val, $i, $col, $reduction) {
return $val + $reduction;
});
It definitely looks better, but this is probably the best we can get when using standard PHP arrays.
Let's have a look at how the same problem could be solved in a language where arrays are objects and map, filter and reduce are its methods. Javascript, for example, is such a language, so we can rewrite the same example from above one more time:
var sum = inputs
.map(fruit => fruit.length)
.filter(len => len > 5)
.reduce((a, b) => a + b);
Note
We'll use the new ES6 standard whenever we show any JavaScript code throughout this entire book.
Well, this was quite easy and it meets all our expectations from functional programming much better than PHP. This might be the reason why we almost never use higher-order functions in PHP. They are just too hard to write, read and maintain.
Before we move on, we should look at another topic related to functional programming in PHP that is worth mentioning.
Anonymous functions in PHP
Every anonymous function is internally represented as an instance of a Closure class, shown as follows (we'll also refer to anonymous functions as closures or callables):
$count = function() {
printf("%d ", count($this->fruits));
};
var_dump(get_class($count));
// string(7) "Closure"
What's unusual is that we can bind custom $this
object when calling a closure, a concept that is very common in JavaScript but very rarely used in PHP.
Let's define a simple class that we'll use for demonstration:
class MyClass {
public $fruits;
public function __construct($arr) {
$this->fruits = $arr;
}
}
Then, test the function stored in $count
variable on two objects:
// closures_01.php
// ... the class definition goes here
$count = function() {
printf("%d ", count($this->fruits));
};
$obj1 = new MyClass(['apple', 'banana', 'orange']);
$obj2 = new MyClass(['raspberry', 'melon']);
$count->call($obj1);
$count->call($obj2);
This example prints to console the following output:
$ php closures_01.php
3
2
In PHP, we can specify what variables we want to pass from the parent scope to the closure with the use
keyword. Variables can be also passed by reference, similar to passing variables by reference on function calls. Consider the following example that demonstrates both principles:
// closures_03.php
$str = 'Hello, World';
$func = function() use ($str) {
$str .= '!!!';
echo $str . "\n";
};
$func();
echo $str . "\n";
$func2 = function() use (&$str) {
$str .= '???';
echo $str . "\n";
};
$func2();
echo $str . "\n";
We have two closures $func
and $func2
. The first one works with a copy of $str
so, when we print it outside of the function, it's unmodified. However, the second closure, $func2
works with a reference to the original variable. The output for this demo is as follows:
$ php closures_03.php
Hello, World!!!
Hello, World
Hello, World???
Hello, World???
We'll be passing objects to closures a lot in this book.
There's also a bindTo($newThis)
method with a similar purpose. Instead of evaluating the closure, it returns a new Closure object with $this
binded to $newThis
, which can be later called with for example, call_user_func()
method. When using closures inside objects, the context $this
is bind automatically, so we don't need to worry about it.
PHP defines a set of names that can be used as class methods with a special effect. These are all prefixed with two underscores __
. For our purposes, we'll be particularly interested in two of them, called __invoke()
and __call()
.
The __invoke()
method is used when we try to use an object as if it were a regular function. This is useful when we use higher-order functions because we can treat objects and functions exactly the same way.
The second __call()
method is used when we attempt to call an object method that doesn't exist (to be precise, a method that is inaccessible). It receives as arguments the original method name and an array of its arguments that was used when trying to call it.
We'll use both of these magic methods in Chapter 2, Reactive Programming with RxPHP.
The principles shown here aren't very common in PHP, but we'll meet them on several occasions when using functional programming.
Note
Throughout this entire book, we'll try to follow PSR-1 and PSR-2 coding standards (http://www.php-fig.org/psr/). However, we'll often violate them on purpose to keep the source codes as short as possible.
Now, we'll finally grasp reactive programming.