Book Image

PHP Reactive Programming

By : Martin Sikora
Book Image

PHP Reactive Programming

By: Martin Sikora

Overview of this book

Reactive Programming helps us write code that is concise, clear, and readable. Combining the power of reactive programming and PHP, one of the most widely used languages, will enable you to create web applications more pragmatically. PHP Reactive Programming will teach you the benefits of reactive programming via real-world examples with a hands-on approach. You will create multiple projects showing RxPHP in action alone and in combination with other libraries. The book starts with a brief introduction to reactive programming, clearly explaining the importance of building reactive applications. You will use the RxPHP library, built a reddit CLI using it, and also re-implement the Symfony3 Event Dispatcher with RxPHP. You will learn how to test your RxPHP code by writing unit tests. Moving on to more interesting aspects, you will implement a web socket backend by developing a browser game. You will learn to implement quite complex reactive systems while avoiding pitfalls such as circular dependencies by moving the RxJS logic from the frontend to the backend. The book will then focus on writing extendable RxPHP code by developing a code testing tool and also cover Using RxPHP on both the server and client side of the application. With a concluding chapter on reactive programming practices in other languages, this book will serve as a complete guide for you to start writing reactive applications in PHP.
Table of Contents (18 chapters)
PHP Reactive Programming
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Functional programming


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.

Let's have a deeper look what this means:

  • Eliminating side effects: While in imperative programming side-effects were desired during program execution, in functional programming it's the exact opposite. Each function is supposed to be an individual building block whose return value is based only on its input values. Note that, in functional programming, it almost never makes sense to define a function that takes no arguments and returns no value. Assuming that functions have no side effects, this means that this function can't do anything (or at least anything observable from the outside). This is in contrast to imperative programming, where using such functions makes sense because they can modify some internal state (of an object for instance). Eliminating side effects leads to more independent and better testable code.

  • Avoiding mutable data: The concept of not modifying any input values and working with their copies works well with not creating any side effects. Executing the same function with the same input parameters will always return the same value.

  • First-class citizens and higher-order functions: In programming languages, stating that type/object/function is a first-class citizen (or first-class element) means that this entity supports operations generally available to all other entities. Usually, this includes:

    • It can be passed as a parameter to functions

    • It can be returned from a function

    • It can be assigned to a variable

    Higher-order functions have a very similar meaning and have to do at least one of these:

    • Take a function as an argument

    • Return a function as a result

    In functional programming, this concept of higher-order function is often used in connection with methods on collections such as map()filter()reduce()concat(), and zip()

Functional programming in PHP

Let's step aside for a moment and see how the three concepts mentioned above are related to PHP.

Eliminating side effects

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.

Avoiding mutable data

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.

Note

Anonymous functions and the Closure class are very well explained in the official documentation, so head over there if you have any hesitations: http://php.net/manual/en/functions.anonymous.php

PHP magic methods

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.