PHP之道

返回首页 Fork me on GitHub

Functional Programming in PHP

PHP supports first-class functions, meaning that a function can be assigned to a variable. Both user-defined and built-in functions can be referenced by a variable and invoked dynamically. Functions can be passed as arguments to other functions and a function can return other functions (a feature called higher-order functions).

Recursion, a feature that allows a function to call itself, is supported by the language, but most of the PHP code focus is on iteration.

Anonymous functions (with support for closures) have been present since PHP 5.3 (2009).

PHP 5.4 added the ability to bind closures to an object’s scope and also improved support for callables such that they can be used interchangeably with anonymous functions in almost all cases.

The most common usage of higher-order functions is when implementing a strategy pattern. The built-in array_filter function asks both for the input array (data) and a function (a strategy or a callback) used as a filter function on each array item.

<?php
$input = array(1, 2, 3, 4, 5, 6);

// Creates a new anonymous function and assigns it to a variable
$filter_even = function($item) {
    return ($item % 2) == 0;
};

// Built-in array_filter accepts both the data and the function
$output = array_filter($input, $filter_even);

// The function doesn't need to be assigned to a variable. This is valid too:
$output = array_filter($input, function($item) {
    return ($item % 2) == 0;
});

print_r($output);

A closure is an anonymous function that can access variables imported from the outside scope without using any global variables. Theoretically, a closure is a function with some arguments closed (e.g. fixed) by the environment when it is defined. Closures can work around variable scope restrictions in a clean way.

In the next example we use closures to define a function returning a single filter function for array_filter, out of a family of filter functions.

<?php
/**
 * Creates an anonymous filter function accepting items > $min
 *
 * Returns a single filter out of a family of "greater than n" filters
 */
function criteria_greater_than($min)
{
    return function($item) use ($min) {
        return $item > $min;
    };
}

$input = array(1, 2, 3, 4, 5, 6);

// Use array_filter on a input with a selected filter function
$output = array_filter($input, criteria_greater_than(3));

print_r($output); // items > 3

Each filter function in the family accepts only elements greater than some minimum value. Single filter returned by criteria_greater_than is a closure with $min argument closed by the value in the scope (given as an argument when criteria_greater_than is called).

Early binding is used by default for importing $min variable into the created function. For true closures with late binding one should use a reference when importing. Imagine a templating or input validation library, where closure is defined to capture variables in scope and access them later when the anonymous function is evaluated.