Level-2 / Dice

Dice - a lightweight Dependency Injection Container for PHP
https://r.je/dice
433 stars 68 forks source link

Request package for Slim Framwork #135

Open adjenks opened 6 years ago

adjenks commented 6 years ago

I would love to see this released as a plugin for the Slim Framwork: https://www.slimframework.com/docs/concepts/di.html

PHP-DI has php-di/slim-bridge, which I am using now, but I'd rather use Dice. http://php-di.org/doc/frameworks/slim.html

I'm sure I could write my own adapter, but I would much sooner transition to Dice if I could just composer require level-2/dice-slim-bridge or something of the sort.

christiaangoossens commented 6 years ago

I wrote this myself as part of the framework I work with. It's a combination of Slim, Dice, the PHP-DI Invoker, and Event (league/event) with a core focus on modularization (thus: everything except the container and the kernel is a module). It's core idea is being as pretty as Laravel (without the evil static stuff, instead using Dice), with the modularization of bundles, without the config hassle of Symfony. Sadly, this code is not yet available (will update if it is).

However, Dice is not directly compatible here, because it has no static set function such as Pimple does. Slim uses this extensively for settings, request and response and all handlers. Therefore I extended Dice with a static set function, having Dice return a static key instead of a created one, if one was already set in the call stack.

I also made some more modifications, for instance adding this syntax $this->container->when(PDO::class)->call({normal Dice config array for call}) and $this->container->substitute(SomeInterface::class)->for(SomeImplementer::class), also enabling this to be done via config if preferred.

Mainly, you need to implement the following things to make it work with Slim:

In the end i have enabled myself to inject the Slim\App and then $this->app->get('/test', TestController::class . '::testGet') or $this->app->get('/test', [TestController:class, 'testGet']) which will create the TestController via Dice (thus also providing dependencies in the controller) and will inject Request, Response and $args into the testGet method.

christiaangoossens commented 6 years ago

Sadly, because I kinda created a new container, extending Dice, for the missing static set and some slightly different way of handling Interfaces, I don't think this bridge concept is very easy to implement.

TRPB commented 6 years ago

Dice doesn't have a ->set() method because I wanted to keep it purely a DI container, not a registry.

If your container is using set and get methods for instances, you don't need a DI container, you need an array. If you're using a container, the container's job is to construct the object and inject the object. So rather than:

$obj = new Obj('foo', 'bar');
$container->set('obj', $obj);

You can use Dice like so:

$dice->addRule('Obj', ['constructParams' => ['foo', 'bar']);

If you really need to set an existing instance, you can do it like so:

class NeedsObj {
    public function __construct(Obj $obj) {}
}

$obj = new Obj('foo', 'bar');

$callback = function() use ($obj) {
     return $obj;
};

$dice->addRule('NeedsObj', ['constructParams' => ['instance' => $callback]]);
christiaangoossens commented 6 years ago

Yes, to add to Tom here (as I agree), DON'T USE THIS SET FUNCTION ANYWHERE YOURSELF. It's only necessary because Pimple has it, and Slim uses it by saying that you have to provide for instance the settings key on your container. It's not part of the PSR standard, and thus you should always avoid it.

For all normal purposes a container should not have a set method.

christiaangoossens commented 6 years ago

Slim should have just allowed you to enter a config array and a PSR container seperately.

TRPB commented 6 years ago

FYI, there is a Dice-Interop package: https://packagist.org/packages/tombzombie/dice-interop

This is a wrapper for dice that implements Cointainer-Interop. If Slim can use any container that has this interface, then you can use this as a wrapper for Dice in the framework.

christiaangoossens commented 6 years ago

Sadly, Slim requires setting static values first, and then using an PSR compatible container, as there is no defined set method for it to use. Dice does by design not support this.

https://www.slimframework.com/docs/concepts/di.html

See:

Required services
Your container MUST implement these required services. 
If you use Slim’s built-in container, these are provided for you. 
If you choose a third-party container, you must define these required services on your own.

settings
Associative array of application settings, including keys:

- httpVersion
- responseChunkSize
- outputBuffering
- determineRouteBeforeAppMiddleware.
- displayErrorDetails.
- addContentLengthHeader.
- routerCacheFile.

environment
Instance of \Slim\Interfaces\Http\EnvironmentInterface.

request
Instance of \Psr\Http\Message\ServerRequestInterface.

response
Instance of \Psr\Http\Message\ResponseInterface.

router
Instance of \Slim\Interfaces\RouterInterface.

foundHandler
Instance of \Slim\Interfaces\InvocationStrategyInterface.

phpErrorHandler
Callable invoked if a PHP 7 Error is thrown. The callable MUST return an instance of 

- \Psr\Http\Message\ResponseInterface and accept three arguments:
- \Psr\Http\Message\ServerRequestInterface
- \Psr\Http\Message\ResponseInterface
- \Error

errorHandler
Callable invoked if an Exception is thrown. The callable MUST return an instance of 

- \Psr\Http\Message\ResponseInterface and accept three arguments:
- \Psr\Http\Message\ServerRequestInterface
- \Psr\Http\Message\ResponseInterface
- \Exception

notFoundHandler
Callable invoked if the current HTTP request URI does not match an application route. The callable MUST return an instance of \Psr\Http\Message\ResponseInterface and accept two arguments:

- \Psr\Http\Message\ServerRequestInterface
- \Psr\Http\Message\ResponseInterface

notAllowedHandler
Callable invoked if an application route matches the current HTTP request path but not its method. The callable MUST return an instance of \Psr\Http\Message\ResponseInterface and accept three arguments:

- \Psr\Http\Message\ServerRequestInterface
- \Psr\Http\Message\ResponseInterface
- Array of allowed HTTP methods

callableResolver
Instance of \Slim\Interfaces\CallableResolverInterface.
TRPB commented 6 years ago

If you choose a third-party container, you must define these required services on your own.

Shouldn't it then be up to the person who wants to use a different container to configure these services in whatever way the third party container requires? As long as $container->get('phpErrorHandler'); returns a relevant instance, why does it matter how the container was configured to do so?

For Dice, I'm guessing a lot of these classes will just work without any configuration anyway (though some might want to be marked as shared instances)

christiaangoossens commented 6 years ago

True, that's how it's designed, but how can you define the settings array in Dice?

Answering this myself with your post from above:

$callback = function() use ($obj) {
     return $obj;
};

$dice->addRule('NeedsObj', ['constructParams' => ['instance' => $callback]]);

You could return an array in the callback function maybe.

christiaangoossens commented 6 years ago

So for final recommendations:

TRPB commented 6 years ago

Yes, that would work. However, with Inversion of Control, you'd usually do it the other way around. Rather than having the container act as a registry for things to pull the settings from, you'd have the container pass the settings array to any class which needed it:

$dice->addRule('SomeClassThatNeedsSettings', ['constructParams' => [$settings]]);
$dice->addRule('SomeOtherClassThatNeedsSettings', ['constructParams' => [$settings]]);
christiaangoossens commented 6 years ago

Agreed. Don't write your own stuff like Slim (using a $container->get('settings')), and use the normal method Tom uses above.

adjenks commented 6 years ago

Thanks for all the great feedback guys. I'll try it all out and let you know how it goes.

garrettw commented 6 years ago

@adjenks Did anything ever come of your testing of Dice with Slim?