How to use arrow functions in PHP? - php

I got to know about arrow functions in PHP 7.4. I tried using them like
<?php
$num = 1;
$arrowfunction = () => {
return $num + 1;
}
echo $arrowfunction();
Because I saw the => operator in the pull request. Just like javascript.
I expected '2' as the output but this didn't work! I got
Parse error: syntax error, unexpected ')' in /test.php on line 3

Arrow functions in PHP are introduced in PHP 7.4. They are a little different.
The fn keyword
The new fn keyword is now a reserved keyword.
Previously, we used to continue using function keyword.
$add = function ($valone,$valtwo) {
return $valone + $valtwo;
};
$add(1,2) // 3
With the advent of new arrow functions:
$add = fn($valone,$valtwo) => $valone + $valtwo;
$add(1,2) // 3
Parent scope
Earlier, we have to follow with the usage of the keyword use for the involvement of a variable from the parent scope.
$y = 1;
$fn = function ($x) use ($y) {
return $x + $y;
};
echo $fn(2); // 3
The expression defined in the parent scope will be implicitly captured by-value.
$y = 1;
$fn = fn($x) => $x + $y;
echo $fn(2); // 3
The above follows for $this variable inside class methods.
class foo {
public function test() {
$context = fn() => var_dump($this);
$context();
}
}
$test = new foo();
$test->test(); // object(foo)#1 (0) { }
Just like previously, we used to perform our operations by using the use keyword to take a variable from the parent scope, so this means that we cannot write the value of the variable from the function into the upper scope.
$y = 1;
$fn = fn() => $y++;
$fn(); // Has no effect
echo $y // 1
If we are thinking of assigning another variable's value from the closure then this also will not work
$y = 1;
$f = 0;
$fn = fn() => $f = $y + 1;
$fn();
echo $f; // 0
Function signatures
This is completely new in PHP, this allows us the define the type of function, variable and the value the function is returning
fn(int $x) => $x; // the argument type must be (int)
fn(): int => $x; // type of return value (int)
Errors are thrown when the defined argument type is not placed in the argument when calling the function. The error can be caught by using the TypeError type
$var = 10;
$int_fn = fn(int $x): int => $x;
var_dump($int_fn($var)); // int(10)
try {
$int_fn("foo");
} catch (TypeError $e) {
echo $e->getMessage(), "\n"; // Argument 1 passed to {closure}() must be of the type int, string given, called in x on line y
}
By PHP 7.1, they support the ?type in arguments which allows the argument to be null too.
$funn = fn(?int... $args): array => $args;
var_dump($funn(20, null, 30)); // Array(3) { [0]=> int(20) [1]=> NULL [2]=> int(30) }
If you supply a string or anything else rather than int to the above function, then you'll get an error
Argument passed to {closure}() must be of the type int or null, string given, called in x on line y
Nested arrow functions
$var = 6;
var_dump((fn() => fn() => $var)()()); // int(6)
var_dump((fn() => function() use($var) { return $var; })()()); // int(6)
Any possible errors inside the closure are not thrown unless called
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$b = 1;
fn() => $b + $c; // no error, nothing
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
$b = 1;
(fn() => $b + $c)(); // Notice: Undefined variable: c in the location on line x
If error reporting is off then you'll just get int(1)
How to use PHP. 7.4 now? For quick online testing just paste these code there
For your native system, I Just cloned this branch of php-src and compiled it using GCC and make. I did my testing via a test.php file and command line to check if everything works.
Core reference - https://wiki.php.net/rfc/arrow_functions_v2

The arrow functions can make your code shorter and more readable in some situations. They were primarily designed with a thought of using them for simple callbacks. As an example consider usort() which takes in a callback function as a user parameter.
Prior to PHP 7 you had to do something like this to define your own callback for usort():
// old syntax prior to PHP 7
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
return ($a < $b) ? -1 : 1;
}
$a = [3, 2, 5, 6, 1];
usort($a, "cmp");
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
PHP 7 has added a spaceship operator and now thanks to arrow functions you can make your code much cleaner.
// New syntax since PHP 7.4
$a = [3, 2, 5, 6, 1];
usort($a, fn($a, $b) => $a<=>$b);
foreach ($a as $key => $value) {
echo "$key: $value\n";
}
Try it online at 3v4l.org
Anonymous functions in PHP can be quite verbose, even when they only perform a simple operation, hence the reason for a shorter syntax. As another example consider the following function:
// Returns an array with each element squared - old syntax
function array_square($arr) {
return array_map(function($x) { return $x*$x; }, $arr);
}
// Returns an array with each element squared - new syntax
function array_square($arr) {
return array_map(fn($x) => $x**2, $arr);
}
print_r(array_square([1,2,3,4,5]));
Reducing the unnecessary syntax helps to understand the real purpose of the code, but keep in mind that shorter does not always mean cleaner! I would recommended to treat arrow functions with the same caution as ternary operators. Only use them when you know it helps readability, not just to make your code shorter.

Related

How to write a function that could be called like func(a)(b)(c) in php?

I need to realize function "calc" that works like that:
$sum = function($a, $b) { return $a + $b; };
calc(5)(3)(2)($sum); // 10
calc(1)(2)($sum); // 3
calc(2)(3)('pow'); // 8
I can write something like this:
function calc(){;
print_r(func_get_args());
return __FUNCTION__;
}
calc(3)(5)(2)('sum');
and it print Array ( [0] => 3 ) Array ( [0] => 5 ) Array ( [0] => 2 ) Array ( [0] => sum ).
So, when I get 'sum' in my function, i should have an array with all previous arguments.
But i have no idea, how can i pass current argument in next function call to manipulate all of them on last iteration. Or is there some sort of recursive solution?
What you're talking about is called Currying. The following code will require PHP 7, since it involves invoking a function returned from another one, which wasn't possible until PHP's Abstract Syntax Tree was implemented in that version.
First things first, you'll need a new sum() function that can operate on an arbitrary number of variables:
$sum = function(...$args) { return array_sum($args); };
Secondly, the important part. A function that returns a new anonymous function, accumulating the arguments as it goes. When you finally pass it something callable (either your $sum function, or a built-in function name like pow), it'll execute it, unpacking the arguments that it's built up.
function calc($x)
{
return function($y = null) use ($x)
{
if (is_callable($y)) {
return $y(...$x);
} else {
$args = (array) $x;
$args[] = $y;
return calc($args);
}
};
}
echo calc(5)(3)(2)($sum); // 10
echo calc(1)(2)($sum); // 3
echo calc(2)(3)('pow'); // 8
See https://3v4l.org/r0emm
(Note that internal functions will be limited to operating on the number of arguments they are defined to take - calc(2)(3)(4)('pow') will raise an error.)
This isn't a particularly common pattern to use (which is probably why you've found it hard to track down), so please for everyone who reads it's sake, think carefully about where you use it.
Credit to the curryAdd answer in this question for the starting blocks.
Edit: I stand corrected, you don't require globals it seems! Definitely use the #iainn's answer over this one.
So to achieve this you're going to have to use globals if you're not doing it within a class to maintain current state. You can see a working example of the below code here (note that it only works for PHP version 7 and above)
<?php
$sum = function(...$args) {
return array_sum($args);
};
function calc(...$args) {
global $globalArguments;
if (is_callable($args[0])) {
$callback = $args[0];
$arguments = array_map(function ($arg) {
return $arg[0];
}, $globalArguments);
return $callback(...$arguments);
}
$globalArguments[] = $args;
return __FUNCTION__;
}
echo calc(3)(2)($sum); // 5
I don't know why you want to do this, but I don't suggest it in production, globals aren't something that should really be used if you can avoid it.
function calc(int $value, Callable $function = null)
{
return function ($v) use ($value, $function) {
$f = function ($call) use ($value, $function) {
return (is_callable($call) && is_callable($function)) ? $call($function($call), $value) : $value;
};
return is_callable($v) ? $f($v) : calc($v, $f);
};
}

Anonymous function with dynamic number of arguments/parameters

So, some time ago I build a parser for jQuery Query Builder plugin, which parses the formula into PHP code that returns some calculation based on added parameters, it could range from return $a + $b; to something like
if($a == 'some_value' || $c == 'other_value') {
return $something;
} else if($b == 'something' && $d == 'anything') {
return $something_else;
} else {
return $anything;
}
and it could be even more complex. The thing is that it creates this as a string, which I then passed to another function which returns a dynamic function created with create_function, but that constructor in PHP is deprecated as of version 7.2.0.
My problem now is that I need to be able to create anonymous function with dynamic number of parameters, and those parameters need to have dynamic variable names. Here is my previous code
protected function createFunction($formula, &$data)
{
$args = '';
foreach($data as $key => $value) {
$args .= '$' . $key . ', ';
}
return create_function(substr($args, 0, strlen($args) - 2), $formula);
}
As you can see, the $formula is that dynamic PHP code I wrote above, and $data is an associative array (usually a row from database). Any ideas?
Edit: Forgot to mention, the formula itself is not the problem, as I can just use eval() function for that (I'm not concerned with security here so it's ok), it's just that I'm not sure how to add dynamic number of parameters with dynamic variable names.
You may go with Anonymous functions with this.
I had used eval in this case due to your comment :
Edit: Forgot to mention, the formula itself is not the problem, as I
can just use eval() function for that (I'm not concerned with security
here so it's ok)
class Foo
{
public function createFunction($formula, $args)
{
$func = function ($args) use ($formula) {
foreach ($args as $key => $val) {
$$key = $val;
}
return eval($formula);
};
return $func($args);
}
}
$foo = new Foo;
$foo->createFunction('echo $a + $b;', ['a' => 1, 'b' => 2]);
a live sample for your code
https://3v4l.org/HrMKN

php inline function like .net

i want to write an inline function in php like below
forexample :
$c = getCountry();
$b = getZones();
$a = [
'x' => function() use ($c, $b)
{
if ( isset($c[0]) )
return getZonesByCountryId($c[0]['id']);
else
return $b;
}
];
i get this error : "Object of class Closure could not be converted to string"
i write inline function in .net like i did it above. please help me !!!
The value of 'x' is going to be a function; the anonymous function itself will be assigned to 'x', not its return value. To assign its return value, you need to actually execute the function:
$a = ['x' => call_user_func(function() use ($c, $b) {
if (isset($c[0])) {
return getZonesByCountryId($c[0]['id']);
} else {
return $b;
}
})];
However, in this particular case it makes absolutely no sense to use such a complicated solution, when this will do just fine:
$a = ['x' => isset($c[0]) ? getZonesByCountryId($c[0]['id']) : $b];

Difference between "use" and passing a parameter to controller function

I don't have a specific problem, just looking to deepen my understanding of what's going on with Silex and with some new-ish PHP features in general. This is based off the code samples on the "usage" page of the Silex documentation:
$blogPosts = array(
1 => array(
'date' => '2011-03-29',
'author' => 'igorw',
'title' => 'Using Silex',
'body' => '...', );
$app->get('/blog/{id}', function (Silex\Application $app, $id) use ($blogPosts) {
//do stuff
}
Questions
What is the difference here between passing the $app and $id as parameters to the function, and use-ing the $blogPosts variable?
Could $blogPosts also have been passed as a parameter to the function?
Also, I more commonly see use ($app). What is the difference between use-ing the $app and passing it is a parameter?
This has nothing to do with silex and everything to do with "some new-ish PHP features".
You are creating an anonymous function (also called a closure), reusable several times with different $app and $id values, BUT with only the same $blogPosts value.
<?php
$a = "a";
$b = "b";
$c = function ($d) use ($b) {
echo $d . "." . $b . PHP_EOL;
};
$b = "c";
$e = function ($d) use ($b) {
echo $d . "." . $b . PHP_EOL;
};
$c($a); // prints a.b, and not a.c
$e($a); // prints a.c
Here, i'm building a function with $b, and once it is build, I use it with variables that do not have to be named the same way the function's argument is named.
Maybe this makes it more transparent
<?php
$a = "a1";
$b = "b1";
$f = function ($x) use ($b) {
echo $x . $b;
};
$f($a); // prints a1b1
// now let's change values of $a and $b
$a = "a2";
$b = "b2"; //--> won't be used as $b was 'b1' when declaring the function.
$f($a); // prints a2b1
?>
The use allows to import variables from the parent scope into the closure scope.
It will allow the function get() to call your closure with the appropriate param $blogPosts imported from the parent scope.

What is the good example of using 'func_get_arg' in PHP?

I just have found out that there is a function called func_get_arg in PHP which enables developer to use variant style of getting arguments.
It seems to be very useful because number of argument can now be arbitrary, but I cannot think of any good example of using it.
What are the some examples of using this function to fully benefit its polymorphic characteristic?
I usually use func_get_args() which is easier to use if wanting multiple arguments.
For example, to recreate PHP's max().
function max() {
$max = -PHP_INT_MAX;
foreach(func_get_args() as $arg) {
if ($arg > $max) {
$max = $arg;
}
}
return $max;
}
CodePad.
Now you can do echo max(1,5,7,3) and get 7.
As of php5.6, there isn't a use case of func_get_arg anymore; its functionality has been replaced by the variadic function using the ... syntax:
/**
* #param array $arguments
*/
public function poit(...$arguments)
{
foreach($arguments as $argument) {
...
}
}
This is especially useful if there are methods that are overloaded at the end; one does need to filter out the first arguments anymore, as showcased by an example:
Old style using func_get_arg:
function foo($a, $b) {
$c = array();
if (func_num_args() > 2) {
for($i = 0; $i < func_num_args()-2; $i++) {
$c[] = func_get_arg($i+2);
}
}
var_dump($a, $b, $c)
// Do something
}
foo('a', 'b', 'c', 'd');
Newer variadic style;
function foo($a, $b, …$c) {
var_dump($a, $b, $c);
// Do something
}
foo('a', 'b', 'c', 'd');
Both foo produce the same output, one is much simpler to write.
First of all, you are using the term "polymorphism" totally wrong. Polymorphism is a concept in object-oriented programming, and it has nothing to do with variable number of arguments in functions.
In my experience, all func_get_args allows you to do is add a little syntactic sugar.
Think of a function that can take any number of integers and return their sum. (I 'm cheating, as this already exists in array_sum. But cheating is good if it keeps the example simple). You could do it this way:
// you can leave "array" out; I have it because we should be getting one here
function sum1(array $integers) {
return array_sum($integers);
}
Now you would call this like so:
$sum = sum1(array(1));
$sum = sum1(array(1, 2, 3, 4));
This isn't very pretty. But we can do better:
function sum2() {
$integers = func_get_args();
return array_sum($integers);
}
Now you can call it like this:
$sum = sum2(1);
$sum = sum2(1, 2, 3, 4);
Let's say we have multiple arrays containing data in which we need to search across the keys for their values without merging these arrays.
The arrays are like:
$a = array('a' => 5, 'b' => 6);
$b = array('a' => 2, 'b' => 8);
$c = array('a' => 7, 'b' => 3);
In that case, say we need to get all the values of the key a from all the arrays. We can write a function that take in arbitrary number of arrays to search in.
// we need the key, and at least 1 array to search in
function simpleSearchArrays($key, $a1){
$arrays = func_get_args();
array_shift($arrays); // remove the first argument, which is the key
$ret = array();
foreach($arrays as $a){
if(array_key_exists($key, $a)){
$ret[] = $a[$key];
}
}
return $ret;
}
So if we use the function:
$x = simpleSearchArrays('a', $a, $b, $c);
$x will then contain array(5, 2, 7).
Personally, I don't think there is a good use case for it inside a normal function. As a control freak I like to know exactly what is being passed to my functions and I like to know exactly what I'm passing.
However, it can be use full for things like Dynamic/Static URL routing. When you are rewriting (via mod_rewrite) the URL args to a single bootstrap.
In this sense, you can have arguments that don't necessarily need to exist with every page request.
I hardly ever use func_get_arg(), but I do use its cousin func_get_args() quite a bit. Here's one example, a function along the lines of the echo statement that entity encodes all its arguments:
function ee() {
$args = func_get_args();
echo implode('', array_map('htmlentities', $args));
}
I use that function quite a bit.
Here's another useful example, a function that does the same job as SQL's COALESCE() function.
function coalesce() {
$args = func_get_args();
foreach ($args as $arg) {
if (!is_null($arg)) {
return $arg;
}
}
return null;
}
It returns the first non-null argument passed in, or null if there's no such argument.
Gets an array of the function's argument list.
This function may be used in conjunction with func_get_arg() and func_num_args() to allow user-defined functions to accept variable-length argument lists.
<?php
function foo()
{
$numargs = func_num_args();
echo "Number of arguments: $numargs \n";
if ($numargs >= 2) {
echo "Second argument is: " . func_get_arg(1) . "\n";
}
$arg_list = func_get_args();
for ($i = 0; $i < $numargs; $i++) {
echo "Argument $i is: " . $arg_list[$i] . "\n";
}
}
foo(1, 2, 3);
?>

Categories