davesmithhayes.com

Software, music, and other musings.

Anonymity in PHP

January 21, 2017

Did you know PHP has anonymous functions? Anonymous classes? Did you know these can be extremely powerful in object oriented software design? If you did then I’m sorry for wasting your time by bringing you to this page. But if you didn’t, I’m going to explain how and when to use both anonymous functions and classes. In this post specifically, I am going to talk about the functions.

Anonymous Functions

The most common use for anonymous functions that I see in PHP code is for the array_map() function from the SPL. This function takes an anonymous function (also called closures, and represented the scalar type callable in PHP) which will manipulate a value within an array, the array to manipulate, and it returns the manipulated array. Simple enough, here’s an example that increments each value within an array.

<?php

$list = [1, 2, 3, 4];

$list = array_map(function ($n) { return ++$n; }, $list);

The declaration of the function is right in line with the array_map() function’s parameters. The array_map() function will iterate over the $list and return the modified array. If we check the output of the $list variable, we can see that each value has been incremented.

Array
(
    [0] => 2
    [1] => 3
    [2] => 4
    [3] => 5
)

So that’s a trivial example of what can be done with anonymous functions. You can assign functions to variables as well, and pass those around your script or application. This functions contain an isolated scope as well. Only the values passed as parameters to the function are accessible to the function. This is a very safe practice, but can be frustrating to new developers who come from languages like C or JavaScript where the variables in the global scope are available within functions that are declared in that same scope. Keeping the global scope out of the function falls into the specification for lambda calculus. A key feature of lambda calculus is the ability to curry in values from outside the scope of the function. Currying is very easy to achieve in PHP with the use keyword.

Curry

We are going to build another trivial anonymous function which will utilize currying, along with different ways to call functions assigned to variables in PHP.

<?php

$curried_val = 5;

$func = function ($passed) use ($curried_val) {
    return $passed + $curried_val;
};

$func(5);

The $curried_val is curried into the function and used to alter the data being passed. Calling the function assigned to the variable is very self explanatory. PHP provides functions that will also call user defined functions. Instead of use $func(5) we can actually do the following with identical results.

call_user_func($func, 5);

More Concrete Examples

Anonymous functions are usually used for simple procedures such as sorting, comparing, incrementing, decrementing, etc. Consider you have an array of objects that represent a basic user in a system.

<?php

class User
{
    public $id;
    public $username;

    __construct($id, $username)
    {
        $this->id = $id;
        $this->username = $username;
    }
}

Now that’s a real basic class. Now say we want to sort the Users by either the ID or the username set. PHP has a function called usort() that takes an array and an anonymous function to sort all of the items in the array. The function must return either -1, 0, or 0. I will explain what these numbers mean after the example. We are going to build two anonymous functions, one for comparing the ID, and the other the Username.

$users = [
    new User(77, "dave"),
    new User(0, "kelly"),
    new User(55, "adam")
];

$sort_by_id = function (User $a, User $b) {
    if ($a->id > $b->id) {
        return 1;
    }

    if ($a->id < $b->id) {
        return -1;
    }

    return 0;
};

$sort_by_username = function (User $a, User $b) {
    return strcmp($a->username, $b->username);
};

We cheated (there’s no cheating in code) here and decided to use PHP’s strcmp() function which will return the proper -1, 0, 1 pattern that is required for usort(). If the result of the comparator is -1, $a should come after $b. If the result is 0, nothing changes. If the result is 1, then $a will reside before $b. So let’s tie it all together.

// by the id
usort($users, $sort_by_id);
print_r($users);

// by the username
usort($users, $sort_by_username);
print_r($users);

Note that the usort() function takes a reference to the array, as opposed to the array_map() function which will return a new array. This is an interesting gotcha that is a plight to a lot of developers coming from languages such as JavaScript or Ruby. The output of the above shows how the functions ordered the array, starting with sorting by the id of the User class, and the second sorting by the username.

Array
(
    [0] => User Object
        (
            [id] => 0
            [username] => kelly
        )

    [1] => User Object
        (
            [id] => 55
            [username] => adam
        )

    [2] => User Object
        (
            [id] => 77
            [username] => dave
        )

)

Array
(
    [0] => User Object
        (
            [id] => 55
            [username] => adam
        )

    [1] => User Object
        (
            [id] => 77
            [username] => dave
        )

    [2] => User Object
        (
            [id] => 0
            [username] => kelly
        )

)

Conclusion

Anonymous functions are useful for when you want other developers to implement something for your function. As shown above, the most common use is for sorting and mapping functions. This allows the developer to use more intricate and interesting methods of manipulating data. They are very easy to handle in PHP and a very powerful construct.

To Top