PHP Dependency Injection

PHP Dependency injection and Inheritance

Dependency Injection is just the process of passing objects into the constructor (or setter methods). A Dependency Injection Container (or Service Container) is nothing more but a helper to inject the correct instances of objects into the constructor.

With that said, it's clear that Dependency Injection doesn't influence PHP in any way (btw, nothing in Symfony does, it's all just plain PHP stuff).

So inheritance works just as it works with some plain PHP objects (which is a strange comparisation, as they already are plain PHP objects).

This means that if CommonRepository#getEntityManager() is public and the CommonRepository is instantiated correctly, this method should return the entity manager that was passed to its constructor.

The same applies to UserRepository: If you save the passed CommonRepository#getEntityManager() instance in the $em property, all methods can access the entity manager using this $em property. This means doing $this->em->find(...) should perfectly work ($em->find(...) shouldn't, as there is no $em variable).

tl;dr: The code you showed in your question (except from the weird extence example) works perfectly.

Php Dependency Injection and namespaces

Dependency injections is usually (if not always) done via a IoC (Inversion of control) - container. The container, or rather it's dependency injection logic takes care of creating the objects and through some magic fetches all the parameters expected to be added and creates them, also from the containers dependency injection logic.

What you are doing is rather just creating new objects. You could do that either way you wish, but personally I would probably pass the view through the constructor.

If you wish to read more about dependency injection and containers, id refer to the wiki entry about it.

You can also take a look at one of my naive implementations of a dependency container using php reflections here if you wish!

PHP Dependency Injection - to inject Container (DIC) or not?

In the application we do something like:

 $logger = new Logger();
$db = new Database($logger, 'db_host','db_user','db_user','db_pass');
$redis = new Redis($logger, 'redis_host','redis_user','redis_pass');

$acme = new Acme($db,$redis, ...other_stuf...);

This is sometimes referenced as poor man's dependecy injection

What you would rather want (as I guesss) should be something like:

$coolContainer = new SomeCoolDependencyInjectionContainer();
// ... do some configuration on the container
$app = $coolContainer->get('app'); // or whatever its usage interface is
$app->run(); // or any other way to launch the app

The container knows how to construct the application and all its components (the knowledge comes out of either configuration that you provide for the container or it's autowiring capabilities). Please note that instantiation happens outside of application. Application is supposed to get everyting already constructed.

So the bottom line is: if you're using some DI container make sure it knows what logger to use for each component that needs one (actually you might wanna do specific loggers for different parts of the app).

You might find the following links interesting (if not read yet, of course):

https://martinfowler.com/articles/injection.html

http://blog.ploeh.dk/2011/07/28/CompositionRoot/

Update on dependecies:

Usually when deciding on dependencies quantity I keep in mind:

  • Uncle Bob's rule for no more than 3
    arguments for a function / method which I personally propagate to constructors
  • single responsibility principle
  • general considerations upon proper code structure

Out of my practice it comes out that even though these ideas are somewhat general and generic following them makes my code cleaner. Cleaner -- meaning more readable and understandable on one hand, and more maintainable on ther other, that being even more important, because it lets go faster and perform refactoring or redesign with less efforts. So basically I believe that best practices here are not problem specific, but rather reflect considerations of somewhat higher level of generality.

Still, concerning final number of dependencies a class comes up with, I don't follow rule of 3 on "no matter what" basis. Occasionly I can have even four. Usually that happens if it's an auxiliary class has no real logic inside. Well, it surely has logic, but not that kind of logic. So generally if I see it's not really worth of efforts I let it stay either until some real issue arises or just forever.

Is Dependency Injection always the right answer?

No pattern is always the answer; It's important to make informed, pragmatic architecture decisions when these can't be deferred.

You are right stating that in the case of PHP, since the whole object graph has to be instantiated on each request, DI might incur in a performance penalty.

However, dependency injection is clearly not about performance. Its a way to implement inversion of control, and ultimately towards a modular design, separation of concerns, reusability and testability. These benefits greatly exceed the "compile-time" error catching ability.

In the early stages of a software project, it is very difficult and risky to predict whether it is going to be simple enough (your 30%) to forego all these benefits and go for an interconnected design instead of a modular one, where the moving parts are tightly coupled, just in name or performance.

Dependency injection, how to inject other object?

The most common design is that the dependency is a polymorphic object, that is, a base class or interface. In that case, Dependency Injection enables you to supply any subtype, including Test Doubles.

But even Concrete Dependencies come with some advantages. As the comments suggest, a dependency may be hiding its own big object graph. Additionally, Constructor Injection enables several classes to share a single instance, which you can inject into all objects that need it.

constructor injection vs method injection

I definitely prefer the first method for Laravel Controllers. At first you don't need an injected model in every method. ( Why would you inject a user model in the index function?).

Second you cannot use the benefits of RouteModelBinding any longer and have to manually check whether a model with given $id really exists and take actions accordingly. Also you cannot use specific FormRequests like CreateUserRequest that could handle validation and authorisation. (This is an optional feature though)

Also note, that a model injected in constructor is never a 'real' model with user data. So this will just give you access to the eleoquent functions. So you could as well use User::find($id) in your code. This will always give you false.

public function __construct(User $user)
{
dd($user->exists);
}

If you want to abstract things you could inject repositories in your constructor.

public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
// then the Repository is responsible for retrieving users
// and you are not coupled to Eloquent. If you later want, you can Read
// users from an XML File if you need
}

Additional info ( a bit offtopic ): Although it is very uncommon and I have never needed to change the request class you can do this by creating a custom request class like this:

namespace App;

use Illuminate\Http\Request;

class MyRequest extends Request
{
// override request methods or add your new own methods
}

And then in the global index.php :

    $response = $kernel->handle(
// instead of Illuminate\Http\Request::capture()
$request = \App\MyRequest::capture()
);

Dependency injection in PHP (slim, php-di)

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)));

php dependency injection with parameter

You need to add a definition so that PHP-DI knows how to construct your useMe object (tested under php 5.6):

$builder = new \DI\ContainerBuilder();
$builder->addDefinitions([
'useMe' => function () {
return new useMe('value_of_param');
},
]);
$container = $builder->build();

This is explained in the PHP-DI manual in the section about PHP Definitions: http://php-di.org/doc/php-definitions.html

A couple of other things you might need to change:

  1. Use . instead of + to concatenate strings: return "See you
    later on other class with my parameter " . $this->param;

  2. Need to return something from the apple() method: return
    $this->objects->go();



Related Topics



Leave a reply



Submit