1. 云栖社区>
  2. PHP教程>
  3. 正文

Improving Your PHP Project Structure With Dependency Injection Part 2: Implementing It in P...

作者:用户 来源:互联网 时间:2017-12-01 20:39:57

Improving Your PHP Project Structure With Dependency Injection Part 2: Implementing It in P... - 摘要: 本文讲的是Improving Your PHP Project Structure With Dependency Injection Part 2: Implementing It in P..., << Previous: Improving Your PHP Pr... Author: Samuel Adeshina Posted on: 2015-11-09 Package: PHP Depend

<< Previous: Improving Your PHP Pr...

Author: Samuel Adeshina

Posted on: 2015-11-09

Package: PHP Dependency Injection Container

In the previous article, we talked about Dependency Injection, the Single Responsibility Principle, and the various ways it can be implemented.

Read this article all the various ways Dependency Injection can be used to improve your project structure, how Dependency Injection containers work, and how to build a Dependency Injection Container from scratch looking at a real Dependendency Injection Container package that autoload dependency classes only when they are needed.

BySamuel Adeshina Improving Your PHP Project Structure With Dependency Injection Part 2: Implementing It in P...

samshal.github.io

< email contact >

Contents Introduction Injecting Dependencies Via A Constructor Injecting Dependencies Via A Setter Method The Need For A Dependency Injection Container PDI: An Autoloading PHP Dependency Injection Container package Conclusion

Improving Your PHP Project Structure With Dependency Injection Part 2: Implementing It in P...

Introduction

In theprevious article, we talked about Dependency Injection, the Single Responsibility Principle, and the various ways it can be implemented.

I am beginning this article by giving a real life example script I just finished debugging. I had a Database Abstraction Class that helped me to manage communications to and from a database, which was working fine except errors and exceptions were not handled properly.

Instead of "trying and catching" all the exceptions within the class, I decided to write an Exception Manager class. This class provides a setter method that accepts an error id and logs the error after notifying the user.

Although, my Database class could easily inherit the Exception manager class, a Service provider or a Dependency Injection container provided more advantages because they remove the overhead that comes with class inheritance and makes it easy to develop both objects independent of each other.

The code snippet below shows a very "compressed" version of both classes.

<?php class DBManager { public function __construct() { } public function contains_error( $param ) { return (count($param) < 1) ? false: true; } public function query( $queryString ) { $result = retrieve( $queryString ); if(contains_error($result)) { throw new /Exception(1); } } } class ExceptionManager { public static $exceptionID; public function __set($value) { process_exception($value); } public function process_exception($param) { log($param); puts($param); } }?>

There are different ways the ExceptionManager object can be passed into the DBManager class as a dependency. Below follows examples of all three ways and a rewrite of the necessary parts of the above code snippet to explain them.

Injecting Dependencies Via A Constructor

A dependency can be injected into an object by passing its instance into the constructor of that object. In simpler terms, you can inject a dependency by passing as a parameter, an instance of the dependency into the __construct method of your class. For instance, assuming class A needs to inject B, B can be injected via the constructor this way:

<?php class A { public function __construct (B $instanceofB) { //do something with $instanceofB (you can call it's various methods and properties) } }?>

Going back to our first example with the DBManager and ExceptionManager objects, I can inject the ExceptionManager class into my DBManager class via the constructor as shown below:

<?php class DBManager { private $exceptionManager; public function __construct( ExceptionManager $emObject) { $this->exceptionManager = $emObject; } public function contains_error($param) { return (count($param) < 1) ? false: true; } public function query($queryString) { $result = retrieve( $queryString ); if( contains_error( $result ) ) { $this->exceptionManager::exceptionID = 1; } } }?>

From the above, we can see that instead of "throwing a new exception", our ExceptionManager class is now handling all errors and exceptions properly and then injecting this object into the DBManager class made the whole process seamless.

Injecting Dependencies Via A Setter Method

Another way a dependency can be injected into an object is via a method that accepts an instance of it as a parameter and then store it in a class variable. Just like in the example above where we equated $exceptionManager, a class variable to the instance of ExceptionManager class that was passed in via the constructor.

For a better understanding of this, let's go through the constructor method again.

The constructor which is a "special" kind of method accepts an instance of the dependency as a parameter. It then stores this instance in a class variable and every other method and property can interact and use the object value of the class variable from any scope.

A method which is usually referred to as a setter method in most literature, can be created to do this. As an example, let's create a setter method for our DBManager class.

<?php class DBManager { private $exceptionManager; public function __construct() { } public function setDependency( ExceptionManager $emObject) { $this->exceptionManager = $emObject; } public function contains_error( $param ) { return (count($param) < 1) ? false: true; } public function query( $queryString ) { $result = retrieve( $queryString ); if( contains_error( $result ) ) { $this->exceptionManager::exceptionID = 1; } } }?>

The setDependency method above does with the dependency exactly what the constructor does with it in the first example. It accepts an instance of the ExceptionManager class and sets the $exceptionManager class variable equal to this new instance.

The Need For A Dependency Injection Container

So far, we've been treating the case where we need only one dependency. In most real-life scenarios our DBManager might not only need an ExceptionManager class, it could also be dependent on a DateTime class for logging the time period of query execution or an ArrayParser class, an Iterator class or even all four at the same time.

An object is not limited to the number of dependencies it requires at a time, In fact thats the purpose of Object Oriented Designs, code reusability, we use and reuse other objects instead of rewriting them all over again.

So back to the question, our DBManager is dependent on the following objects: an ArrayParser object, a DateTime object, an Iterator object and an ExceptionManager object. How do we inject all these into our dependent class?

A dependency Injection Container or most commonly known as IoC containers, (Inversion of Control containers) can be used to handle Dependency Injection calls.

We can also use a very simple methodology to load our dependencies using the setter or constructor methods approaches. We can just pass them all as parameters or via an array. This will do, but it is not advisable. It is best to always use a Dependency Injection container or a Service provider.

There are various Dependency Inject containers for PHP such as PHP-DI, Pimple and numerous others. A search for "Dependency Injection" under the PHP tag on github would return tons of packages.

PDI: An Autoloading PHP Dependency Injection Container package

ThePDI package is more of a service provider that automatically knows when and where you need a dependency and injects it into your dependent object using an autoloader.

Usually, to inject your dependencies (if you are not using an IoC container), you use an interface, a constructor or a setter method to "manually" inject the dependency into your program.

With PDI, you do not have to do all these, you just call an instance of the dependency object and PDI injects it into the dependent class for immediate use.

For example, using PDI we could use the ExceptionManager class inside the DBManager class this way:

<?php class DBManager extends /PDI { public function __construct() { } public function contains_error( $param ) { return (count($param) < 1) ? false: true; } public function query( $queryString ) { $result = retrieve( $queryString ); if (contains_error( $result )) { $this->ExceptionManager::exceptionID = 1; $this->DateTime->log(time()); } } }?>

The DBManager class inherits the PDI class, this makes it possible to use the ExceptionManager class without injecting it via any method, PDI does this automatically. Assuming we already have a DateTime object that contains a log method, we can also load and use it automatically without first injecting it or extending, just like we did above.

PDI contains no public method or interface, it's main job is to "find" and "provide" (or inject) any dependency into your project. Internally, it has a setter and getter method that creates a variable with the name of the class which is your dependency and gets an object instance of that class, respectively.

In other words, you provide the name of the class you need to use and PDI returns an object instance of that class on the fly. I illustrate how PDI works with another example below.

You have a Car class that is dependent on an Automobile and an Engine class. The Automobile class contains two public methods called "setWheel" and "getWheel", while the Engine class contains a public property called "engineType" that accepts a string value. The code snippet below explains how both dependencies (Automobile and Engine classes) can be injected into our Car class using PDI

<?php class Car extends /PDI { public $numberOfWheel; public $engineType; public $automobileObject; public $engineObject;xxx public function setParameters() { $this->Automobile->setWheel(4); $this->Engine->engineType = "24AE1"; }xxx public function getParameters() { $this->numberOfWheel = $this->Automobile->getWheel(); $this->engineType = $this->Engine->engineType; } }?>

As we can see from the above, we did not create a new instance of either of the Automobile and Engine class. We just used them "as if" they have been instantiated before. Calling

$this->Automobile

triggered PDI's call to the

__get

method and the object of the Automobile class was returned immediately, then we could use all the methods and properties inside it, just like we also did for the Engine class

PDI completely supports PHP 7 and is still under active development but you can download it from the PHP Classes site . You can also clone it from github .

Conclusion

We have seen why dependency injection is important and how to properly inject dependencies into our programs. We talked about thePDI service provider and used code samples to explain how it can be used. In the final article to this series, I will talk about the PHP-DI and Pimple containers, compare them and explain how they both work, individually.

If you liked this article or would like to ask a question about how the PHP DI package works to make dependency injection more elegant, post a comment here.

You need to be aregistered user orlogin to post a comment

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索,以便于您获取更多的相关知识。