php
Higher Order Functions in PHP

Higher Order Functions in PHP

This article will look at how higher order functions work in PHP and how to use them.

What is a higher order function

The general definition of a higher order function is a function that either takes another function as input or returns another function as output (Source). There are several examples in the PHP standard library that are higher order functions like array_map, array_filter, array_reduce etc.

Currying

One very simple example for a higher order function is currying. This is a technique that originated in functional programming languages like Haskel.
For this example let's suppose we have a function that multiplies two numbers like so:

function multiply(int $x, int $y): int {
    return $x * $y;
}

But say we use this function very often to multiply a number by 2. So what we could do is define a new function that creates a function that multiply by 2 like this:

function doubler(): callable {
    return function(int $x): int { 
        return multiply(2, $x);
     };
}

The function call now looks like this:

$doublerFunction = doubler();
$doublerFunction(20);

This way we implicitly call multiply but now with one argument being preset to 2. Of course this is a very simple example and hardly useful. So let's look at something more interesting.

Use cases

The first use case I'd like to bring up is Importing or reading from a file. When working with large data you might also want to consider a Generator but that is a topic for a different post.

The file we are reading is CSV (Comma Separated Values) file with headers. So for a file that looks like this:

Name Age
Peter 20
Paul 30
Marry 40

We want to return an array like this:

[
    0 => ['Peter' => '20'],
    1 => ['Paul' => '30'],
    2 => ['Marry' => '40'],
]

Assuming the file is properly formatted this is a possible solution:

declare(strict_types=1);
class CSVReader {
    public function read(string $fileName): array {
        // Read the raw content from the file into an array of strings
        $rawFileContent = file($fileName,  FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

        // Parse every line as a csv string
        $csvFileContent = array_map('str_getcsv', $rawFileContent);        

        // Remove the first line (as this is the header row)
        $header = array_shift($csvFileContent);

        // Iterate over the remaining rows with the combine function to use the header as keys and the row as values  
        return array_map(function (array $row) use ($header): array {
                return array_combine($header, $row);
            }, $csvFileContent);
    }
}

Yes, this works. But this is very inefficient. The array of lines needs to be traversed twice. Once for the CSV parsing and once for the array parsing.

In this piece of code you might also have noticed that you can invoke functions from a string like 'str_getcsv'. This is only required for global functions. For functions on objects you can do $object->funcName likewise for static methods SomeClass::staticMethod.

Scopes and other issues

PHP doesn't have the concept of block scopes like other languages. But it has a function scope. You might have noticed that use ($header) in the code. This means when calling the function use the $header variable from outside. Now you can read the content of the variable. If you want to modify it you need to reference it with &$header if header is a primitive datatype.

Another thing that PHP doesn't do is type checking of those function arguments. So for example the array $a = [1, 2, true, 3] can be mapped with array_map(function (int $i) { return $i + 1; }, $a); without any complaints. (true + 1 is 2 even with strict types)

Last words

Functional programming in PHP is possible but without a fancy syntax for those inline functions like the Arrow Notation in Javascript and a proper type checking, this is not always a good solution. Sometimes those functions are even slower than their loop equivalent. However if properly implemented like in the doctrine https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/ArrayCollection.php#L305

module those can be quite fun to work with.

# Higher Order Functions in PHP This article will look at how higher order functions work in PHP and how to use them. ## What is a higher order function The general definition of a higher order function is a function that either takes another function as input or returns another function as output ([Source](https://en.wikipedia.org/wiki/Higher-order_function)). There are several examples in the PHP standard library that are higher order functions like `array_map`, `array_filter`, `array_reduce` etc. ## Currying One very simple example for a higher order function is currying. This is a technique that originated in functional programming languages like Haskel. For this example let's suppose we have a function that multiplies two numbers like so: ```php function multiply(int $x, int $y): int { return $x * $y; } ``` But say we use this function very often to multiply a number by 2. So what we could do is define a new function that creates a function that multiply by 2 like this: ```php function doubler(): callable { return function(int $x): int { return multiply(2, $x); }; } ``` The function call now looks like this: ```php $doublerFunction = doubler(); $doublerFunction(20); ``` This way we implicitly call multiply but now with one argument being preset to 2. Of course this is a very simple example and hardly useful. So let's look at something more interesting. ## Use cases The first use case I'd like to bring up is Importing or reading from a file. When working with large data you might also want to consider a [Generator](http://php.net/manual/en/class.generator.php) but that is a topic for a different post. The file we are reading is CSV (Comma Separated Values) file with headers. So for a file that looks like this: | Name | Age | |-------|-----| | Peter | 20 | | Paul | 30 | | Marry | 40 | We want to return an array like this: ```php [ 0 => ['Peter' => '20'], 1 => ['Paul' => '30'], 2 => ['Marry' => '40'], ] ``` Assuming the file is properly formatted this is a possible solution: ```php declare(strict_types=1); class CSVReader { public function read(string $fileName): array { // Read the raw content from the file into an array of strings $rawFileContent = file($fileName, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); // Parse every line as a csv string $csvFileContent = array_map('str_getcsv', $rawFileContent); // Remove the first line (as this is the header row) $header = array_shift($csvFileContent); // Iterate over the remaining rows with the combine function to use the header as keys and the row as values return array_map(function (array $row) use ($header): array { return array_combine($header, $row); }, $csvFileContent); } } ``` Yes, this works. But this is very inefficient. The array of lines needs to be traversed twice. Once for the CSV parsing and once for the array parsing. In this piece of code you might also have noticed that you can invoke functions from a string like `'str_getcsv'`. This is only required for global functions. For functions on objects you can do `$object->funcName` likewise for static methods `SomeClass::staticMethod`. ## Scopes and other issues PHP doesn't have the concept of block scopes like other languages. But it has a function scope. You might have noticed that `use ($header)` in the code. This means when calling the function use the `$header` variable from outside. Now you can read the content of the variable. If you want to modify it you need to reference it with `&$header` if header is a primitive datatype. Another thing that PHP doesn't do is type checking of those function arguments. So for example the array `$a = [1, 2, true, 3]` can be mapped with `array_map(function (int $i) { return $i + 1; }, $a);` without any complaints. (true + 1 is 2 even with strict types) ## Last words Functional programming in PHP is possible but without a fancy syntax for those inline functions like the Arrow Notation in Javascript and a proper type checking, this is not always a good solution. Sometimes those functions are even slower than their loop equivalent. However if properly implemented like in the doctrine [collections](https://github.com/doctrine/collections/blob/master/lib/Doctrine/Common/Collections/ArrayCollection.php#L305) module those can be quite fun to work with.

Hello

edited Mar 31 at 12:29 am
3

wonderful articles, thanks so much for this nice explanation

wonderful articles, thanks so much for this nice explanation
2
547
2
3
live preview
enter atleast 10 characters
WARNING: You mentioned %MENTIONS%, but they cannot see this message and will not be notified
Saving...
Saved
With selected deselect posts show selected posts
All posts under this topic will be deleted ?
Pending draft ... Click to resume editing
Discard draft