PHP chaining callback functions

93 Views Asked by At

I've encountered this code, but I can't understand its logic. Could someone explain why the output is the way it is?

<?php

$functions = [
    
    function($next) {
        
        echo 'A' .PHP_EOL;
        $next();
    },
    
    function($next) {
        
        echo 'B' .PHP_EOL;
        $next();
    },
    
    function($next) {
        
        echo 'C' .PHP_EOL;
        $next();
    }
    
];
    
$a = function() {
    
    echo 'Main Content' .PHP_EOL;
     
};

foreach($functions as $function) {
    
    $a = fn() => $function($a);
    
}

$a();

Output:

C B A Main Content

2

There are 2 best solutions below

0
hassan On

It's a kind of reversed invocation, the code snippet is setting an initial callback function line 18 and then looping over the $functions array, overriding the old variable $a by the new function in the loop. At the end, the last function (the one with echo 'C' .PHP_EOL;) is being called, causing an initial call to the $next() callback line 14, which is - from the foreach loop - points to the b function, which is being called, causing the invocation of the $next() callback line 10 and so on. Until it reaches the initial function declaration.

0
Vinay On

The code you provided looks like from some middleware, wherein the outer function must utilize the result of inner function to perform some task

For simplicity sake I have converted the 3 iterations of the foreach loop into an equivalent code

Now when the loop starts, on its 1st iteration it will be something like (For better clarity I used the "full-form" of anonymous function instead of the shorthand notation)

$firstLoopFn = function($next) {
    echo 'A' .PHP_EOL;
    $next(); // <-- the origin $a function("Main Content") which does not call any other function
}

$firstLoopFn becomes the new $a in your original code

What happens in the 2nd iteration? Same

$secondLoopFn = function($next) {   
    echo 'B' .PHP_EOL;
    $next();  // <-- this is actually $firstLoopFn
}

$secondLoopFn becomes the new $a in your original code

Similarly the 3rd iteration

$thirdLoopFn = function($next) {   
    echo 'C' .PHP_EOL;
    $next();  // <-- this is actually $secondLoopFn
}

$thirdLoopFn becomes the new $a in your original code

Now the order, it is nothing but similar to this

$result = Function3rd( Function2d( Function1st( ... ) ) )

Function3rd will run first the then Function2d and so on

Please observe in your case the last iteration of foreach loop adds the final "layer" so it gets runs first (Function3rd here)

Also the fact that echo precedes the call to inner function in the source code so it gets run before the inner function called, to simply say function prints first then calls the inner function

    thirdLoopFn( /*echo C */ 
        secondLoopFn(  /*echo B */ 
            firstLoopFn( /*echo A */ $a)
        )
    )