1

Lets say I have a closure:

$object->group(function() {
    $object->add('example');
    $object->add('example');
});

It won't work because $object is not defined in the closure.

Notice: Undefined variable: manager

So I would have to use ($object):

$object->group(function() use ($object) {
    $object->add('example');
    $object->add('example');
});

Now I want to keep it as simple as the first one so somehow $object has to be injected in to the closure.

The Laravel Framework does this with Routes for example:

Route::group(['middleware' => 'auth'], function () {
    Route::get('/', function ()    {
        // Uses Auth Middleware
    });

    Route::get('user/profile', function () {
        // Uses Auth Middleware
    });
});

I believe Laravel does this with the Reflection class.

How could I achieve this?

Melvin Koopmans
  • 2,994
  • 2
  • 25
  • 33
  • I see absolutely nothing wrong with `function() use ($object)`. There's nothing cleaner with the other approach. Absolutely zilch. – N.B. Feb 18 '17 at 09:55
  • I just want to know how Laravel does this. – Melvin Koopmans Feb 18 '17 at 09:57
  • Laravel uses `static`. You're using objects. – Marco Aurélio Deleu Feb 18 '17 at 09:58
  • @MarcoAurélioDeleu No it's a facade. Basically a static proxy to a non-static method. – Melvin Koopmans Feb 18 '17 at 09:59
  • Look at [array_map](http://php.net/array_map) function and then take a look at [call_user_func_array](http://php.net/call_user_func_array). That's how it's done. You define a parameter to your closure, then you invoke your closure with `call_user_func_array` where you specify which function to call and what parameters to pass to it. – N.B. Feb 18 '17 at 21:31

2 Answers2

1

Your goal is to know how to supply a parameter to a closure. It's achieved with call_user_func_array.

Let's define a method in a class which accepts a closure.

class MyTestClass
{
    public function doWork(callable $callback)
    {
        return call_user_func_array($callback, [$this]);
    }
}

$obj = new MyTestClass();

$obj->doWork(function(MyTestClass $obj) {
    // 
});

Note: didn't test, but I take it that's what you were after?

N.B.
  • 13,688
  • 3
  • 45
  • 55
  • In this case you still need to set the parameter, right? I want to do it just like Laravel. So somehow the dependency is injected into the closure. Maybe it the content gets cloned and appended to? I have no clue. – Melvin Koopmans Feb 18 '17 at 22:28
  • I have found a very ugly way to do it, so I just decided to stick with the parameters. What you could do is get the content of the method, like so: http://stackoverflow.com/questions/7026690/reconstruct-get-code-of-php-function. Then use `create_function` to create a function which does accept a parameter and then inject the dependency. – Melvin Koopmans Feb 18 '17 at 22:58
  • 1
    You can have variable amount of parameters in your closure. You can have nullable parameters. If you want to typehint against variable number of parameters, then you probably need to play with `Reflection` and that gets ugly (to inspect what it expects as parameters). I don't think you need to overdo it really :) – N.B. Feb 18 '17 at 23:10
0

You don't have to worry about reflection. The infamous Laravel's dependency injection container will handle it for you. All you need is to tell him what to inject when which class or interface is type hinted.

// xXxServiceProvider.php
function register(){
   ...
   this->app->bind('ObjNamespace\ObjClass', function ($app) {
       return new ObjNamepsace\ObjClass();
   });
}

Now, let's say you want an ObjNamepsace\ObjClass in your route handle.

Route::get('user/profile', function (ObjNamepsace\ObjClass $object) {
    // $object is resolved via type hinting
});

Moreover, there is three binding methods, you may choose which one suits your use-case:

  • bind: injects a new instance at every call, Laravel will create a new $object every time.

  • singleton: injects the same instance, Laravel will create the instance at first call and inject it at every call needed. Use it if you want to create $object once.

  • instance: you bind the class or interface to an object instance.

motia
  • 1,939
  • 16
  • 22
  • The laravel routes was an example of what I want to achieve. This project is not made in Laravel – Melvin Koopmans Feb 18 '17 at 10:55
  • Why is Laravels Service Container "infamous" – Melvin Koopmans Feb 18 '17 at 10:56
  • As far as I know, dependency injection usage does not differ much across frameworks. The same principles apply everywhere. – motia Feb 18 '17 at 11:40
  • The service container is exactly what makes Laravel such a popular framework if you ask me along with the facades – Melvin Koopmans Feb 18 '17 at 11:53
  • I don't suppose I know everything about Laravel DI. But I know it allows some magical code not available in all frameworks. For instance, it can inject classes though they are nor registered. Maybe it have more tricks. I agree with you facades and.DI make laravel popular. But believe me, not all people love them. – motia Feb 18 '17 at 12:05
  • Yeah of course :) People have their own preferences. But as of now Laravel is the most popular PHP framework for a reason. – Melvin Koopmans Feb 18 '17 at 12:09