Decoupling Your Objects Using Dependency Injection - The Grumpy Programmer's Guide To Building Testable PHP Applications (2015)

The Grumpy Programmer's Guide To Building Testable PHP Applications (2015)

5. Decoupling Your Objects Using Dependency Injection

Whenever I encounter a code base for the first time, my initial thought is about how I can test individual components as I modify parts of the application to meet new requirements or to fix bugs. In a perfect world, you will inherit an application with lots of relevant tests and great code coverage.

Most of the time you have an application that is working…and no way to test components in isolation. That doesn’t mean the application sucks, it just means it will be difficult to make changes without the nagging feeling that you might have broken something else.

Most PHP developers are using object-oriented programming in their applications. You create objects to accomplish certain tasks, and if you went with a Model-View-Controller design pattern you are hopefully isolating your business logic in models, handling your routing requests in your controllers, and displaying output in nicely marked-up templates.

The problem with testing some of these things is the tendency for most developers to create tightly-coupled objects that perform tasks that often stray from their original purpose. So if you have a module of code that you need to test, the first thing to look at is decoupling things.

To guide you in your attempts to decouple your code and move it towards a scenario where it consists of well-crafted modules that talk to each other via rainbows and unicorn sightings, the first tool I offer you is the concept known as the Law of Demeter.

One of my all-time favourite programming books “The Pragmatic Programmer” describes the Law of Demeter like this:

The Law of Demeter for functions states that any method of an object should call only methods belonging to itself, any parameters that were passed in to the method, any objects it created, and any directly held component objects.

So what is really the point? The point is that by sticking to these rules as much as you can, you will reduce the friction when making changes to your code and also make it easier to test, since you will be able to use mock objects when really trying to test components in isolation.

However, one of the downsides of really embracing the Law of Demeter is that you will find yourself writing a lot of “wrapper code”, code that takes what was passed to it and then hands it off to some other function or object for processing.

How can we stay on the positive side of LoD? It’s quite simple: we use Dependency Injection (hereafter DI) to pass in the various objects and values that our classes need to get the job done and don’t force them to assume anything about the dependencies we give them except their public interfaces.

The method I prefer to use is called Constructor Injection. Typically I will create a constructor for my object that accepts all the dependencies it will require. This is easily accomplished by creating a __construct() method for your class. I think some code examples are in order.

In our sample application, we are using two types of object to interact with our data source. A Model object that provides a representation of a single record in our database and a Data Mapper object that speaks to our data source and in turn creates Model objects.

Now, if I wasn’t concerned about testability I would create one big object that performed all that functionality for me. I’d create a connection to my database in the constructor and then write methods that talk to the database and send me back the results I want. Makes sense, right? Not from a testing perspective.

If we apply the Law of Demeter here, by the rules our Model doesn’t need to know anything about a Mapper except how to use it. That way we are free to do things like provide mock Mappers if we really want to get down and dirty about testing things.

So what if I want to test things out? If I’ve hard-coded the details and creation of my DB connection in the constructor for my Mapper, then I cannot run any tests on a database dedicated just to testing. Here’s a test showing why just a sprinkling of DI lets you easily test your code.

1 <?php

2

3 include 'test_bootstrap.php';

4

5 class FranchiseModelTest extends \PHPUnit_Framework_TestCase

6 {

7 protected $_conn;

8

9 public function setUp()

10 {

11 $this->_conn = new PDO(

12 'pgsql:host=localhost;dbname=ibl_stats',

13 'stats',

14 'st@ts=Fun'

15 );

16 }

17

18 ...

19

20 public function testSaveUpdatesDatabase()

21 {

22 $mapper = new IBL\FranchiseMapper($this->_conn);

23 $franchise = new IBL\Franchise();

24 $franchise->setId(25);

25 $franchise->setNickname('TST');

26 $franchise->setName('Test Team');

27 $franchise->setConference('Conference');

28 $franchise->setDivision('Division');

29 $franchise->setIp(0);

30 $mapper->save($franchise);

31

32 // Update existing model

33 $franchise->setIp(35);

34 $mapper->save($franchise);

35

36 // Reload Franchise record and compare them

37 $franchise2 = $mapper->findById($franchise->getId());

38 $this->assertEquals(35, $franchise2->getIp());

39

40 // Clean up the franchise

41 $mapper->delete($franchise);

42 }

43 }

This makes sense, right? Our Mapper doesn’t need to know the details of creating a connection to the data source. It just needs to know that it has been given a connection to use.

To further see this at work, you can even look at the save() method for FranchiseMapper.

1 <?php

2

3 namespace IBL;

4

5 class FranchiseMapper

6 {

7 protected $_conn;

8 protected $_map = array();

9

10 ...

11

12 public function save(\IBL\Franchise $franchise)

13 {

14 if ($this->findById($franchise->getId())) {

15 $this->_update($franchise);

16 } else {

17 $this->_insert($franchise);

18 }

19 }

20

21 protected function _insert(\IBL\Franchise $franchise)

22 {

23 try {

24 $sql = "

25 INSERT INTO franchises

26 (nickname, name, conference, division, ip, id)

27 VALUES(?, ?, ?, ?, ?, ?)";

28 $sth = $this->_conn->prepare($sql);

29 $binds = array(

30 $franchise->getNickname(),

31 $franchise->getName(),

32 $franchise->getConference(),

33 $franchise->getDivision(),

34 $franchise->getIp(),

35 $franchise->getId()

36 );

37 $sth->execute($binds);

38 } catch(\PDOException $e) {

39 echo "A database problem occurred: " . $e->getMessage();

40 }

41 }

42

43 protected function _update(\IBL\Franchise $franchise)

44 {

45 try {

46 $sql = "

47 UPDATE franchises

48 SET nickname = ?,

49 name = ?,

50 conference = ?,

51 division = ?,

52 ip = ?

53 WHERE id = ?";

54 $sth = $this->_conn->prepare($sql);

55 $fields = array(

56 'nickname',

57 'name',

58 'conference',

59 'division',

60 'ip',

61 'id'

62 );

63 $binds = array();

64

65 foreach ($fields as $fieldName) {

66 $field = $this->_map[$fieldName];

67 $getProp = (string)$field->accessor;

68 $binds[] = $franchise->{$getProp}();

69 }

70

71 $sth->execute($binds);

72 } catch(\PDOException $e) {

73 echo "A database problem occurred: " . $e->getMessage();

74 }

75 }

76 }

Again, we’re telling our methods that “hey, here’s an item that you need to use” and it knows only how to use it, not what the internals look like.

Here’s a scenario that might make more sense of how to refactor code with some help from LoD and DI.

Imagine you’re creating your own framework and you are writing an Access Control Layer (ACL for short) component. For those not familiar with the idea, ACLs take a look at the resource you are trying to access and compare it to the user level you currently have, then tells you whether or not that user can access that resource. It might look something like this:

1 <?php

2

3 namespace Grumpy;

4

5 class Acl {

6 protected $_acls;

7

8 ...

9

10 public function accessAllowed()

11 {

12 $request = \Grumpy\Context::getRequest();

13

14 return ($this->_acls[$request->getUri()] >= $_SESSION['user_level']);

15 }

16

17 }

18

19 // Meanwhile inside your controller

20

21 $acl = new \Grumpy\Acl();

22

23 if (!$acl->accessAllowed()) {

24 \Grumpy\View::render('access_denied.tmpl');

25 } else {

26 \Grumpy\View::render('show_stolen_cc.tmpl');

27 }

We’re pulling in our request object via a static method call, and we’re getting user info right out of the session. Very common coding practices but not testable at all. Let’s clean this up a bit.

1 <?php

2

3 namespace Grumpy;

4

5 class Acl {

6 protected $_acls;

7 protected $_request;

8 protected $_userLevel;

9

10 ...

11

12 public function __construct($request, $userLevel)

13 {

14 ...

15

16 $this->_request = $request;

17 $this->_userLevel = $userLevel;

18 }

19

20 ...

21

22 public function accessAllowed()

23 {

24 return ($this->_acls[$this->_request->getUri()]

25 >= $this->_userLevel);

26 }

27

28 }

29

30 // Meanwhile inside your controller

31

32 $acl = new \Grumpy\Acl($this->request, $_SESSION['user_level']);

33

34 if (!$acl->accessAllowed()) {

35 \Grumpy\View::render('access_denied.tmpl');

36 } else {

37 \Grumpy\View::render('show_stolen_cc.tmpl');

38 }

Now that you’ve learned about how to use Constructor Injection to pass dependencies into your objects, we can explore cranking up the power level through the use of Dependency Injection Containers, hereafter referred to as a DIC.

At some point your application becomes large enough that you will find yourself having to manage not only a lot of objects, but complicated objects. You may ask yourself how to move these objects around for the purposes of dependency injection. The best way to handle this is to put the objects you know you will need to move around into a container and then simply retrieve them when you need them.

So what sorts of applications need things like this? The prime candidate for getting the most use of a DIC is a web application framework. Why? Most frameworks are heavily object oriented and use a large number of objects to accomplish tasks. You can really make it easier for users of the framework if you place objects that they are likely to need to use frequently inside the container. They can then be retrieved for later use, already configured and ready to go.

Consider our application with its Model and Mapper structure. We could use a container to store our database connection object instead of always creating the instance ourselves. Here, we will use Pimple as our DIC.

1 <?php

2

3 // Inside our bootstrap

4

5 // Create our container

6 $container = new \Pimple();

7

8 // Inject our DB connection object for later reuse

9 $container['db_connection'] = function ($c) {

10 return new PDO(

11 'pgsql:host=localhost;dbname=ibl_stats',

12 'stats',

13 'st@ts=Fun'

14 );

15 };

16

17

18 // Then in a controller somewhere...

19 $mapper = new IBL\FranchiseMapper($container['db_connection']);

The upside to this sort of thing is now your objects REALLY don’t need to know anything about the other objects or parameters they are using. If you really wanted to go completely nuts about this sort of stuff you could create instances of all your Mappers ahead of time in the bootstrap and then just pull them out of the container when you need them.

1 <?php

2

3 // In the bootstrap, after having already added

4 // in the database connection

5 $container['franchise_mapper'] = function ($c) {

6 return new \IBL\FranchiseMapper($c['db_container']);

7 };

8 $container['game_mapper'] = function ($c) {

9 return new \IBL\GameMapper($c['db_container']);

10 };

I’m not saying that this is a best practice but it illustrates that proper use of a DIC can go a long way towards making your application in line with the principles espoused in the Law of Demeter.

So, what else can we do with our dependency injection container now that we have it? Well, use it! In many ways you can also look at this as a “registry on steroids”. I have typically used components for frameworks that act as registries of information. I store various values that I require persisted throughout the application and then retrieve them as required through calls to this registry or configuration object or whatever you want to call it.

Another reason to use a container for the dependencies is that it creates one place where you can find them. Without it, your code is littered with requests to instantiate those dependencies prior to injection. Again, imagine the scenario where you are switching out a data mapper from one that uses Postgres as the backend to one that uses MongoDB: how much pain would it be to go through all your code and replace all the instances where you create an old mapper and replace it with the new one? If you were using a container, you only change it in your bootstrap.

Being able to extract your dependencies when you need to inject them is helpful when trying to build your testable application. It saves you the trouble of instantiating them just before you use them, and if you use a container you can return an object that is already configured the way you need it.

It is also very easy to abuse the user of a DIC. Let’s say that you have an object called Omega, that has three dependencies that you need to pass in:

1 <?php

2

3 class Omega

4 {

5 protected $foo;

6 protected $bar;

7 protected $bizz;

8

9 public function __construct($foo, $bar, $bizz)

10 {

11 $this->foo = $foo;

12 $this->bar = $bar;

13 $this->bizz = $bizz;

14 }

15 }

16

17 $dep1 = new Foo();

18 $dep2 = new Bar();

19 $dep3 = new Bizz();

20 $newOmega = new Omega($dep1, $dep2, $dep3);

You decide to be clever and use a dependency injection container to store stuff in. Then you think “why don’t I just pass in the container since the stuff is in there already?”

1 <?php

2

3 class Omega

4 {

5 protected $foo;

6 protected $bar;

7 protected $bizz;

8

9 public function __construct($container)

10 {

11 $this->foo = $container['foo'];

12 $this->bar = $container['bar'];

13 $this->bizz = $container['biz'];

14 }

15 }

16

17 $container['foo'] = new Foo();

18 $container['bar'] = new Bar();

19 $container['bizz'] = new Bizz();

20 $newOmega = new Omega($container);

Now, let’s suppose further that I am the one who is assigned to review this code as part of the normal development flow. I would take a look at this and immediately point out that you have now coupled your dependency injection container with Omega.

“What happens if you ever change the container? Then you have to go through all your existing code that uses the container and rework it!”

If you are going to use a dependency injection container (and I highly recommend doing so for an application of medium to large complexity) then treat it as a container only, and retrieve your dependencies from it before injecting them into your objects.

Dependency injection is a concept that I think every developer needs to understand even if they prefer not to use it. I also think that dependency injection is a critical part of creating objects that can be integrated with other objects with the lowest amount of friction.

You can reduce tight coupling of your code with some help from mock objects in your tests. The concept of mock objects is a very simple one: you create a representation of an object that is a dependency for your test and then create just the functionality for that object that your test requires in order to succeed. Here is an example that ties in the use of a mock object and ensures that your code can run both on the web and the CLI.

Part of the reason for PHP’s success in tackling and solving the problems of Web 1.0 was that it was built FOR the web. Contrast this with common “competitors” like Python and Ruby, who started out on the server and over time have built up libraries and frameworks that allow them to work just fine in web applications.

Given that PHP is so tightly coupled with creating web pages, it requires effort to avoid talking directly to all those awesome features built into the language that are web-specific. Cookies, sessions and $_SERVER immediately spring to mind. For PHP developers, interacting with these things are second nature.

But this becomes a problem if you want to build an application that can be tested via CLI (Command Line Interface) tools like PHPUnit. You have to make sure that you are abstracting away any access to web-specific information or functions so that your application will actually work when modules from it are accessed from the CLI. Cookies come to mind and I’m sure that with more research you will find more examples of things that only live on the web.

Let’s consider the example of your own ACL implementation that I showed you before. If you recall, it requires that you access the request object used in your application…which is tied to the web. Then it requires a value that you stored in the session…which is usually associated with the web but can be used in a CLI environment.

All hope is not lost. In our first quick little refactoring we used dependency injection to move grabbing the request object and the value from the session that we needed outside of the ACL object itself. How could you deal with this if your script is not running in the context of a web request?

1 <?php

2

3 // Now here is our test case

4

5 include 'test_bootstrap.php';

6

7 class AclTest extends \PHPUnit_Framework_TestCase

8 {

9 ...

10

11 public function testDashboardAcl()

12 {

13 // Create a mock object

14 $testRequest = $this->getMock(

15 '\Grumpy\Request',

16 array('getUri')

17 );

18

19 // Create our test response

20 $testRequest

21 ->expects()

22 ->method('getUri')

23 ->with('/dashboard');

24

25 $testUserLevel = 3;

26 $testAcl = new \Grumpy\Acl($testRequest, $testUserLevel);

27 $response = $testAcl->accessAllowed();

28 $this->assertTrue($response);

29 }

30

31 }

Again, it might seem like extra work just to make a test pass but it illustrates how straight-forward it really can be to ensure that your code is decoupled to the point where it doesn’t even care what environment it is running in.