How to Use "Dependency Injection" in Simple PHP Functions, and Should I Bother

How can I use Dependency Injection in simple php functions, and should I bother?

Dependency injection is a big word for "I have some more parameters in my constructor".

It's what you did before the awfull Singleton wave when you did not like globals :

<?php
class User {
private $_db;
function __construct($db) {
$this->_db = $db;
}
}

$db = new Db();
$user = new User($db);

Now, the trick is to use a single class to manage your dependencies, something like that :

class DependencyContainer 
{
private _instances = array();
private _params = array();

public function __construct($params)
{
$this->_params = $params;
}

public function getDb()
{
if (empty($this->_instances['db'])
|| !is_a($this->_instances['db'], 'PDO')
) {
$this->_instances['db'] = new PDO(
$this->_params['dsn'],
$this->_params['dbUser'],
$this->_params['dbPwd']
);
}
return $this->_instances['db'];
}
}

class User
{
private $_db;
public function __construct(DependencyContainer $di)
{
$this->_db = $di->getDb();
}
}

$dependencies = new DependencyContainer($someParams);
$user = new User($dependencies);

You must think you just another class and more complexity. But, your user class may need something to log messages like lot of other classes. Just add a getMessageHandler function to your dependency container, and some $this->_messages = $di->getMessageHandler() to your user class. Nothing to change in the rest of your code.

You'll get lot of infos on symfony's doc

Dependency Injection when using PHP's built in classes

It depends on the design requirements of your application.

If you know that your application requires a DateTime for a specific method to function, then pass in a DateTime. If you require custom functionality around a DateTime, then consider sub-classing DateTime and send that.

If you decide to use PHP's type hints in your method signature, consider using an interface instead, so that any object sent will fulfill that contract with the receiving class. This approach can also help insulate your application against incompatible changes in PHP in the future.

What is a Dependency Injection Container?

Note: the first three headings answer your questions, while the following ones answer anticipated questions and provide coverage of anything in the first two sections.

Could this be classified as a dependency injection container?

No, this does not look like a dependency injection container. A dependency injection container is meant to reduce the work that instantiation requires by determining, creating, and injecting all dependencies. Rather what you have there appears to be a combination of a factory and a service locator.

Factories abstract the creation of objects. This is essentially what your Container class is doing. By calling designated methods (i.e., newModel), your container takes on the responsibility of locating the exact object to be instantiated and constructing an instance of that object.

The reason I would call this a "poor" factory is that it's beginning to look like it might be used to locate services. Service locators work by hiding an object's dependencies: instead of being dependent on GenericService, an object might depend on a service locator. Given the service locator, it can request an instance of GenericService. I see similar behavior beginning to take hold in your add() and getInstance() methods. Service locators are generally considered anti-patterns because they abstract dependencies therefore making code impossible to test!

Is a dependency injection container comprised of one class?

It depends. You could very easily make a simple dependency injection container with one class. The issue is that the nature of a simple container tends to get more advanced into a not-so-simple container. When you start improving your pattern, you need to consider how the different components play together. Ask yourself: do they follow SOLID principles? If not, refactoring is necessary.

What is a dependency injection container?

I said it above, but again: a dependency injection container is meant to reduce the work that instantiation requires by determining, creating, and injecting all dependencies. A DIC will look at all dependencies of a class, and all dependencies those dependencies may have, and so on... In this sense, the container is responsible for hierarchically instantiating all dependencies.

The Container class you provide relies on very strict definitions of pre-defined classes. For example, classes in your model layer appear to only be dependent on a database connection. (Similar statements can be said about classes in your controller & view layer).

How does a dependency injection container find dependencies?

A dependency injection container will detect dependencies. Typically this happens through 1 of 3 mechanisms: autowiring, annotations, and definitions. PHP-DI docs provide a good idea of what all three of these entail here. In short, though: autowiring detects dependencies by reflecting on a class, annotations are used to write in dependencies using comments above a class, and definitions are used to hard-code dependencies. Personally, I prefer autowiring because it's clean & simple.

Can I create a simple dependency injection container or no?

Yes, you can. Start with the idea that an injector should be able to instantiate any object (service, view, controller, etc...). It needs to look at the relevant object and hierarchically instantiate all dependencies (hint: possibly through some method of recursion).

A quick example of a simple injector using autowiring looks like this:

<?php
class Injector
{
public function make($className)
{
$dependencies = [];

//Create reflection of the class-to-make's constructor to get dependencies
$classReflection = new ReflectionMethod($className, "__construct");

foreach($classReflection->getParameters() as $parameter) {
$dependencyName = $parameter->getClass()->getName();

//Use the injector to make an instance of the dependency
$dependencies[] = $this->make($dependencyName);
}

$class = new ReflectionClass($className);

//Instantiate the class with all dependencies
return $class->newInstanceArgs($dependencies);
}
}

Tested with something like the following, you can see how the injector recursively checks and instantiates all dependencies

class A {
protected $b;
public function __construct(B $b) { $this->b = $b; }
public function output(){ $this->b->foo(); }
}

class B {
protected $c;
public function __construct(C $c) { $this->c = $c; }
public function foo() { $this->c->bar(); }
}

class C {
public function __construct() { }
public function bar() { echo "World!"; }
}

$injector = new Injector;

$a = $injector->make("A");
//No need to manually instantiate A's dependency, B, or B's dependency, C

$a->output();

This basic injector has obvious faults. For example, there is an opportunity to create a recursion disaster if two classes are dependent on each other (there should be a check for that). However, as is, this works as a basic example of what an injector looks like.

Injector vs. Dependency Injection Container

To make this more powerful and fall under the definition of "dependency injection container", you'd want a way to share instantiated instances across multiple make() calls. For example, you may have another method called share(). This method would store the instance passed to it. Whenever a class is built through the make() method and depends on a class previously shared, instead of instantiating a new instance, it would use the already-instantiated one.

For a simple & powerful dependency injection container I suggest Auryn, but by all means, try to understand & create your own before using the ones already available.

Why to use Dependency Injection components in PHP frameworks

Why should you use Dependency Injection over the Singleton pattern?

Let's assume we have a Singleton object named DatabaseConnection which wraps a connection to a MySQL database for us and does some other neat things, who knows. Because reusing code is a good thing, we use this object in a lot of projects.

What if at some point we decide to switch one of our projects from MySQL to another database product? We would have to modify every place where we call the DatabaseConnection object and replace it with our new implementation. Or, we could modify the class itself -- but we still want to use the original one with other projects, so we end up with two implementations with the same name which is just asking for trouble, really.

And what about unit tests? We do those, of course, because we are good developers! But if we unit test a function that uses the database, we don't want the test to actually rely on the database or even change things there. There's no way to replace the DatabaseConnection with a mock object (that just returns static data) because our project is tightly coupled to it.

That's what Dependency Injection does: It helps to prevent tight coupling. If we inject the connection with $someObject->setDatabaseConnection($databaseConnection), we can inject any object there that behaves like the original one. We can inject mock objects, alternative implementations or extensions that inherit the original class.

Now a Dependency Injection Container is just a nice helper to manage object instances and their dependencies more easily, but it's not needed for doing Dependency Injection.

How to use autowire(...), create(...), and get(...) in definitions for PHP-DI?

I would say use get() only if you know why you would need it.

Then to choose between autowire() and create() is up to you: do you need autowiring or not?

Using simply create() is telling PHP-DI to just do new FooService() to create the service. If that's enough, then fine. If your service has dependencies, you can either rely on autowiring (using autowire()) or define the dependencies to inject manually using create(). It's up to you.

PHP dependency injection. Is my code here actually dependency injection container?

To me this looks like a collection of methods to instantiate objects needed by the specific API. I don't know that I would consider it dependency injection in and of itself. It is hard to tell though without seeing how this class is actually used. To me, a dependency injection container would basically containe metadata about how to instantiate various classes that implement a common interface.

So a sample set of classes that might interact to achieve dependency injection might look like:

class db_dependency_provider {
private static $class_map = array(
'postgres' => 'postgres_abstraction_class',
'mysql' => 'mysql_abstraction_class',
'oracle' => 'oracle_abstraction_class'
}

public static function get_db_abstraction($type) {
$class = self::$class_map[$type];
return new $class();
}
}

interface db_abstraction_interface {
public function connect();
public function query($query);
// etc.
}

class mysql_db_abstraction implements db_abstraction_interface {
// actual implementation
}

class postgres_db_abstraction implements db_abstraction_interface {
// actual implementation
}

class some_class_that_needs_a_db {
$db = null;
public function __construct($db) {
$this->db = $db;
}
// other methods
}

// use dependency injection container
$class = new some_class_that_needs_a_db(db_dependency_provider::get_db_abstraction('mysql'));

What is dependency injection?

Dependency Injection is passing dependency to other objects or framework( dependency injector).

Dependency injection makes testing easier. The injection can be done through constructor.

SomeClass() has its constructor as following:

public SomeClass() {
myObject = Factory.getObject();
}

Problem:
If myObject involves complex tasks such as disk access or network access, it is hard to do unit test on SomeClass(). Programmers have to mock myObject and might intercept the factory call.

Alternative solution:

  • Passing myObject in as an argument to the constructor
public SomeClass (MyClass myObject) {
this.myObject = myObject;
}

myObject can be passed directly which makes testing easier.

  • One common alternative is defining a do-nothing constructor. Dependency injection can be done through setters. (h/t @MikeVella).
  • Martin Fowler documents a third alternative (h/t @MarcDix), where classes explicitly implement an interface for the dependencies programmers wish injected.

It is harder to isolate components in unit testing without dependency injection.

In 2013, when I wrote this answer, this was a major theme on the Google Testing Blog. It remains the biggest advantage to me, as programmers not always need the extra flexibility in their run-time design (for instance, for service locator or similar patterns). Programmers often need to isolate the classes during testing.

PHP - Best practice Global database variable in class (OOP)

For global objects which can potentially be used anywhere I personally prefer singleton classes:

class DBConnection {

private static $instance = null;

private $db;
private function __construct() {
$this->db = new mysqli('localhost', 'my_user', 'my_password', 'my_db');
}

public static function getDB() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance->db;
}
}

This way you can just call: DBConnection::getDB() anywhere you need to, and this can also work with auto-loading.



Related Topics



Leave a reply



Submit