whitenoisedb
whitenoisedb

Reputation: 403

Return function inside anonymous function. Why?

Considering this example from Slim PHP, which is used as middleware.

What's the difference between this,

$authenticateForRole = function ( $role = 'member' ) {
    return function () use ( $role ) {
        $user = User::fetchFromDatabaseSomehow();
        if ( $user->belongsToRole($role) === false ) {
            $app = \Slim\Slim::getInstance();
            $app->flash('error', 'Login required');
            $app->redirect('/login');
        }
    };
};    

and this,

$authenticateForRole = function ( $role = 'member' ) {
    $user = User::fetchFromDatabaseSomehow();
    if ( $user->belongsToRole($role) === false ) {
        $app = \Slim\Slim::getInstance();
        $app->flash('error', 'Login required');
        $app->redirect('/login');
    }
};

Aren't those functions the same?

Upvotes: 1

Views: 139

Answers (1)

lid
lid

Reputation: 204

In the context of the slim framework and middleware, and the particular example this question is associated with.

The logic is wrapped in a closure so it can be called at later point by the framework middleware logic and preserve the user argument for the $role.

for reference and context - Usage code for from original post

$app->get('/foo', $authenticateForRole('admin'), function () {
//Display admin control panel
});

understanding the calling context, you should see why these two are different.

from this point on when referring to the 2nd code you provided I will reference to it as $authenticateForRole2 in code.


case 1 - Fail

Using the 2nd code as shown below: will result in an immediate execution of the function $authenticateForRole2 the return value(null) will be passed as the middlewere argument. The "middlewere" argument is expected to be an callable/invokable object, and that is why this code is incorrect.

$app->get('/foo', $authenticateForRole2('admin'), function () {
//Display admin control panel
});

case 2 - lambada with no control over $role (~Works)

Partially Works, not 100% same functionality it will use the default $role as set in the declaration of the function. And you can't provide a different $role parameter, when defining the api(setting up the paths)

$app->get('/foo', $authenticateForRole2, function () {
//Display admin control panel
});

case 3 - repetition of declaration (Works but not recommended)

This should work but it will require repeating the declaration for each $role value. for example if one path is for 'admin' and the other is for 'member'

note I moved the $role inside the function, because slim pass an object as argument to the middlewere function when it is called.

$app->get('/foo', 
    function()  {  
        $role = 'admin'
        $user = User::fetchFromDatabaseSomehow();
        if ( $user->belongsToRole($role) === false ) {
            $app = \Slim\Slim::getInstance();
            $app->flash('error', 'Login required');
            $app->redirect('/login');
        }
} , function () {
//Display admin control panel
});

$app->get('/bar', 
    function (){  
        $role = 'member'
        $user = User::fetchFromDatabaseSomehow();
        if ( $user->belongsToRole($role) === false ) {
            $app = \Slim\Slim::getInstance();
            $app->flash('error', 'Login required');
            $app->redirect('/login');
        }
} , function () {
//Display admin control panel
});

case 4 ( original example )

The original example uses the closure wrapping trick to provide a DRY way for the role authentication method.

$app->get('/foo', $authenticateForRole('admin'), function () {
//Display admin control panel
});  

$app->get('/bar', $authenticateForRole('member'), function () {
//Display member control panel
});

Upvotes: 1

Related Questions