Functional Programming in PHP: Higher-order Functions
If you examine several frameworks and large-scale applications, you’ll certainly see a higher-order function at some point. Many languages support the idea of higher-order functions, including JavaScript, Java, .NET, Python and even PHP, to name a few.
But what is a higher-order function and why would we want to use one? What advantages does it give us and can we use it to simplify our code? In this article, we’ll talk about higher-order functions in PHP specifically, but will show how they’ve been used in other languages for comparison.
First-class Functions
Before we can get into what a higher-order function is, we must first understand that we have to have a language that can support a feature called first-class functions (aka first-order functions). This means that the language treats functions as first-class citizens. In other words, the language treats functions like it does variables. You can store a function in a variable, pass it to other functions as a variable, return them from functions like variables and even store them in data structures like arrays or object properties like you can with variables. Most modern languages these days have this feature by default. All you really need to know is that a function can be passed around and used much like variables are.
For our purposes, we’ll be focusing mostly on passing functions as arguments and returning functions as results, and briefly touching on the concept of non-local variables and closures. (You can read more about these concepts in sections 1.1, 1.4 and 1.3 of the Wikipedia article that was linked to in the previous paragraph.)
What Are Higher-order Functions?
There are two main characteristics that identify a higher-order function. A higher-order function can implement just one or both of the following ideas: a function that takes one or more functions as an input or returns a function as an output. In PHP there’s a keyword that’s a clear giveaway that a function is higher-order: the keyword callable
. While this keyword doesn’t have to be present, the keyword makes it easy to identify them. If you see a function or method that has a callable parameter, it means that it takes a function as input. Another easy sign is if you see a function return a function using its return
statement. The return statement might be just the name of the function, or could even be an anonymous/in-line function. Below are some examples of each type.
// Simple user-defined function we'll pass to our higher-order function
function echoHelloWorld() {
echo "Hello World!";
}
// Higher-order function that takes a function as an input and calls it
function higherOrderFunction(callable $func) {
$func();
}
// Call our higher-order function and give it our echoHelloWorld() function.
// Notice we pass just the name as a string and no parenthesis.
// This echos "Hello World!"
higherOrderFunction('echoHelloWorld');
Here are some simple examples of higher-order functions that return a function:
// Returns an existing function in PHP called trim(). Notice it's a simple string with no parentheses.
function trimMessage1() {
return 'trim';
}
// Here's an example using an anonymous inline function
function trimMessage2() {
return function($text) {
return trim($text);
};
}
// Returns the 'trim' function
$trim1 = trimMessage1();
// Call it with our string to trim
echo $trim1(' hello world ');
// Returns the anonymous function that internally calls 'trim'
$trim2 = trimMessage1();
// Call it again with our string to trim
echo $trim2(' hello world ');
As you can imagine, you can take in a function and also use that to generate another function that’s returned. Pretty neat trick, right? But why would you want to do any of this? It sounds like something that would simply make things more complicated.
Why Would You Use or Create a Higher-order Function?
There are several reasons you might want to create higher-order functions in your code. Everything from code flexibility, code reuse, code extension or to imitate a code solution you saw in another program. While the reasons are numerous, we’ll cover a few of them here.
Adding code flexibility
Higher-order functions add a ton of flexibility. Based on the previous examples, you probably can see a few uses for something like this. You can write a single function that takes in a whole suite of different functions and uses them without having to write code to execute them individually.
Maybe the higher-order function itself doesn’t know what type of function it will receive. Who said that the function has to know anything about the function it’s taking in? One minute the input function could be an add()
function, and in the next it could be a divide()
function. In either case it just works.
Easily expand your code
This functionality also makes it easier to add additional input functions later. Let’s say you have add()
and divide()
, but down the road you need to add a sum()
function. You can write the sum()
function and pipe it through the higher-order function without ever having to change it. In a later example, we’ll demonstrate how we can use this functionality to create our own calc()
function.
Imitate features of another language
Another reason you might want to use a higher-order function in PHP is to simulate the behavior of something like a decorator in Python. You “wrap” a function inside another function to modify how that function behaves. For instance, you could write a function that times other functions. You write a higher-order function that takes in a function, starts a timer, calls the function, then ends the timer to see how much time has elapsed.
Examples of Existing Higher-order Functions in PHP
It’s really simple to find examples of higher-order functions in PHP. Look at the PHP documentation and find a function/method that takes a callable
input parameter and you’ve found one. Below are a few examples of commonly used higher-order functions. You might have even used them without ever knowing they were higher-order functions.
The higher-order dynamic duo: array_map
and array_filter
$arrayOfNums = [1,2,3,4,5];
// array_map: example of taking an inline anonymous function
$doubledNums = array_map(function($num) {
return $num * 2;
}, $arrayOfNums);
var_dump($doubledNums); // Prints out array containing [2, 4, 6, 8, 10];
Let’s see how to use another one, this time with a filter function we’ve defined separately:
$arrayOfNums = [1,2,3,4,5];
// Example of creating a function and giving it to a higher-order function array_filter()
function isEven($num) {
return ($num % 2) === 0;
}
// array_filter is a higher-order function
$evenNums = array_filter($arrayOfNums, 'isEven');
var_dump($evenNums); // Prints out array containing [2, 4]
array_filter
and array_map
are two very popular higher-order functions that you’ll find in plenty of code projects. Another one you might use is call_user_func
, which takes in a function and a list of arguments and calls on that function. This is essentially a customized higher-order function. Its whole purpose is to call other functions that the user has defined:
function printCustomMessage($message) {
echo "$message";
}
call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');
How to Create Your Own Higher-order Functions
We’ve shown lots of examples of how higher-order functions work in PHP, and we’ve already created a few custom ones to demonstrate their various purposes. But let’s show off the flexibility a bit more with a new function we’ll call calc()
. This function will take in two arguments and a function that will operate on those two arguments to give us a result:
function add($a, $b) {
return $a + $b;
}
function subtract($a, $b) {
return $a - $b;
}
function multiply($a, $b) {
return $a * $b;
}
// Higher-order function that passes along $n1 and $n2
function calc($n1, $n2, $math_func) {
return $math_func($n1, $n2);
}
$addedNums = calc(1, 5, 'add');
$subtractedNums = calc(1, 5, 'subtract');
$multipliedNums = calc(1, 5, 'multiply');
// Prints 6
echo "Added numbers: $addedNums\n";
// Prints -4
echo "Subtracted numbers: $subtractedNums\n";
// Prints 5
echo "Multiplied numbers: $multipliedNums\n";
Notice that our calc()
function was written very generically to take in a set of other functions and calls them with parameters $n1
and $n2
. In this case, our calculation function doesn’t care what the function it takes in does. It just passes along the parameters to the function and returns the result.
But wait a minute: our boss just asked us to add a function to add two strings together in our existing system. But concatenating strings isn’t your typical math operation. However, with our setup here, we just need to add in the string concatenation and pipe it through calc()
:
function addTwoStrings($a, $b) {
return $a . " " . $b;
}
// Prints "Hello World!"
$concatenatedStrings = calc('Hello', 'World!', 'addTwoStrings');
While this example is very contrived, it demonstrates how you might use the concept in bigger projects. Maybe the higher-order function determines how events are handled. Maybe, based on the event triggered, you can route it through a handler function you pass in. The possibilities are endless.
How does this compare to other languages that have higher-order functions? Let’s take a simple example decorator in Python and compare it to JavaScript and then compare it to PHP. This way, if you know Python or JS, you can see how it is equivalent.
A side-by-side comparison with Python and JavaScript
def say_message(func):
def wrapper():
print('Print before function is called')
func()
print('Print after function is called')
return wrapper
def say_hello_world():
print("Hello World!")
# Pass our hello world function to the say_message function.
# It returns a function that we can then call
# Note: This line could have been replaced with the @say_message (pie syntax) on the say_hello_world method
say_hello_world = say_message(say_hello_world)
# Call the created function
say_hello_world()
# Output...
# Print before function is called
# Hello World!
# Print after function is called
Now let’s see how this might be achieved in JavaScript higher-order functions:
// Takes a function, returns a function.
function say_message(func) {
return function() {
console.log("Print before function is called");
func();
console.log("Print after function is called");
}
}
function say_hello_world() {
console.log("Hello World!");
}
// Again pass our function to say_message as an input parameter
// say_message returns a function that is wrapped around the say_hello_world function
say_hello = say_message(say_hello_world);
// Call the function
say_hello();
// Output...
// Print before function is called
// Hello World!
// Print after function is called
Now lastly, let’s compare this with PHP:
// Takes a function, returns a function.
function say_message($func) {
// We use 'use' so that our anonymous function can see $func coming in
return function() use ($func) {
echo "Print before function is called";
$func();
echo "Print after function is called";
};
}
function say_hello_world() {
echo "Hello World!";
}
// Again pass our function to say_message as an input parameter
// say_message returns a function that is wrapped around the say_hello_world function
$say_hello = say_message('say_hello_world');
// Call the function
$say_hello();
// Output...
// Print before function is called
// Hello World!
// Print after function is called
As you can see from the side-by-side comparison, the PHP version is very similar to the JavaScript syntax. But if you know a bit about Python and decorators, you can see how decorators might be translated to one of the other two programming languages.
A Quick Word on Lambdas/Anonymous Functions
Many times, when you’re working with higher-order functions, you may see the use of inline anonymous functions (aka lambdas). In some of the examples we’ve seen above, I’ve used this format. In addition to that, I’ve also used the more explicit version of defining the function first and then passing it to our higher-order function. This is so that you get used to seeing both versions. More often than not, you’ll see the inline lambda version in frameworks that make heavy use of higher-order functions. Don’t let this format throw you. They’re just creating a simple function, without a name, and using that in place of where you’d have given the stand-alone function name.
Conclusion
In this article, we covered the basic definition of what makes a function a higher-order function. Any function that takes in a function or returns a function (or both) can be considered a higher-order function. We also talked a bit about why you might want to create these types of functions and what their advantages are.
We then showed some examples of existing higher-order functions in PHP, like array_map
and array_filter
, and then went on to create our own higher-order function called calc()
, which took several input functions and operated on a couple of arguments. We then went into a side-by-side comparison, showing how PHP’s solution looks compared to a Python decorator and JavaScript implementation.
I hope you learned a bit more about higher-order functions and why you might want to consider them in your next large-scale solution. They can offer many advantages and reduce boiler-plate code that may be redundant.
If you’d like to learn more about functional programming in PHP, you can consult our tutorial.
Thanks for reading!