Matthew Daly's Blog

I'm a web developer in Norfolk. This is my blog...

2nd February 2019 8:45 pm

Creating Your Own Dependency Injection Container in PHP

Dependency injection can be a difficult concept to understand in the early stages. Even when you’re using it all the time, it can often seem like magic. However, it’s really not all that complicated once you actually get into the nuts and bolts of it, and building your own container is a good way to learn more about how it works and how to use it.

In this tutorial, I’ll walk you through creating a simple, minimal dependency injection container, using PHPSpec as part of a TDD workflow. While the end result isn’t necessarily something I’d be happy using in a production environment, it’s sufficient to understand the basic concept and make it feel less like a black box. Our container will be called Ernie (if you want to know why, it’s a reference to a 90’s era video game that had a character based on Eric Cantona called Ernie Container).

The first thing we need to do is set up our dependencies. Our container will implement PSR-11, so we need to include the interface that defines that. We’ll also use PHP CodeSniffer to ensure code quality, and PHPSpec for testing. Your composer.json should look something like this:

{
"name": "matthewbdaly/ernie",
"description": "Simple DI container",
"type": "library",
"require-dev": {
"squizlabs/php_codesniffer": "^3.3",
"phpspec/phpspec": "^5.0",
"psr/container": "^1.0"
},
"license": "MIT",
"authors": [
{
"name": "Matthew Daly",
"email": "450801+matthewbdaly@users.noreply.github.com"
}
],
"require": {},
"autoload": {
"psr-4": {
"Matthewbdaly\\Ernie\\": "src/"
}
}
}

We also need to put this in our phpspec.yml file:

suites:
test_suite:
namespace: Matthewbdaly\Ernie
psr4_prefix: Matthewbdaly\Ernie

With that done, we can start working on our implementation.

Creating the exceptions

The PSR-11 specification defines two interfaces for exceptions, which we will implement before actually moving on to the container itself. The first of these is Psr\Container\ContainerExceptionInterface. Run the following command to create a basic spec for the exception:

$ vendor/bin/phpspec desc Matthewbdaly/Ernie/Exceptions/ContainerException

The generated specification for it at spec/Exceptions/ContainerExceptionSpec.php will look something like this:

<?php
namespace spec\Matthewbdaly\Ernie;
use Matthewbdaly\Ernie\ContainerException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ContainerExceptionSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(ContainerException::class);
}
}

This is not sufficient for our needs. Our exception must also implement two interfaces:

  • Throwable
  • Psr\Container\ContainerExceptionInterface

The former can be resolved by inheriting from Exception, while the latter doesn’t require any additional methods. Let’s expand our spec to check for these:

<?php
namespace spec\Matthewbdaly\Ernie\Exceptions;
use Matthewbdaly\Ernie\Exceptions\ContainerException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ContainerExceptionSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(ContainerException::class);
}
function it_implements_interface()
{
$this->shouldImplement('Psr\Container\ContainerExceptionInterface');
}
function it_implements_throwable()
{
$this->shouldImplement('Throwable');
}
}

Now run the spec and PHPSpec will generate the boilerplate exception for you:

$ vendor/bin/phpspec run
Matthewbdaly/Ernie/Exceptions/ContainerException
11 - it is initializable
class Matthewbdaly\Ernie\Exceptions\ContainerException does not exist.
Matthewbdaly/Ernie/Exceptions/ContainerException
16 - it implements interface
class Matthewbdaly\Ernie\Exceptions\ContainerException does not exist.
Matthewbdaly/Ernie/Exceptions/ContainerException
21 - it implements throwable
class Matthewbdaly\Ernie\Exceptions\ContainerException does not exist.
100% 3
1 specs
3 examples (3 broken)
23ms
Do you want me to create `Matthewbdaly\Ernie\Exceptions\ContainerException`
for you?
[Y/n]
y
Class Matthewbdaly\Ernie\Exceptions\ContainerException created in /home/matthew/Projects/ernie-clone/src/Exceptions/ContainerException.php.
Matthewbdaly/Ernie/Exceptions/ContainerException
16 - it implements interface
expected an instance of Psr\Container\ContainerExceptionInterface, but got
[obj:Matthewbdaly\Ernie\Exceptions\ContainerException].
Matthewbdaly/Ernie/Exceptions/ContainerException
21 - it implements throwable
expected an instance of Throwable, but got
[obj:Matthewbdaly\Ernie\Exceptions\ContainerException].
33% 66% 3
1 specs
3 examples (1 passed, 2 failed)
36ms

It’s failing, but we expect that. We need to update our exception to extend the base PHP exception, and implement Psr\Container\ContainerExceptionInterface. Let’s do that now:

<?php
namespace Matthewbdaly\Ernie\Exceptions;
use Psr\Container\ContainerExceptionInterface;
use Exception;
class ContainerException extends Exception implements ContainerExceptionInterface
{
}

Let’s re-run the spec:

$ vendor/bin/phpspec run
100% 3
1 specs
3 examples (3 passed)
24ms

The second exception we need to implement is Psr\Container\NotFoundExceptionInterface and it’s a similar story. Run the following command to create the spec:

$ vendor/bin/phpspec desc Matthewbdaly/Ernie/Exceptions/NotFoundException

Again, the spec needs to be amended to verify that it’s a throwable and implements the required interface:

<?php
namespace spec\Matthewbdaly\Ernie\Exceptions;
use Matthewbdaly\Ernie\Exceptions\NotFoundException;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class NotFoundExceptionSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(NotFoundException::class);
}
function it_implements_interface()
{
$this->shouldImplement('Psr\Container\NotFoundExceptionInterface');
}
function it_implements_throwable()
{
$this->shouldImplement('Throwable');
}
}

For the sake of brevity I’ve left out the output, but if you run vendor/bin/phpspec run you’ll see it fail due to the fact that the generated class doesn’t implement the required interfaces. Amend src/Exceptions/NotFoundException as follows:

<?php
namespace Matthewbdaly\Ernie\Exceptions;
use Psr\Container\NotFoundExceptionInterface;
use Exception;
class NotFoundException extends Exception implements NotFoundExceptionInterface
{
}

Running vendor/bin/phpspec run should now see it pass. Now let’s move on to the container class…

Building the container

Run the following command to create the container spec:

$ vendor/bin/phpspec desc Matthewbdaly/Ernie/Container

However, the default generated spec isn’t sufficient. We need to check it implements the required interface:

<?php
namespace spec\Matthewbdaly\Ernie;
use Matthewbdaly\Ernie\Container;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
class ContainerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(Container::class);
}
function it_implements_interface()
{
$this->shouldImplement('Psr\Container\ContainerInterface');
}
}

Now, if we run PHPSpec, we’ll generate our class:

$ vendor/bin/phpspec run
Matthewbdaly/Ernie/Container
11 - it is initializable
class Matthewbdaly\Ernie\Container does not exist.
Matthewbdaly/Ernie/Container
16 - it implements interface
class Matthewbdaly\Ernie\Container does not exist.
75% 25% 8
3 specs
8 examples (6 passed, 2 broken)
404ms
Do you want me to create `Matthewbdaly\Ernie\Container` for you?
[Y/n]
y
Class Matthewbdaly\Ernie\Container created in /home/matthew/Projects/ernie-clone/src/Container.php.
Matthewbdaly/Ernie/Container
16 - it implements interface
expected an instance of Psr\Container\ContainerInterface, but got
[obj:Matthewbdaly\Ernie\Container].
87% 12% 8
3 specs
8 examples (7 passed, 1 failed)
40ms

Now, as we can see, this class doesn’t implement the interface. Let’s remedy that:

<?php
namespace Matthewbdaly\Ernie;
use Psr\Container\ContainerInterface;
class Container implements ContainerInterface
{
}

Now, if we run the tests, they should fail because the class needs to add the required methods:

$ vendor/bin/phpspec run
✘ Fatal error happened while executing the following
it is initializable
Class Matthewbdaly\Ernie\Container contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Psr\Container\ContainerInterface::get, Psr\Container\ContainerInterface::has) in /home/matthew/Projects/ernie-clone/src/Container.php on line 7

If you use an editor or IDE that allows you to implement an interface automatically, you can run it to add the required methods. I use PHPActor with Neovim, and used the option in the Transform menu to implement the contract:

<?php
namespace Matthewbdaly\Ernie;
use Psr\Container\ContainerInterface;
class Container implements ContainerInterface
{
/**
* {@inheritDoc}
*/
public function get($id)
{
}
/**
* {@inheritDoc}
*/
public function has($id)
{
}
}

Running vendor/bin/phpspec run should now make the spec pass, but the methods don’t actually do anything yet. If you read the spec for PSR-11, you’ll see that has() returns a boolean to indicate whether a class can be instantiated or not, while get() will either return an instance of the specified class, or throw an exception. We will add specs that check that built-in classes can be returned by both, and unknown classes display the expected behaviour. We’ll do both at once, because in both cases, the functionality to actually resolve the required class will be deferred to a single resolver method, and these methods will not do all that much as a result:

function it_has_simple_classes()
{
$this->has('DateTime')->shouldReturn(true);
}
function it_does_not_have_unknown_classes()
{
$this->has('UnknownClass')->shouldReturn(false);
}
function it_can_get_simple_classes()
{
$this->get('DateTime')->shouldReturnAnInstanceOf('DateTime');
}
function it_returns_not_found_exception_if_class_cannot_be_found()
{
$this->shouldThrow('Matthewbdaly\Ernie\Exceptions\NotFoundException')
->duringGet('UnknownClass');
}

These tests verify that:

  • has() returns true when called with the always-present DateTime class
  • has() returns false for the undefined UnknownClass
  • get() successfully instantiates an instance of DateTime
  • get() throws an exception if you try to instantiate the undefined UnknownClass

Running the specs will raise errors:

$ vendor/bin/phpspec run
Matthewbdaly/Ernie/Container
21 - it has simple classes
expected true, but got null.
Matthewbdaly/Ernie/Container
26 - it does not have unknown classes
expected false, but got null.
Matthewbdaly/Ernie/Container
31 - it can get simple classes
expected an instance of DateTime, but got null.
Matthewbdaly/Ernie/Container
36 - it returns not found exception if class cannot be found
expected to get exception / throwable, none got.
66% 33% 12
3 specs
12 examples (8 passed, 4 failed)
98ms

Let’s populate these empty methods:

<?php
namespace Matthewbdaly\Ernie;
use Psr\Container\ContainerInterface;
use Matthewbdaly\Ernie\Exceptions\NotFoundException;
use ReflectionClass;
use ReflectionException;
class Container implements ContainerInterface
{
/**
* {@inheritDoc}
*/
public function get($id)
{
$item = $this->resolve($id);
return $this->getInstance($item);
}
/**
* {@inheritDoc}
*/
public function has($id)
{
try {
$item = $this->resolve($id);
} catch (NotFoundException $e) {
return false;
}
return $item->isInstantiable();
}
private function resolve($id)
{
try {
return (new ReflectionClass($id));
} catch (ReflectionException $e) {
throw new NotFoundException($e->getMessage(), $e->getCode(), $e);
}
}
private function getInstance(ReflectionClass $item)
{
return $item->newInstance();
}
}

As you can see, both the has() and get() methods need to resolve a string ID to an actual class, so that common functionality is stored in a private method called resolve(). This uses the PHP Reflection API to resolve the class name to an actual class. We pass the string ID into a constructor of ReflectionClass, and the resolve() method will either return the created instance of ReflectionClass, or throw an exception.

For the uninitiated, ReflectionClass allows you to reflect on the object whose fully qualified class name is passed to the constructor, in order to interact with that class programmatically. The methods we will use include:

  • isInstantiable - confirms whether or not the class can be instantiated (for instance, traits and abstract classes can’t)
  • newInstance - creates a new instance of the item in question, as long as it has no dependencies in the constructor
  • newInstanceArgs - creates a new instance, using the arguments passed in
  • getConstructor - allows you to get information about the constructor

The Reflection API is pretty comprehensive, and I would recommend reading the documentation linked to above if you want to know more.

For the has() method, we check that the resolved class is instantiable, and return the result of that. For the get() method, we use getInstance() to instantiate the item and return that, throwing an exception if that fails.

Registering objects

In its current state, the container doesn’t allow you to set an item. To be useful, we need to be able to specify that an interface or string should be resolved to a given class, or for cases where we need to pass in scalar parameters, such as a database object, to specify how a concrete instance of that class should be instantiated. To that end, we’ll create a new set() public method that will allow a dependency to be set. Here are the revised specs including this:

<?php
namespace spec\Matthewbdaly\Ernie;
use Matthewbdaly\Ernie\Container;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;
use DateTime;
class ContainerSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType(Container::class);
}
function it_implements_interface()
{
$this->shouldImplement('Psr\Container\ContainerInterface');
}
function it_has_simple_classes()
{
$this->has('DateTime')->shouldReturn(true);
}
function it_does_not_have_unknown_classes()
{
$this->has('UnknownClass')->shouldReturn(false);
}
function it_can_get_simple_classes()
{
$this->get('DateTime')->shouldReturnAnInstanceOf('DateTime');
}
function it_returns_not_found_exception_if_class_cannot_be_found()
{
$this->shouldThrow('Matthewbdaly\Ernie\Exceptions\NotFoundException')
->duringGet('UnknownClass');
}
function it_can_register_dependencies()
{
$toResolve = new class {
};
$this->set('Foo\Bar', $toResolve)->shouldReturn($this);
}
function it_can_resolve_registered_dependencies()
{
$toResolve = new class {
};
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf($toResolve);
}
function it_can_resolve_registered_invokable()
{
$toResolve = new class {
public function __invoke() {
return new DateTime;
}
};
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf('DateTime');
}
function it_can_resolve_registered_callable()
{
$toResolve = function () {
return new DateTime;
};
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf('DateTime');
}
function it_can_resolve_if_registered_dependencies_instantiable()
{
$toResolve = new class {
};
$this->set('Foo\Bar', $toResolve);
$this->has('Foo\Bar')->shouldReturn(true);
}
}

This needs to handle quite a few scenarios, so there are several tests we have in place. These verify that:

  • The set() method returns an instance of the container class, to allow for method chaining
  • When a dependency is set, calling get() returns an instance of that class
  • When a concrete class that has the __invoke() magic method set is passed to set(), it is invoked and the response returned.
  • When the value passed through is a callback, the callback is resolved and the response returned
  • When a dependency is set, calling has() for it returns the right value

Note that we use anonymous classes for testing - I’ve written about these before and they’re very useful in this context because they allow us to create a simple class inline for testing purposes.

Running the specs should result in us being prompted to generate the set() method, and failing afterwards:

$ vendor/bin/phpspec run
Matthewbdaly/Ernie/Container
42 - it can register dependencies
method Matthewbdaly\Ernie\Container::set not found.
Matthewbdaly/Ernie/Container
49 - it can resolve registered dependencies
method Matthewbdaly\Ernie\Container::set not found.
Matthewbdaly/Ernie/Container
57 - it can resolve registered invokable
method Matthewbdaly\Ernie\Container::set not found.
Matthewbdaly/Ernie/Container
68 - it can resolve registered callable
method Matthewbdaly\Ernie\Container::set not found.
Matthewbdaly/Ernie/Container
77 - it can resolve if registered dependencies instantiable
method Matthewbdaly\Ernie\Container::set not found.
70% 29% 17
3 specs
17 examples (12 passed, 5 broken)
316ms
Do you want me to create `Matthewbdaly\Ernie\Container::set()` for you?
[Y/n]
y
Method Matthewbdaly\Ernie\Container::set() has been created.
Matthewbdaly/Ernie/Container
42 - it can register dependencies
expected [obj:Matthewbdaly\Ernie\Container], but got null.
Matthewbdaly/Ernie/Container
49 - it can resolve registered dependencies
exception [exc:Matthewbdaly\Ernie\Exceptions\NotFoundException("Class Foo\Bar does not exist")] has been thrown.
Matthewbdaly/Ernie/Container
57 - it can resolve registered invokable
exception [exc:Matthewbdaly\Ernie\Exceptions\NotFoundException("Class Foo\Bar does not exist")] has been thrown.
Matthewbdaly/Ernie/Container
68 - it can resolve registered callable
exception [exc:Matthewbdaly\Ernie\Exceptions\NotFoundException("Class Foo\Bar does not exist")] has been thrown.
Matthewbdaly/Ernie/Container
77 - it can resolve if registered dependencies instantiable
expected true, but got false.
70% 11% 17% 17
3 specs
17 examples (12 passed, 2 failed, 3 broken)
90ms

First, we need to set up the set() method properly, and define a property to contain the stored services:

private $services = [];
public function set(string $key, $value)
{
$this->services[$key] = $value;
return $this;
}

This fixes the first spec, but the resolver needs to be amended to handle cases where the ID is set manually:

private function resolve($id)
{
try {
$name = $id;
if (isset($this->services[$id])) {
$name = $this->services[$id];
if (is_callable($name)) {
return $name();
}
}
return (new ReflectionClass($name));
} catch (ReflectionException $e) {
throw new NotFoundException($e->getMessage(), $e->getCode(), $e);
}
}

This will allow us to resolve classes set with set(). However, we also want to resolve any callables, such as callbacks or classes that implement the __invoke() magic method, which means that sometimes resolve() will return the result of the callable instead of an instance of ReflectionClass. Under those circumstances we should return the item directly:

public function get($id)
{
$item = $this->resolve($id);
if (!($item instanceof ReflectionClass)) {
return $item;
}
return $this->getInstance($item);
}

Note that because the __invoke() method is automatically called in any concrete class specified in the second argument to set(), it’s only possible to resolve classes that define an __invoke() method if they are passed in as string representations. The following PsySh session should make it clear what this means:

>>> use Matthewbdaly\Ernie\Container;
>>> $c = new Container;
=> Matthewbdaly\Ernie\Container {#2307}
>>> class TestClass { public function __invoke() { return "Called"; }}
>>> $c->get('TestClass');
=> TestClass {#2319}
>>> $c->set('Foo\Bar', 'TestClass');
=> Matthewbdaly\Ernie\Container {#2307}
>>> $c->get('Foo\Bar');
=> TestClass {#2309}
>>> $c->set('Foo\Bar', new TestClass);
=> Matthewbdaly\Ernie\Container {#2307}
>>> $c->get('Foo\Bar');
=> "Called"

As you can see, if we pass in the fully qualified class name of a class that defines an __invoke() method, it can be resolved as expected. However, if we pass a concrete instance of it to set(), it will be called and will return the response from that. This may not be the behaviour you want for your own container.

According to this issue on the PHP League’s Container implementation, it was also an issue for them, so seeing as this is just a toy example I’m not going to lose any sleep over it. Just something to be aware of if you use this post as the basis for writing your own container.

Resolving dependencies

One thing is missing from our container. Right now it should be able to instantiate pretty much any class that has no dependencies, but these are quite firmly in the minority. To be useful, a container should be able to resolve all of the dependencies for a class automatically.

Let’s add a spec for that:

function it_can_resolve_dependencies()
{
$toResolve = get_class(new class(new DateTime) {
public $datetime;
public function __construct(DateTime $datetime)
{
$this->datetime = $datetime;
}
});
$this->set('Foo\Bar', $toResolve);
$this->get('Foo\Bar')->shouldReturnAnInstanceOf($toResolve);
}

Here we have to be a bit crafty. Anonymous classes are defined and instantiated at the same time, so we can’t pass it in as an anonymous class in the test. Instead, we call the anonymous class and get its name, then set that as the second argument to set(). Then we can verify that the returned object is an instance of the same class.

Running this throws an error:

$ vendor/bin/phpspec run
Matthewbdaly/Ernie/Container
86 - it can resolve dependencies
exception [err:ArgumentCountError("Too few arguments to function class@anonymous::__construct(), 0 passed and exactly 1 expected")] has been thrown.
94% 18
3 specs
18 examples (17 passed, 1 broken)
60ms

This is expected. Our test class accepts an instance of DateTime in the constructor as a mandatory dependency, so instantiating it fails. We need to update the getInstance() method so that it can handle pulling in any dependencies:

private function getInstance(ReflectionClass $item)
{
$constructor = $item->getConstructor();
if (is_null($constructor) || $constructor->getNumberOfRequiredParameters() == 0) {
return $item->newInstance();
}
$params = [];
foreach ($constructor->getParameters() as $param) {
if ($type = $param->getType()) {
$params[] = $this->get($type->getName());
}
}
return $item->newInstanceArgs($params);
}

Here, we use the Reflection API to get the constructor. If there’s no constructor, or it has no required parameters, we just return a new instance of the reflected class as before.

Otherwise, we loop through the required parameters. For each parameter, we get the string representation of the type specified for that parameter, and retrieve an instance of it from the container. Afterwards, we use those parameters to instantiate the object.

Let’s run the specs again:

$ vendor/bin/phpspec run
100% 18
3 specs
18 examples (18 passed)
51ms

Our container is now complete. We can:

  • Resolve simple classes out of the box
  • Set arbitrary keys to resolve to particular classes, or the result of callables, so as to enable mapping interfaces to concrete implementations, or resolve classes that require specific non-object parameters, such as PDO
  • Resolve complex classes with multiple dependencies

Not too bad for just over 100 lines of PHP…

Final thoughts

As I’ve said, this is a pretty minimal example of a dependency injection container, and I wouldn’t advise using this in production when there are so many existing, mature solutions available. I have no idea how the performance would stack up against existing solutions, or whether there are any issues with it, and quite frankly that’s besides the point - this is intended as a learning exercise to understand how dependency injection containers in general work, not as an actual useful piece of code for production. If you want an off-the-shelf container, I’d point you in the direction of league/container, which has served me well.

You can find the code for this tutorial on GitHub, so if you have any problems, you should take a look there to see where the problem lies. Of course, if you go on to create your own kick-ass container based on this, do let me know!

27th January 2019 11:10 pm

Understanding Query Objects

The project I’ve been maintaining for the last year has inherited a rather dubious database structure that would currently be very difficult to refactor, which also makes many queries more convoluted than they should be. At present, I’m involved in building a whole new home page, which has necessitated adding some new queries. Since some of these involve carrying out unions between several similar tables (that should have been one table, grr…), they can involve some quite large chunks for each query.

As a result, it’s made sense to break those queries down further. Since Zend 1 doesn’t have anything analogous to scopes in Eloquent, I don’t really have an easy way to break these queries up in the models (and I’m trying to get the query logic out of the models at present anyway), so I opted to make them into query objects instead, which is a pattern I hadn’t used before (but probably should have).

A query object is pretty much what it says on the tin - it’s a PHP object that executes a single, very specific query. This may seem like overkill, but it’s only really useful for the most complex and convoluted of queries. It can accept parameters, as you’d expect, and some parts of the query may be optional based on that, but fundamentally it should build and run only one single query.

In this post I’ll go through how you might create one, how it relates to the repository pattern, and when to create one.

Creating a query object class

I’m a big fan of the __invoke() magic method in PHP. For the uninitiated, it lets you instantiate the class, and then use it in the same way you would a function, making it very useful for callbacks. This also brings some other advantages:

  • Unlike with a function, you can create private methods to do other parts of the work, making it easier to understand the main method.
  • It can have a constructor, and can therefore both accept dependencies via the constructor, and be instantiated via dependency injection, simplifying setup and testing when compared to using a callback.
  • Since __invoke() is an innate part of the PHP language, it makes more sense for classes that have a single responsibility to use that method name to do that, rather than picking something like handle() or run().

As a result, my query objects generally use the __invoke() method to trigger the query.

Since Zend 1 is no longer supported, I won’t bother displaying how I’d write the query in that specific context. I have yet to use this pattern with Laravel, but if I did, it would look something like this:

<?php
namespace App\Queries;
use Illuminate\Database\DatabaseManager;
final class DashboardItems
{
protected $db;
public function __construct(DatabaseManager $db)
{
$this->db = $db;
}
public function __invoke(int $days = 7)
{
return $this->fooTable()
->union($this->barTable())
->whereRaw('start_date >= (NOW() - INTERVAL ? DAY)', [$days]);
->get();
}
private function fooTable()
{
return $this->db->table('foo')
->where('type', '=', 'fooType');
}
private function barTable(int $days)
{
return $this->db->table('bar')
->where('type', '=', 'barType');
}
}

Note that we break each one of the tables we want to perform a UNION on into a private method. This is probably the biggest advantage of query objects - it lets you break particularly unwieldy queries up into logical steps, making them more readable. You could do this by adding private methods on a repository class too, but I’d be reluctant to add private methods to a repository that were only used in one query - to my mind, a query object is a better home for that.

What about repositories?

I regularly use the repository pattern in my code bases, whether that’s for Laravel projects or the current Zend 1-based legacy project. It’s an ongoing effort to refactor it so that all the queries are called from repository classes, leaving the models to act as containers for the data. So how do query objects fit in here?

It’s important to note that while a repository represents all queries relating to a table, a query object represents only a single query, and so the repository should still be the place where the query is called from. However, the repository should just defer the actual querying to the query object. The relevant parts of the application structure for my current application look a bit like this:

└── app
├── Queries
│ └── DashboardItems.php
└── Repositories
└── DashboardRepository.php

And the repository might call the query object as follows:

<?php
namespace App\Repositories;
use App\Queries\DashboardItems;
final class DashboardRepository
{
public static function dashboardItems(int $days = 7)
{
$query = new DashboardItems;
return $query($days);
}
}

At present my repositories all use static methods as I’m still in the process of migrating the queries over to the repository classes. That also means I can’t easily use dependency injection. For a Laravel application, a similar call might look like this:

<?php
namespace App\Repositories;
use App\Queries\DashboardItems;
final class DashboardRepository
{
protected $dashboardQuery;
public function __construct(DashboardItems $dashboardQuery)
{
$this->dashboardQuery = $dashboardQuery;
}
public function dashboardItems(int $days = 7)
{
return $this->dashboardQuery($days);
}
}

The only real difference is that we can instantiate the query object out of the container, simplifying setup.

When to use query objects

I think it probably goes without saying, but it should be a rare query that actually needs to be implemented as a query object, especially if you’re using an ORM like Eloquent that provides features like scopes, and as yet I only have two using this pattern, as well as two others that were implemented as “reporter” classes, but could be query objects instead. So far, my experience has been that the sort of queries that are large enough to be worth considering include:

  • Queries that generate reports, particularly if they have various options
  • Queries that use unions, as in the above example, since it makes sense to use a private method to fetch each table
  • Queries with multiple complex joins

Smaller queries will typically fit happily inside a single method in your repository classes. If that’s the case, then they can live there without trouble. However, if you have a query that’s becoming too big to fit inside a single method, rather than adding private methods to your repository class, it may make more sense to refactor it out into a query object in its own right. You can still call it via the same method on your repository class, but the repository can just defer to the query object. As I usually use decorators to cache the responses from my repository classes anyway, then it makes sense to stick with this approach to keep caching consistent too.

Query objects only really offer any value for particularly large queries. However, they can be invaluable in those circumstances. By enabling you to break those big queries up into a series of steps, they help make them easier to understand.

13th January 2019 6:50 pm

Writing a Custom Sniff for PHP Codesniffer

I’ve recently come around to the idea that in PHP all classes should be final by default, and have started doing so as a matter of course. However, when you start doing something like this it’s easy to miss a few files that haven’t been updated, or forget to do it, so I wanted a way to detect PHP classes that are not set as either abstract or final, and if possible, set them as final automatically. I’ve mentioned before that I use PHP CodeSniffer extensively, and that has the capability to both find and resolve deviations from a coding style, so last night I started looking into the possibility of creating a coding standard for this. It took a little work to understand how to do this so I thought I’d use this sniff as a simple example.

The first part is to set out the directory structure. There’s a very specific layout you have to follow for PHP CodeSniffer:

  • The folder for the standard must have the name of the standard, and be in the source folder set by Composer (in this case, src/AbstractOrFinalClassesOnly.
  • This folder must contain a ruleset.xml file defining the name and description of the standard, and any other required content.
  • Any defined sniffs must be in a Sniffs folder.

The ruleset.xml file was fairly simple in this case, as this is a very simple standard:

<?xml version="1.0"?>
<ruleset name="AbstractOrFinalClassesOnly">
<description>Checks all classes are marked as either abstract or final.</description>
</ruleset>

The sniff is intended to do the following:

  • Check all classes have either the final keyword or the abstract keyword set
  • When running the fixer, make all classes without the abstract keyword final

First of all, our class must implement the interface PHP_CodeSniffer\Sniffs\Sniff, which requires the following methods:

public function register(): array;
public function process(File $file, $position): void;

Note that File here is an instance of PHP_CodeSniffer\Files\File. The first method registers the code the sniff should operate on. Here we’re only interested in classes, so we return an array containing T_CLASS. This is defined in the list of parser tokens used by PHP, and represents classes and objects:

public function register(): array
{
return [T_CLASS];
}

For the process() method, we receive two arguments, the file itself, and the position. We need to keep a record of the tokens we check for, so we do so in a private property:

private $tokens = [
T_ABSTRACT,
T_FINAL,
];

Then, we need to find the error:

if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
}

We use $file to get the token before class, and pass the $tokens property as a list of acceptable values. If the preceding token is not either abstract or final, we add a fixable error. The first argument is the string error message, the second is the location, and the third is the class of the sniff that has failed.

That will catch the issue, but won’t actually fix it. To do that, we need to get the fixer from the file object, and call its addContent() method to add the final keyword. We amend process() to extract the fixer, add it as a property, and then call the fix() method when we come across a fixable error:

public function process(File $file, $position): void
{
$this->fixer = $file->fixer;
$this->position = $position;
if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
$this->fix();
}
}

Then we define the fix() method:

private function fix(): void
{
$this->fixer->addContent($this->position - 1, 'final ');
}

Here’s the finished class:

<?php declare(strict_types=1);
namespace Matthewbdaly\AbstractOrFinalClassesOnly\Sniffs;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Files\File;
/**
* Sniff for catching classes not marked as abstract or final
*/
final class AbstractOrFinalSniff implements Sniff
{
private $tokens = [
T_ABSTRACT,
T_FINAL,
];
private $fixer;
private $position;
public function register(): array
{
return [T_CLASS];
}
public function process(File $file, $position): void
{
$this->fixer = $file->fixer;
$this->position = $position;
if (!$file->findPrevious($this->tokens, $position)) {
$file->addFixableError(
'All classes should be declared using either the "abstract" or "final" keyword',
$position - 1,
self::class
);
$this->fix();
}
}
private function fix(): void
{
$this->fixer->addContent($this->position - 1, 'final ');
}
}

I’ve made the resulting standard available via Github.

This is a bit rough and ready and I’ll probably refactor it a bit when I have time. In addition, it’s not quite displaying the behaviour I want as it should, since ideally it should only be looking for the abstract and final keywords in classes that implement an interface. However, it’s proven fairly easy to create this sniff, except for the fact I had to go rooting around various tutorials that weren’t all that clear. Hopefully this example is a bit simpler and easier to follow.

3rd January 2019 11:55 pm

You Don't Need That Module Package

Lately I’ve seen a number of Laravel packages being posted on places like Reddit that offer ways to make your project more modular by letting you break their classes out of the usual structure and place them in a separate folder called something like packages/ or modules/. However, these packages are completely redundant, and it requires very little work to achieve the same thing with Composer alone. In addition, much of it is not specific to Laravel and can also be applied to any other framework that uses Composer.

There are two main approaches I’m aware of - keeping it in a single project, and moving the modules to separate Composer packages.

Single project

Suppose we have a brand new Laravel project with the namespace left as the default App. This is what the autoload section of the composer.json file will look like:

"autoload": {
"psr-4": {
"App\\": "app/"
},
"classmap": [
"database/seeds",
"database/factories"
]
},

Composer allows for numerous ways to autoload classes and you can add additional namespaces as you wish. Probably the best approach is to use PSR-4 autoloading, as in this example:

"autoload": {
"psr-4": {
"App\\": "app/",
"Packages\\": "packages"
},
"classmap": [
"database/seeds",
"database/factories"
]
},

Now, if you put the model Post.php in the folder, packages/Blog/Models/, then this will map to the namespace Packages\Blog\Models\Post, and if you set the namespace to this in the file, and run composer dump-autoload, you should be able to import it from that namespace without trouble. As with the App\ namespace, because it’s using PSR-4 you’re only specifying the top-level namespace and the folders and files underneath have to mirror the namespace, so for instance, Packages\Foo\Bar maps to packages/Foo/Bar.php. If for some reason PSR-4 autoloading doesn’t map well to what you want to do, then there are other methods you can use - refer to the relevant section of the Composer documentation for the other methods available.

The controllers are the toughest part, because by default Laravel’s routing works on the assumption that the controllers are all under the App\Http\Controllers namespace, so you can shorten the namespace used. There are two ways around this I’m aware of. One is to specify the full namespace when referencing each controller:

Route::get('/', '\App\Modules\Http\Controllers\FooController@index');

The other option is to update the RouteServiceProvider.php‘s namespace property. It defaults to this:

protected $namespace = 'App\Http\Controllers';

If there’s a more convenient namespace you want to place all your controllers under, then you can replace this, and it will become the default namespace applied in your route files.

Other application components such as migrations, routes and views can be loaded from a service provider very easily. Just create a service provider for your module, register it in config/app.php, and set up the boot() method to load whichever components you want from the appropriate place, as in this example:

$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
$this->loadRoutesFrom(__DIR__.'/../routes.php');
$this->loadViewsFrom(__DIR__.'/../views', 'comments');

Separate packages

The above approach works particularly well in the initial stages of a project, when you may need to jump around a lot to edit different parts of the project. However, later on, once many parts of the project have stabilised, it may make more sense to pull the modules out into separate repositories and use Composer to pull them in as dependencies, using its support for private repositories. I’ve also often taken this approach right from the start without issue.

This approach has a number of advantages. It makes it easier to reuse parts of the project in other projects if need be. Also, if you put your tests in the packages containing the components they test, it means that rather than running one monolithic test suite for the whole project, you can instead run each module’s tests each time you change it, and limit the test suite of the main project to those integration and acceptance tests that verify the whole thing, along with any unit tests for code that remains in the main repository, resulting in quicker test runs.

Don’t get me wrong, making your code more modular is definitely a good thing and I’m wholly in favour of it. However, it only takes a little knowledge of Composer to be able to achieve this without any third party package at all, which is good because you’re no longer dependent on a package that may at any time fall behind the curve or be abandoned.

2nd January 2019 11:00 pm

Why Bad Code Is Bad

This may sound a little trite, but why is it bad to write bad code?

Suppose you’re a client, or a line manager for a team of developers. You work with developers regularly, but when they say that a code base is bad, what are the consequences of that, and how can you justify spending time and money to fix it? I’ve often heard the refrain “If it works, it doesn’t matter”, which may have a grain of truth, but is somewhat disingenuous. In this post, I’ll explain some of the consequences when your code base is bad. It can be hard to put a definitive price tag on the costs associated with delivering bad code, but this should give some idea of the sort of issues you should take into account.

Bad code kills developer productivity

Bad code is harder to understand, navigate and reason about than good code. Developers are not superhuman, and we can only hold so much in our heads at one time, which is why many of the principles behind a clean and maintainable code base can essentially be boiled down to “break it into bite-sized chunks so developers can understand each one in isolation before seeing how they fit together”.

If one particular class or function gets too big and starts doing too much, it quickly becomes very, very hard to get your head around what that code does. Developers typically have to build a mental model of how a class or function works before they can use it effectively, and the smaller and simpler you can keep each unit of code, the less time and effort it takes to do so. The mark of a skilled developer is not the complexity of their code bases, but their simplicity - they’ve learned to make their code as small, simple, and readable as possible. A clean and well laid-out code base makes it easy for developers to get into the mental state called “flow” that is significantly more productive.

In addition, if an application doesn’t conform to accepted conventions in some way, such as using inappropriate HTTP verbs (eg GET to change the state of something), then quite apart from the fact that it won’t play well with proxy servers, it imposes an additional mental load on developers by forcing them to drop a reasonable set of assumptions about how the application works. If the application used the correct HTTP verbs, experienced developers would know without being told that to create a new report, you’d send a POST request to the reports API endpoint.

During the initial stages of a project, functionality can be delivered quite quickly, but if the code quality is poor, then over time developer velocity can decrease. Ensuring a higher quality code base helps to maintain velocity at a consistent level as it gets bigger. This also means estimates will be more accurate, so if you quote a given number of hours for a feature, you’re more likely to deliver inside that number of hours.

Bad code is bad for developer welfare

A code base that’s repetitive, badly organised, overly complex and hard to read is a recipe for stressed developers, making burnout more likely. If a developer suffers burnout, their productivity will drop substantially.

In the longer term, if developer burnout isn’t managed correctly, it could easily increase developer turnover as stressed developers quit. It’s also harder to recruit new developers if they’re faced with the prospect of dealing with a messy, stressful code base.

Bad code hampers your ability to pivot

If the quality of your code base is poor, it can mean that if functionality needs to be changed or added, then more work is involved. Repetitive code can mean something has to be updated in more than one place, and if it becomes too onerous, it can make it too time-consuming or expensive to justify the changes.

Bad code may threaten the long-term viability of your project

One thing that is certain in our industry is that things change. Libraries, languages and frameworks are constantly being updated, and sometimes there will be potentially breaking changes to some of these. On occasion, a library or framework will be discontinued, making it necessary to migrate to a replacement.

Bad code is often tightly coupled to a particular framework or library, and sometimes even to a particular version, making it harder to migrate if it becomes necessary. If a project was written with a language or framework version that had a serious issue, and was too tightly coupled to migrate to a newer version, it might be too risky to keep it running, or it might be necessary to run an insecure application in spite of the risks it posed.

Bad code is more brittle

A poor code base will break, a lot, and often in ways that are clearly visible to end users. Duplicate code makes it easy to miss cases where something needs to be updated in more than one place, and if the code base lacks tests, a serious error may not be noticed for a long time, especially if it’s something comparatively subtle.

Bad code is hard, if not impossible, to write automated tests for

If a particular class or function does too much, it becomes much harder to write automated tests for it because there are more variables going in and more expected outcomes. A sufficiently messy code base may only really be testable by automating the browser, which tends to be very slow and brittle, making test-driven development impractical. Manual testing is no substitute for a proper suite of automated tests, since it’s slower, less consistent and not repeatable in the same way, and it’s only sufficient by itself for the most trivial of web apps.

Bad code is often insecure

A bad code base may inadvertently expose user’s data, or be at risk from all kinds of attacks such as cross-site scripting and SQL injection attacks that can also potentially expose too much data.

For any business with EU-based users, the risks of exposing user’s data are very serious. Under the GDPR, there’s a potential fine of up to €20 million, or 4% of turnover. That’s potentially an existential risk for many companies.

In addition, a bad code base is often more vulnerable to denial-of-service attacks. If it has poor or no caching, excessive queries, or inefficient queries, then every time a page loads it will carry out more queries than a more optimised site would. Given the same server specs, the inefficient site will be overwhelmed quicker than the efficient one.

Summary

It’s all too easy to focus solely on delivering a working product and not worry about the quality of the code base when time spent cleaning it up doesn’t pay the bills, and it can be hard to justify the cost of cleaning it up later to clients.

There are tools you can use to help keep up code quality, such as linters and static analysers, and it’s never a bad idea to investigate the ones available for the language(s) you work in. For best results they should form part of your continuous integration pipeline, so you can monitor changes over time and prompt developers who check in problematic code to fix the issues. Code reviews are another good way to avoid bad code, since they allow developers to find problematic code and offer more elegant solutions.

I’m not suggesting that a code base that has a few warts has no value, or that you should sink huge amounts of developer time into refactoring messy code when money is tight, as commercial concerns do have to come first. But a bad code base does cause serious issues that have financial implications, and it’s prudent to recognise the problems it could cause, and take action to resolve them, or better yet, prevent them occurring in the first place.

Recent Posts

Flexible Data Types With the JSON Field

Storing Wordpress Configuration in Environment Variables

Using Mix Versioning Outside Laravel

Setting Private Properties in Tests

Skipping Environment Specific Phpunit Tests

About me

I'm a web and mobile app developer based in Norfolk. My skillset includes Python, PHP and Javascript, and I have extensive experience working with CodeIgniter, Laravel, Zend Framework, Django, Phonegap and React.js.