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?
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.
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.
Related
I am learning PHP using reviewing some complete PHP projects. (I know that this is a bad way, but my goal is not to be a PHP programmer!) Anyway, I faced with the following function that is weird a little to me:
function filterIt($filter): callable {
return function ($value) use ($filter) {
return filter_var($value, $filter) !== false;
};
}
I don't know what this function do and why it has been witter in such a way that a function is inside of another function! inner function returns something and main function also. Why we need such complicated function? or maybe one can make it simpler?
For this reason I want to write isEven() function as callable function like above. But I have no idea!
I don't know what that function do, but by mimicking from that:
function isEven($num): callable {
return function () use ($num) {
return $num % 2 == 0;
};
}
I couldn't debug this using var_dump or print_r .
Not sure how you are calling this in you're test, but as the function is actually returning a callable you would be able to debug it once you run it like this:
<?php
function isEven($num): callable {
return function () use ($num) {
return $num % 2 == 0;
};
}
var_dump(isEven(14)());
IMO the summary of the filterIt() function is "give me an anonymous function that filters based on $filter", and then that callable is likely passed somewhere else to be applied. I would venture to guess that the author wrote this function as a shorthand so that they did not need to write out the anonymous function definition over and over for different values of $filter.
Below is a simplified example of such behaviour:
class ExampleCollection {
protected $filters = [];
public function __construct(protected array $items) {}
public function addFilter(callable $filter) {
$this->filters[] = $filter;
}
public function getFiltered() :\Generator {
foreach($this->items as $item) {
foreach($this->filters as $filter) {
if( $filter($item) ) {
continue 2;
}
}
yield $item;
}
}
}
function makeMultipleFilter(int $value) :callable {
return function($a)use($value) {
return $a % $value === 0;
};
}
$c = new ExampleCollection([1,2,3,4,5,6,7,8,9,10]);
// add filters to exclude multiples of 3 and 4
$c->addFilter(makeMultipleFilter(3));
$c->addFilter(makeMultipleFilter(4));
foreach($c->getFiltered() as $item) {
printf("Got: %d\n", $item);
}
Output:
Got: 1
Got: 2
Got: 5
Got: 7
Got: 10
But I agree with Chris Haas' comment that the author's intent is not always obvious and is best asked of them, if possible. Further to that, not all code is exemplary, even if someone exemplary happens to have written it. Everyone writes themselves into a corner sometimes and has to resort to a confusing and/or ugly piece of code to get around it. Which is not to say that this is what that is, though it is somewhat confusing on first read.
Hi I am trying to build a class to emulate Gouette as a learning exercise:
https://github.com/FriendsOfPHP/Goutte/blob/master/README.rst
I think I am on the right track by using method chaining which I think they are doing, what I'm not sure of is how they do something like this:
$crawler->filter('h2 > a')->each(function ($node) {
print $node->text()."\n";
});
Would this be some kind of anonymous function?
This is my code so far:
class Bar
{
public $b;
public function __construct($a=null) {
}
public function chain1()
{
echo'chain1';
return $this;
}
public function loop($a)
{
echo'chain2';
return $this;
}
public function chain2()
{
echo'chain2';
return $this;
}
}
$a=array('bob','andy','sue','rob');
$bar1 = new Bar();
$bar1->chain1()->loop($a)->chain2();
I've tried to simplify the code to show just this one aspect of what your after...
class Bar
{
private $list;
public function __construct($a=null) {
$this->list = $a;
}
public function each( callable $fn )
{
foreach ( $this->list as $value ) {
$fn($value);
}
return $this;
}
}
$a=array('bob','andy','sue','rob');
$bar1 = (new Bar($a))->each(function ($value) {
print $value."\n";
});
As you can see, I've created the object with the list you have, and then just called each() with a callable. You can see the function just takes the passed in value and echoes it out.
Then in each() there is a loop across all the items in the list provided in the constructor and calls the closure ($fn($value);) with each value from the list.
The output from this is...
bob
andy
sue
rob
As for the chained calls, the idea is (as you've worked out) is to return an object which will be the start point for the next call. Some use $this (as you do) some systems (like Request commonly does) return a NEW copy of the object passed in. This is commonly linked to the idea of immutable objects. The idea being that you never change the original object, but you create a new object with the changes made in it. Psr7 Http Message, why immutable? gives some more insight into this.
i have a class and i want to call dynamicly all functions starting by default name:
class social_button
{
public function __construct()
{
[...]
}
private function social_facebook()
{[...]}
private function social_instagramm();
{[...]}
private function social_twitter();
{[...]}
[and so on]
}
My matter is, that i wont write all time:
$this->social_facebook();
$this->social_twitter();
...
because it could/will be an endless list.
So here is my questions:
Is there a way to call all functions generic/dynamic starting with "social"?
Like: $this->social_*();
(The " * " is something like a placeholder, which contains an unlimited number of chars)
Sorry for my bad english and much thanks to all answers.
Best regards
You can build the method name with the string concatenation:
$service = 'facebook';
$this->{'social_' . $service}();
or
$service = 'social_facebook';
$this->$service();
If you wan to call all of them, go with:
$services = ['facebook', 'twitter'];
foreach ($services as $service) {
$this->{'social_' . $service}();
}
Edit: See the answer by localheinz below for a better method, using reflection. get_class_methods() will only return public methods.
Building off hsz's answer:
You can get the list of a class' methods using get_class_methods(). Then you can loop through the results, and call the method if it starts with "social_".
// Get the list of methods
$class_methods = get_class_methods("social_button");
// Loop through the list of method names
foreach ($class_methods as $method_name)
{
// Are the first 7 characters "social_"?
if (substr($method_name, 0, 7) == "social_")
{
// Call the method
$this->{$method_name}();
}
}
The problem with the accepted answer is that it will not work with the example posted with the question. get_class_methods() returns only public methods, but the methods in question are marked as private.
If you want to determine all methods, use reflection instead:
class social_button
{
private function social_facebook()
{
return 'Facebook';
}
private function social_instagram()
{
return 'Instagram';
}
private function social_twitter()
{
return 'Twitter';
}
public function all()
{
$reflection = new \ReflectionObject($this);
$prefix = 'social_';
// filter out methods which do not start with the given prefix
$methods = array_filter($reflection->getMethods(), function (\ReflectionMethod $method) use ($prefix) {
return 0 === strpos($method->getName(), $prefix);
});
// invoke all methods and collect the results in an array
$results = array_map(function (\ReflectionMethod $method) {
$name = $method->getName();
return $this->$name();
}, $methods);
return $results;
}
}
$button = new social_button();
var_dump($button->all());
For reference, see:
http://php.net/manual/en/class.reflectionobject.php
http://php.net/manual/en/class.reflectionmethod.php
http://php.net/manual/en/function.array-filter.php
http://php.net/manual/en/function.array-map.php
For an example, see:
https://3v4l.org/qTkOM
I have a lot of code in some classes, when I must pre-declare array $keys = [];:
public function convertIdStringToMongoID($array_id = array())
{
$keys = [];
foreach ($array_id as $k => $id) {
$keys[] = new \MongoId($id);
}
return $keys;
}
It looks now good, how to make this more beauty?
According to this docs can try mapping your array, something like this:
public function convertIdStringToMongoID($array_id = array())
{
$func = function($id) {
return new \MongoId($id);
};
return array_map($func, $array_id);
}
Or according to this example, something like:
public function convertIdStringToMongoID($array_id = array())
{
return array_map(array($this, 'to_id'), $array_id);
}
private function to_id($id) {
return new \MongoId($id);
}
Both examples use functional programming approach.
The code you posted is clean and fast. You can write it in a more compact way using array_map() and an anonymous function:
public function convertIdStringToMongoID(array $array_id = array())
{
return array_map(function ($id) { return new \MongoId($id); }, $array_id);
}
Some people might consider it more beautiful, others will say it is slightly more difficult to read and understand it this way.
Both will agree that the execution time of this version is slightly longer than your version. However, the difference is not significant, there are other places to search for optimizations (access to disk and external resources, database queries, Mongo etc.)
I've found some solutions to a sorting problem I've been having, but the code uses anonymous functions in PHP. Im using version 5.2.17 and I believe anonymous functions are not supported.
How would I change the following blocks of code so I can use them in PHP 5.2.17
$keys = array_flip($order);
usort($items, function($a, $b) use($keys)
{
return $keys[$a['id']] - $keys[$b['id']];
});
from PHP sort multidimensional array by other array
And
$sorted = array_map(function($v) use ($data) {
return $data[$v - 1];
}, $order);
from PHP - Sort multi-dimensional array by another array
UPDATE:
One of the problems is I'm not sure how the variables $a, $b and $v are used. So I'm not sure how to create normal functions, thus bypassing the anon functions.
Both of the anonymous functions make use of the use clause to pass variables into the local scope.
You can achieve the same with object methods in which the objects have those variables as properties.
Inside the object method you then can access these.
$sorted = array_map(function($v) use ($data) {
return $data[$v - 1];
}, $order);
An exemplary mapping object then could look like:
class MapObj
{
private $data;
public function __construct($data) {
$this->data = $data;
}
public function callback($v) {
return $this->data[$v - 1];
}
}
As you can see it has the same functionality but just written in PHP 5.2 syntax.
And it's usage:
$map = new MapObj($data);
$callback = array($map, 'callback');
$sorted = array_map($callback, $order);
And that's how it works. Callbacks for object methods are always written in form of an array with two members, the first one is the object instance, and the second one is the name of the object method.
Naturally you can extend this an put the mapping function into the mapping object, so it's more straight forward:
class MapObj
{
...
public function map(array $order) {
$callback = array($this, 'callback');
return array_map($callback, $order);
}
}
New usage:
$map = new MapObj($data);
$sorted = $map->map($order);
As you can see this might make the usage a bit more straight forward. I must admit, my method naming is not really brilliant here, so I leave some room for your improvements.
Another benefit is, you can make the visibility of the callback method private then.
The situation with passing the data to work with in the callback as a parameter to the mapping function. That is because you wrote you already have a class that you want to make use of, but you can not touch the constructor. So the given example is a bit short.
Here is another example without using the constructor, I removed it:
class MyObj
{
private $data;
private function callback($v) {
return $this->data[$v - 1];
}
public function map($order, $data) {
$callback = array($this, 'callback');
$this->data = $data;
return array_map($callback, $order);
}
}
As you can see, the constructor is not needed any longer to pass the $data, but instead it's just passed into the map() method as an additional parameter. Usage:
$myObj = new MyObj(....); // somewhere.
// later on:
$myObj->map($order, $data);
// could be also:
$this->map($order, $data);
As you can see, how you set the private member variable is up to you. Do what fits the job.
What you have here is a closure over $data -- it's not 100% possible to rewrite it without an anonymous function. Here's the closest possible approximation:
function _array_sort_callback($a, $b) {
global $_array_sort_callback__keys;
return $_array_sort_callback__keys[$a['id']] - $_array_sort_callback__keys[$b['id']];
}
... {
$keys = array_flip($order);
global $_array_sort_callback__keys;
$_array_sort_callback__keys = $keys;
usort($items, "_array_sort_callback");
}
Note that I've prefixed the name of the global to try to avoid a collision. Both the function name and the global name need to be unique within your application.
Also, keep in mind that PHP 5.2.17 is obsolete and unsupported. You should migrate off of it as soon as possible.
If you want to mimic a closure, where you snapshot variables at a specific time, you can use a simple base class to serve as a container for the values, and then just define sub classes to implement the comparison logic.
Untested
// base class exists purely to capture the value of some variables at instantiation time
// kinda like a closure
class VarCapture {
protected $vars;
function __construct($vars) {
$this->vars = $vars;
}
}
class ItemComparer extends VarCapture {
function compare($a, $b) {
$keys = $this->vars['keys'];
return $keys[$a['id']] - $keys[$b['id']];
}
}
class PluckMapper extends VarCapture {
function pluck($v) {
$data = $this->vars['data'];
return $data[$v - 1];
}
}
$keys = array_flip($order);
$ic = new ItemComparer(compact('keys'));
$callable = array($ic, 'compare');
usort($items, $callable);
$pm = new PluckMapper(compact('data'));
$callable = array($mp, 'pluck');
$sorted = array_map($callable, $order);
Note that I made use of php's callback psuedo type
http://php.net/manual/en/language.types.callable.php
You can also re-write it into pre-5.3 anonymous functions, a la create_function(). Although create_function() functions don't normally act as closures, you can use some tricks (without using global variables) to make them work as closures in some limited circumstances. You encode the closed-over variables directly into the source of the function. The limitations are that data only goes one-way -- in; closed-over variables can only be "simple" data types, like numbers, strings, and arrays; and functions created with create_function are never deallocated, leaking memory; plus it is not very efficient. But I think it is sufficient for your example (assuming you only use arrays and strings and such).
$keys = array_flip($order);
usort($items, create_function('$a,$b', '$keys = '.var_export($keys,true).';
return $keys[$a["id"]] - $keys[$b["id"]];
'));
and
$sorted = array_map(create_function('$v', '$data = '.var_export($data,true).';
return $data[$v - 1];
'), $order);
The more general pre-5.3 solution, though, is to use an object and method as the closure, as in hakra's answer.