21

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

Dharman
  • 30,962
  • 25
  • 85
  • 135
weegee
  • 3,256
  • 2
  • 18
  • 32
  • So I originally downvoted this because it is obviously a question that was posed purely so that you could answer it. Apparently that is encouraged (is Community Wiki still a thing?) so I have retracted the downvote... begrudgingly. – Sean Bright Jun 18 '19 at 21:46
  • 1
    This question (and its answer) was really helpful to me. I didn't realize 7.4 had arrow functions. – zedfoxus Jun 18 '19 at 21:48
  • @SeanBright thanks a lot and yes I think that is encouraged https://stackoverflow.blog/2011/07/01/its-ok-to-ask-and-answer-your-own-questions/ – weegee Jun 18 '19 at 21:48
  • 2
    surely it has to be an on-topic question in the first place? and this is way to broad. –  Jun 18 '19 at 21:48
  • 2
    [There is a ton of useful information in the RFC](https://wiki.php.net/rfc/arrow_functions_v2) which is linked directly from the PR that you linked. – Sean Bright Jun 18 '19 at 21:49
  • Yes, I just test all of them and mentioned it here, so if people want to ask about scopes, keywords or anything else regarding this, they can refer to here @SeanBright – weegee Jun 18 '19 at 21:50
  • 2
    I think this should have been added to [Reference — What does this symbol mean in PHP?](https://stackoverflow.com/questions/3737139/reference-what-does-this-symbol-mean-in-php) instead of a new Q&A. – John Conde Jun 18 '19 at 22:08
  • 1
    @JohnConde That was the first thought that came in my mind but It will create some degree of confusion as we have many answers that address the arrow symbol https://stackoverflow.com/questions/1241819 https://stackoverflow.com/questions/1655336 https://stackoverflow.com/questions/4758791 So i thought that this needs a new Q&A – weegee Jun 18 '19 at 22:13

3 Answers3

40

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

weegee
  • 3,256
  • 2
  • 18
  • 32
  • 1
    I have added another answer about IIFE [Reference - What does this error mean in PHP?](https://stackoverflow.com/a/56658110/1839439). You can improve it if you'd like. – Dharman Jun 18 '19 at 23:05
  • 1
    Also this would be worth mentioning [How to use $this inside php closure?](https://stackoverflow.com/questions/39182290/how-to-use-this-inside-php-closure) – Dharman Jun 18 '19 at 23:09
10

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
0

We can also use arrow function inside of an another function.

This is how we can use in OOP php. Here summation is the arrow function that is called inside as an argument of the add method that is inside of calculate class. The add method accepts a callable function as an argument.

$summation = fn(...$args) => array_sum($args);
class calculate{
  public function add(callable $arrowFunctionCall) : int|float {  //Accepts callable function
     return $arrowFunctionCall(8, 12.6);
  }
}
$cal = new calculate();
echo $cal->add($summation); //Outpur will be 20.6

Also, if you want to use the arrow function inside of a normal function, you can do this by following.

$summation = fn(...$args) => array_sum($args);
function addition(callable $arrowFunctionCall) {  //Accepts callable function
    return $arrowFunctionCall(7, 18);
 }
 echo addition($summation); // Output is 25