I have a Slim Php (slim4) application to which I added Monolog for logging purposes. I'm adding the logger to the application like this:
$containerBuilder->addDefinitions([
LoggerInterface::class => function (ContainerInterface $c) {
$logger = new Logger('appname');
...
return $logger
This works fine for injecting the logger in most of my classes, by just doing:
public function __construct(ContainerInterface $container = null, LoggerInterface $logger)
{
// I can use $logger here
Now I'd also like to use the logger in the middleware like authentication. I don't see how I can properly do this.
I can get this working by adding the logger as a named entry in the container like this:
$containerBuilder->addDefinitions([
"LoggerInterface" => function (ContainerInterface $c) {
and then passing it to the middleware as a constructor parameter by getting it back from the container:
$middlewares[] = new MyAuthentication(..., $container->get('LoggerInterface'));
But this:
a) breaks the injection by classname for the other classes
b) is
apparently not a best practice
So what is the correct way to get this logger injected in the middleware?
Without adding the LoggerInterface as a named entry to the container, could you just inject the LoggerInterface class directly to your middleware via $container->get()? I.E. in a routes.php App function:
$container = $app->getContainer();
$app->add(new MiddleWare\MyAuthentication(..., $container->get(LoggerInterface::class)));
In short, I do not think you are going to be able to auto-wire the dependencies for the middleware as they need to be constructed before being appended to the router. You would need to explicitly inject the dependency as suggested by #Woodrow, although I would opt for method injection instead of constructor injection for the LoggerInterface as it would adhere to the LoggerAwareInterface.
You could let the dependency injection container (PHP-DI) resolve and inject all dependencies into your middleware:
LoggerInterface::class => function (ContainerInterface $container) {
return new Logger('name');
},
Then just register the middleware with the class name:
$app->add(\MiddleWare\MyAuthentication::class);
(You should not inject the container itself into the middleware)
That's all.
Related
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-11 ContainerInterface. Then the DIC should pass the correct container instance.
Example
use Psr\Http\Message\ServerRequestInterface;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
The same "rule" applies to the request handler interface.
Full example:
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
class Foo
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function __invoke(
Request $request,
Response $response,
array $args = []
): Response {
var_dump($this->container);
}
}
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 and league/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.
This happens as a result of how PHP-DI auto-registers itself. As of writing this answer, PHP-DI container auto-registers itself to the key DI\Container, and also the three implemented interfaces, on creation (see these lines of Container.php). As a result, if you type hint your constructor parameter against DI\Container or one of the three interfaces it implements, (which includes Psr\Container\ContainerInterface), PHP-DI is able to resolve itself.
ُThe problem is the use of self::class (line 110 of that file) makes DI\Container key somehow hard-coded, so although you're creating a child class of DI\Container (Config) the container still registers to same key as before. One way to overcome this is to let the container know that Config should also be resolved to itself. I see two options for this:
To register the container to same key as its class name, like what DI\Container does (This seems to be the right way to do it)
Manually registering the container after instantiating it
Here is a fully working example:
<?php
require '../vendor/autoload.php';
use DI\Container;
use Slim\Factory\AppFactory;
use Psr\Container\ContainerInterface;
use DI\Definition\Source\MutableDefinitionSource;
use DI\Proxy\ProxyFactory;
class Config extends Container
{
public function __construct(
MutableDefinitionSource $definitionSource = null,
ProxyFactory $proxyFactory = null,
ContainerInterface $wrapperContainer = null
) {
parent::__construct($definitionSource, $proxyFactory, $wrapperContainer);
// Register the container to a key with current class name
$this->set(static::class, $this);
}
}
class Foo
{
public function __construct(Config $config)
{
die($config->get('custom-key'));
}
}
$config = new Config();
$config->set('custom-key', 'Child container can resolve itself now');
// Another option is to not change Config constructor,
// but manually register the container in intself with new class name
//$config->set(Config::class, $config);
AppFactory::setContainer($config);
$app = AppFactory::create();
$app->get('/', \Foo::class);
$app->run();
Please note: As best practices suggest, you should not type hint against a concrete class (DI\Container or your Config class), instead you should consider type hinting against the interface (Psr\Container\ContainerInterface).
The problem is a misuse of a PHP-DI feature called autowiring:
Autowiring is an exotic word that represents something very simple:
the ability of the container to automatically create and inject
dependencies.
In order to achieve that, PHP-DI uses PHP's reflection to detect what
parameters a constructor needs.
If you use a factory method to create the container you can disable autowiring and the "strange" behaviour stops:
$builder = new ContainerBuilder(Config::class);
$builder->useAutowiring(false);
$config = $builder->build();
But I guess a better solution is to learn how to use autowiring properly :)
I had overlooked all these details because my code was originally written for Slim/3, which used Pimple as hard-coded default container. I had wrongly assumed they would work similarly but, albeit being container solutions, both libraries are quite different.
I am using PHP-DI 6 Container in my PHP Project. At the very begging of my program I just initialize the container and get Application class with all dependencies injected.
$container = new Container();
$application = $container->get(Application::class);
$application->initialize();
$application->run();
On the image below you can see classes that I use in my project.
Asterisk Dispatcher gets injected into Application class.
private $asteriskDispatcher;
public function __construct(AsteriskDispatcher $asteriskDispatcher)
{
$this->asteriskDispatcher = $asteriskDispatcher;
}
Then, inside AsteriskDispatcher class I need to create a list of Asterisk Manager instances, which is going to contain some dependencies as well in near future.
I don't want to inherit container through the all classes. Is there a way to initialize PHP-DI container as singleton, so I can use it any moment I want to create some objects?
This is how I am doing this right now, I just create a new instance of PHP-DI container inside my AsteriskDispatcher class and this looks so damn awful.
class AsteriskDispatcher implements AsteriskDispatcherInterface
{
private $listOfAsteriskManagers;
public function __construct()
{
$configurations = AsteriskConnectionsConfiguration::$connectionsConfiguration;
$this->listOfAsteriskManagers = new \SplDoublyLinkedList();
$container = new Container();
foreach ($configurations as $configuration)
{
$this->listOfAsteriskManagers->push($container->make(AsteriskManager::class,
array('configuration' => $configuration)));
}
}
}
I really want to understand how can I use PHP-DI container without breaking SOLID principles.
From the documentation:
If you need to use the make() method inside a service, or a controller, or whatever, it is recommended that you type-hint against FactoryInterface *. That avoids coupling your code to the container. DI\FactoryInterface is automatically bound to DI\Container so you can inject it without any configuration.
*emphasis mine
So you should change your AsteriskDispatcher constructor to be like:
public function __construct(FactoryInterface $factory) {
// your code ...
// more of your code ...
$factory->make(AsteriskManager::class, ['configuration' => $configuration]);
// the rest of your code.
}
PS: Singletons are evil (mostly).
I am trying to inject Artisan into a service so I can avoid using the facade.
Looking at the facade class reference I can see that the class I should be injecting is:
Illuminate\Console\Application
So I would assume that doing this:
<?php
namespace App\Service;
use Illuminate\Console\Application;
class DummyDataService
{
/**
* #var Application
*/
private $application;
public function __construct(
Application $application
) {
$this->application = $application;
}
public function insertDummyData()
{
$this->application->call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
}
}
...would work. However, I get the following error:
BindingResolutionException in Container.php line 824:
Unresolvable dependency resolving [Parameter #2 [ <required> $version ]] in class Illuminate\Console\Application
It works if I just call the method on the facade like so:
Artisan::call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
I can't figure out what the problem is so far. Has anyone experienced any similar issues? I try to avoid facades where possible.
Thanks in advance.
You should inject Illuminate\Contracts\Console\Kernel and not Illuminate\Console\Application to achieve what you want, so your class should look like this:
<?php
namespace App\Service;
use Illuminate\Contracts\Console\Kernel;
class DummyDataService
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
public function insertDummyData()
{
$this->kernel->call('db:seed', [
'--class' => 'DummyDataSeeder'
]);
}
}
If you take a peek at the constructor for Illuminate\Console\Application you will see that it expects a $version parameter and does not provide any sort of default. Therefore, if one is not explicitly provided it will fail because of that dependency. Honestly, this seems like a bug to me. If you look at its Symphony parent class, you will see that it provides a default string of 'UNKNOWN' in its constructor. If you modify Illuminate\Console\Application to have that same default, your commands in the code should now work.
This leaves you with two options.
Just use the Artisan facade for this instance. You should be fine using constructor injection for the rest of the facades, as this affects specifically the Artisan facade.
Change the constructor in the source code to have the default. Not ideal, as all of your changes will be lost every time you update Laravel, but it is an option. You might be able to cook up some sort of service provider that injects a version into all Illuminate\Console\Application instances as well, but I'm not sure.
I am honestly unsure if there are unforeseen ramifications of adding that default into the constructor, although I would imagine they would be minimal as it must be explicitly defined everywhere it is called. I might even make this into a PR and see if Taylor comments on it or just merges it.
I have just started using ZF2 and am really enjoying it.
One thing that puzzles me a bit is the absence of a Registry component. I realise that the Service Manager makes the Registry obsolete in most cases. I rely on it heavily and its great.
But from time to time I find myself needing access to a 'global' object, and I don't have access to the Service Manager. For example, in my Domain\User object I need access to a Zend\Log.
I don't want to make the Service Manager available in my Domain objects, since they are beautiful and pristine, and unadulterated by such considerations. I could 'new' a log instance whenever required, but I do it so often I'd rather have a preconfigured instance to hand. I could wrap it in a singleton, but that seems like a backward step. I could create my own mini-registry, but if that was a good idea, I'm sure the Zend guys would have left such a component in place.
So, what other options are there?
EDIT:
So, could I use Zend DI perhaps? I see this question partially covers it, Configuring class alias for using with Zend\Di\Di
You are exactly addressing the problem of dependency injection. I have blogged about this topic before, how to refactor towards dependency injection and what to do if you have soft dependencies like a Logger.
The idea is you inject the pre-configured logger. When your Domain\User object is created, the logger is injected. This makes the Domain\User object only dependent on the logger without having the knowledge how to create the logger. It's even better if you rely on a Logger interface, so you can swap to any logger implementation you want.
Example
As an example, I assume you are using Zend\Log. You have a Logger like Zend\Log\Logger with various writers attached. The logger implements Zend\Log\LoggerInterface.
Your Domain\User class:
namespace Domain;
Zend\Log\LoggerInterface;
class User
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function doSomething()
{
$this->logger->info('Do Something');
//
}
}
For Zend Framework, you should work with factories to inject this logger into your objects. The best way is to define the Logger as a service first, as you could reuse the logger for other objects as well.
Note I use the Service Manager and not Zend\Di here. Zend\Di is obsolete and replaced by the faster and more flexible Zend\ServiceManager. Both can achieve dependency injection.
The factory for you logger:
namespace MyModule\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Log\Logger;
class LoggerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$logger = new Logger;
// do more with $logger here
return $logger;
}
}
Then register this factory to create the service "logger" for you. In your module.config.php:
'service_manager' => array(
'factories' => array(
'logger' => 'MyModule\Factory\LoggerFactory',
),
),
Now logger is available in your service manger. For your domain model, do the same. Create a factory first:
namespace MyModule\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Domain\User;
class UserFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$logger = $serviceLocator->get('logger');
$user = new User($logger);
return $user;
}
}
Then register this one too:
'service_manager' => array(
'factories' => array(
'logger' => 'MyModule\Factory\LoggerFactory',
'Domain\User' => 'MyModule\Factory\UserFactory',
),
),
If you have access to the service locator, you can get the Domain\User and the logger is automatically injected:
$user = $sl->get('Domain\User');
My team likes the idea of constructor-injected dependencies because it makes deps very clear when looking at a class. With the use of the facades, I'm aware they can be mocked and swapped, but one would have to examine every line of a class to figure out what it depends on! I discovered that I could find the true class behind the facade with, for instance, Form::getFacadeRoot().
The controller code that I've ended up with is:
use Illuminate\Html\FormBuilder as Form;
use Illuminate\Validation\Factory as Validator;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage as Session;
use Illuminate\Http\Request as Input;
use Illuminate\Routing\Redirector as Redirect;
use Illuminate\View\Environment as View;
class HomeController extends BaseController {
protected $form;
protected $validator;
protected $session;
protected $input;
protected $redirect;
protected $view;
protected $layout = 'layouts.master';
protected $validationRules = array(
'name' => array('required', 'min:3'),
'email' => array('required', 'regex:/^.+#.+\..{2,4}$/')
);
public function __construct(Form $form, Validator $validator, Session $session,
Input $input, Redirector $redirect, View $view
) {
$this->form = $form;
$this->validator = $validator;
$this->session = $session;
$this->input = $input;
$this->redirect = $redirect;
$this->view = $view;
}
...
}
When my test does $this->client->request('Get', '/');, it errors out:
Illuminate\Container\BindingResolutionException: Unresolvable dependency resolving [Parameter #2 [ <required> $csrfToken ]].
Am I on even close to the right track here? I'm sort of making this up as I go along because I don't see much discussion on this issue. Feel free to comment on my reason for even trying; I could be sold on facades, yet.
Thanks !
You need to map the class dependencies to a class using Laravel's IoC container. This can be done using the App facade. So in your example above with the constructor
public function __construct(Form $form, Validator $validator, Session $session, Input $input, Redirector $redirect, View $view)
You would create a binding that would look something along the lines of:
App::bind('Form', function(){
return new Illuminate\Html\FormBuilder()
});
Taylor Otwell recommends using Interfaces as contracts for the class dependencies. So Ideally your finished code would look something like that below (I've slimed it down a bit for the example).
For your controller:
use Namespace\For\FormInterface;
class HomeController extends BaseController {
public function __construct(FormInterface $form)
{
$this->form = $form;
}
public function myHomePage()
{
$this->form->myFormFunction()
}
}
For the interface:
namespace Namespace\For;
interface FormInterface(){
public function myFormFunction()
}
The class to be injected:
use Namespace\For\FormInterface;
class MyFormClass implements FormInterface{
public function myFormFunction()
{
// Do some stuff here
}
}
And then finally you create the binding that brings it all together:
App::bind('Namespace\For\FormInterface', function()
{
return new MyFormClass();
});
What's happening here is every time Laravel sees an instance of FormInterface type hinted in a controller if creates a new myFormFunction() and passes it in as the param. By using interfaces it gives your class dependencies a contract to follow to ensure that they can be easily swapped without causing errors. So say your team later develops a new and improved form class you would simply update your binding like so:
App::bind('Namespace\For\FormInterface', function()
{
return new MyNewFormClass();
});
I would highly recommend looking into Service Providers as they provide an excellent way to manage packages and IoC bindings. A good article on Service Providers can be found here:
http://fideloper.com/laravel-4-where-to-put-bindings
And you can read more about Laravel's IoC container here:
http://laravel.com/docs/ioc
Furthermore if you can get your hands on a copy of the book From Apprentice to Artisan. Advanced Application Architecture With Laravel 4, by Taylor Otwell I would highly recommend a read. Its easy to follow and really goes into detail about managing dependency injection.
Hope that helps.
I think you're going to sacrifice quite a bit on readability, if you choose this route.
Ultimately, dependency injection is just a pattern to allow for testability. The facades are easily testable without injection, so I don't see much value in doing this...