1

So I'm really confused about anonymous functions in PHP, I want to know if anonymous functions are mainly used as a matter of taste or coding style.

I'm asking this because you can achieve the same result without a callback function and less code.

So here is some test code:

$output = 10;
$people = (new People(new John));

 //VERSION 1 ANONYMOUS FUNCTION
$people->run(function ($value) use(&$output){
    $output = $output + $value;
});

var_dump($output); //RESULT => 20

 //VERSION 2 WITHOUT ANONYMOUS FUNCTION
var_dump($people->run() + $output); //RESULT => 30

You can run and see the full code here: https://www.tehplayground.com/IhWJJU0jbNnzuird

<?php

interface HumanInterface
{
    public function hello();
}

class People
{
    protected $person;
    protected $value;

    public function __construct(HumanInterface $person)
    {
        $this->person = $person;
        return $this;
    }

    public function run(callable $callback = null, $name = null)
    {
        $this->value = 10;
        if(is_callable($callback)) {
            return call_user_func($callback, $this->value);
        }

        return $this->value;
    }
}

class John implements HumanInterface
{
    public function hello()
    {
        return 'hi i am john';
    }
}
$output = 10;
$people = (new People(new John));
$people->run(function ($value) use(&$output){
    $output = $output + $value;
});

var_dump($output);
var_dump($people->run() + $output);

So my question is: why use an anonymous function? Is it a matter of personal choice?

Uffo
  • 9,628
  • 24
  • 90
  • 154

2 Answers2

1

Anonymous functions or „Closures“ are very useful if it is used as a one-time callback. If you use PHP's usort-method for example. The second parameter can be a Closure. So instead of writing a named function which is used once and then never again you use a Closure. IMHO this is the only way to use Closures: as a callback.

Tamali
  • 326
  • 2
  • 8
1

Anonymous functions are useful to pass around for later execution or to other code accepting functions. Using them can dramatically reduce the code needed to do things.

Imagine you have a UserCollection object that uses a generic Collection underneath. You want to reduce the UserCollection to just the Users from a certain country. So you could add a method findByCountry() on the UserCollection:

public function findByCountry($country) : UserCollection {
    $subset = new UserCollection;
    foreach ($this->users as $user) {
        if ($user->country === $country) {
            $subset->add($user);
        }
    }
    return $subset;
}

This is all fine, but additional finder methods will all do the same: iterate and collect into the subset with only the criteria being different. So a lot of boilerplate code.

You can separate the boilerplate from the criteria easily by adding a method find(callable $callback) on the underlying Collection, like this:

public function find(callable $criteria) : Collection {
    $subset = new Collection;
    foreach ($this->users as $user) {
        if ($criteria($user)) {
            $subset->add($user);
        }
    }
    return $subset;
}

This is a generic finder. Now your code in the UserCollection will only contain the actual criteria:

public function findByCountry($country): UserCollection {
    return $this->subset(function(User $user) {
        return $user->country === $country;
    });
}

private function subset($criteria): UserCollection {
    return new UserCollection($this->allUsers->find($criteria));
}

By separating the criteria from the boilerplate, it's much easier to grasp that you are trying to find users by country. There is no iteration code distracting from the actual criteria. So it becomes easier to understand and less effort to write. Also, you cannot accidentally mess up the iteration because it's defined elsewhere.

When using anonymous functions like this, they are very similar to using the Strategy Pattern or a FilterIterator. The notable difference being that you are creating them on the fly.

Note that you have to differentiate between Lambdas and Closures. I deliberately ignored the difference for this answer.

Gordon
  • 312,688
  • 75
  • 539
  • 559