I'm migrating my app from Slim/3 to Slim/4. Perhaps I'm confused because there're endless syntaxes for the same stuff but I composed this:
use DI\Container;
use Slim\Factory\AppFactory;
use Slim\Psr7\Request;
use Slim\Psr7\Response;
require dirname(__DIR__) . '/vendor/autoload.php';
class Config extends Container
{
}
class Foo
{
protected $config;
public function __construct(Config $config)
{
$this->config = $config;
}
public function __invoke(Request $request, Response $response, array $args): Response {
var_dump($this->config->get('pi'));
return $response;
}
}
$config = new Config();
$config->set('pi', M_PI);
var_dump($config->get('pi'));
AppFactory::setContainer($config);
$app = AppFactory::create();
$app->get('/', \Foo::class);
$app->run();
... and it isn't working as I expected because I get two entirely different instances of the container (as verified by setting a breakpoint in \DI\Container::__construct()
):
- The one I create myself with
$config = new Config();
. - One that gets created automatically at
$app->run();
and is then passed as argument to\Foo::__construct()
.
What did I get wrong?
The container attempts to resolve (and create) a new instance of the
\DI\Container
class, since this is not the interface Slim uses. Instead, try declaring the PSR-11ContainerInterface
. Then the DIC should pass the correct container instance.Example
The same "rule" applies to the request handler interface.
Full example:
Just a last note: Injecting the container is an anti-pattern. Please declare all class dependencies in your constructor explicitly instead.
Why is injecting the container (in the most cases) an anti-pattern?
In Slim 3 the "Service Locator" (anti-pattern) was the default "style" to inject the whole (Pimple) container and fetch the dependencies from it.
The Service Locator (anti-pattern) hides the real dependencies of your class.
The Service Locator (anti-pattern) also violates the Inversion of Control (IoC) principle of SOLID.
Q: How can I make it better?
A: Use
composition
and (explicit) constructor dependency injection.Dependency injection is a programming practice of passing into an object it’s collaborators, rather the object itself creating them.
Since Slim 4 you can use modern DIC like
PHP-DI
andleague/container
with the awesome "autowire" feature. This means: Now you can declare all dependencies explicitly in your constructor and let the DIC inject these dependencies for you.To be more clear: "Composition" has nothing to do with the "Autowire" feature of the DIC. You can use composition with pure classes and without a container or anything else. The autowire feature just uses the PHP Reflection classes to resolve and inject the dependencies automatically for you.