Using Modern PHP with WordPress: Part 2 – Dependency Injection and Containers

In Part 1 of this series, I have explained the purposes for this project. I hope you are able to pick up concepts and approaches from this series.

One of the core principles I use in PHP development is Dependency Injection (DI). That can be a scary concept for developers not used to using the pattern.

At its core, DI is the concept that each class should be self-contained and testable on its own. When a class is decoupled from other classes, it makes for more reusable code. DI operates on the core principle that if a class uses another class for its functionality, the required class is injected into the class, usually via the constructor.

<?php

class Person {

  private $job;  

  public function __construct( \Job $job ) {
    $this->job = $job;
  }

  public function get_job() {
    return $this->job->my_job();
  }
}

class Job {
  public function my_job() {
    return 'PHP Developer';
  }
}

$person = new Person();
echo $person->get_job();
// returns PHP Developer

This is obviously a simple example. There is a class Person and, via the constructor, we inject the Job class into it. We then assign the private property $job with the injected object. Thereafter, the Person class can now access the Job class without having to instantiate an object inside any other methods in the class.

In my example plugin, Photogrammapy, I use this approach in my Map class.

class Map {

	private $params;

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

In this case, I’ve defined a class, Defaults, that carries a bunch of default values for my Google Maps integration. Instead of instantiating the Defaults class in the constructor, I inject it into the class and then assign it to the private property $params. Many developers wrongly do this:

class Map {

    private $params;

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

Here’s the rub. the latter example is problematic. It makes the Map class untestable unless the Defaults class is also available. DI solves this for us. Now we can write a test that defines our own decoupled Defaults class in the context of a test and inject that into our testable Map class.

Containers

Part of the Dependency Injection pattern involves containers. Containers, simply, will allow a class to be instantiated on demand. That means, on a page load, we aren’t hooking a bunch of classes into things and then leaving them unused. This is for performance and memory management.

For this project, I have used the PHP-DI library, installed via Composer. More on Composer in later parts of this series.

I have an Init class which is loaded via the init() callback on plugins_loaded. This class, as the name suggests, handles all the bootstrapping of my containers making them available to the rest of the application.

class Init {
	
	protected $providers = [];

	public function __construct() {
		Bootstrap::bootstrap();
	}

	public function init() {
		$this->register_providers();
	}

	public function register_providers() {
		$this->providers[ Photographs::NAME ] = new Photographs();
		$this->providers[ Settings::NAME ] = new Settings();
		$this->providers[ Map::NAME ] = new Map( new Defaults() );
	}
}

So that’s DI in a nutshell. Of course, that just scratches the surface, and there’s plenty of information about PHP dependency injection for further study.

In the next post, I’ll discuss Composer as a dependency manager for a project.

I am a professional PHP developer and am looking for my next job. Hire me!

Using Modern PHP with WordPress: Part 3 – Composer

In this ongoing series, I am providing insight into how I built the Photogramappy plugin in WordPress using modern PHP approaches, standards, and techniques. In my last article, we looked at dependency injection and containers as a way to decouple classes and logic from each other.

In this article, I’d like to dive a bit into Composer, and how it brings package management to the project. The simplest way to explain it, is that it’s a list of PHP libraries needed for the project. This is done in a JSON file named composer.json.

You can see mine here.

In modern PHP, a library should contain it’s own composer.json it should have a “type” defined. In my case, I call it “wordpress-plugin”. In my main WordPress project, I have a different composer.json file that defines how WordPress is setup, what plugins should be installed, etc. Any plugins with type “wordpress-plugin” will be installed in my plugins directory, thanks to the composer/installers library.

So if the Photogramappy “library” is installed in my main project (it is), then composer will put it in the plugins directory where it should be. Easy, but a little out of scope for this piece. You might want to check out the excellent write-up on Composer-driven WordPress at Root.io.

There are two areas of my plugin’s composer.json to highlight that really make this work. My plugin relies on a package called PHP-DI, which I discussed in the last article. It provides the mean to containerize classes, as demonstrated.

Another library I heavily depend on is Metabox Orchestra. I will discuss how I use it in my next article.

Finally, this project includes tests as well, but tests are not needed for the plugin to work. They are only needed in development to ensure the plugin actually works.

"require": {
    "php": ">=7.2",
    "php-di/php-di": "^6.0",
    "inpsyde/metabox-orchestra": "^0.4.1"
  },
  "require-dev": {
    "wp-cli/wp-cli": "^1.5.1",
    "lucatume/function-mocker": "~1.0",
    "phpunit/phpunit": "^6",
    "php-http/httplug": "^1.1",
    "codeception/codeception": "^2.5",
    "vlucas/phpdotenv": "^2.5",
    "lucatume/wp-browser": "^2.2."
  },

The “require” block is where libraries and requirements that allow the plugin to work are defined. Here we see that this plugin must use at least PHP 7.2, the php-di/php-di library and the inpsyde/metabox-orchestra library.

The “require-dev” block is not for libraries that would make the plugin work, but it is for libraries needed in development. While wp-cli/wp-cli is not required for development, I usually include it in my projects anyway because it’s handy to have for IDE autocompletion. The rest of the libraries here are a standard set of libraries needed for running tests.

By using Composer to manage dependencies, it makes it possible to ship a “lean” product without having to version control stuff that is version controlled elsewhere.

In the next article, I’ll discuss usage of the Metabox Orchestra library to quickly scaffold metaboxes in an Object-Oriented way.

Using Modern PHP with WordPress: Part 4 – Metabox Orchestra

In the last article, we discussed Composer. That is an entire subject matter of it’s own, so I didn’t really cover everything. But I covered enough for this project.

In the Photogramappy plugin, I have registered postmeta for latitude and longitude. The idea is, someone would create a new Photograph post, upload a featured image and enter the latitude and longitude as meta on the post edit screen.

function latitude_and_longitude_metabox() {

    add_meta_box(
        'lat-long',
        __( 'Latitude and Longitude', '' ),
        'lat_long_meta_box_callback',
        'post'
    );
}
add_action( 'add_meta_boxes', 'latitude_and_longitude_metabox' );

function lat_long_meta_box_callback() {
    ?>
    <!-- Some HTML for the Metabox -->
    <?php
}

function save_lat_long( $post_id, $post ) {
    // Do some checks then save the meta
}
add_action( 'save_post', 'save_lat_long' );

This is the traditional way of registering metaboxes in WordPress but it’s so tedious, in my opinion. It also doesn’t leverage more modern approaches. For this project, I’ve opted to use the Metabox Orchestra library to handle metaboxes in an OOP way.

Each metabox needs 4 things to work. It needs a PostMetabox object which is just the creation of the metabox. Note: it could also be a TermMetabox object, if registering meta with a term. It also needs a BoxView object which renders the HTML and form fields that is in the box. Third, it needs an BoxAction, which is a class that handles the saving of the data. Finally, with those 3 objects, it is necessary to “register” the meta with WordPress. This is done with a Box object.

A note about the file structure I am using, because I’m using a number of techniques here. The Metabox.php file is an interface. All registered metaboxes (I only have one, but if I wanted to add more), would implement this interface which requires a register_meta() method. The Metabox_Object.php is an abstract class that all registered metaboxes must extend. This abstract class has the method register_meta() method that the Metabox interface requires. Almost all metabox instances would not have to declare their own, because almost all will do the exact same thing. However, a metabox could provide their own instead. Finally, the Geo_Coordinate namespace has a Geo_Coordinate.php class which extends Metabox_Object and implements MetaBox using the objects created in the Config namespace.

Object Meta
- Metabox.php
- Metabox_Object.php
- Geo_Coordinate
-- Geo_Coordinate.php
-- Config
--- Action.php
--- View.php
--- Box.php

So, in summary, Object_Meta\Geo_Coordinate\Config\View provides the form fields inside the metabox by extending the BoxView class. We handle the processing of saved data in the Object_Meta\Geo_Coordinate\Config\Action class which extends BoxAction. The Object_Meta\Geo_Coordinate\Geocoordinate class extends Object_Meta\Metabox_Object class which registers the meta by injecting a Boxes object, using the Boxes object and injecting the Box class (which extends PostMetabox).

Finally, we hook into WordPress on the metabox-orchestra.register-boxes hook. We now have metaboxes in a clean, OOP way!

See all the source code here.

In Part 5 of this series, we’ll dive a bit into Codeception and testing.

Using Modern PHP with WordPress: Part 5 – Tests

Over the course of this series, we have looked at a variety of techniques I used while building the Photogramappy plugin. We’ve discussed the Dependency Injection pattern, Composer for dependency management, and I demonstrated using pure PHP libraries, abstract classes, and interfaces to build WordPress metaboxes.

While it would take weeks or months to really deep dive into everything that can be used in modern PHP development, the end goal is to demonstrate that WordPress development can be done using modern, industry-savvy PHP. I am also looking for full time work, so if you’re interested in discussing opportunities, please reach out to me.

In this final article in this series, I want to talk a little bit about testing. Testing is often difficult to build into projects, unless the expectation is set up front that this is the only way this project can be done. Testing didn’t become a “real thing” to me until I was on a project that didn’t have it. It was complicated. Very, very complicated. It seemed as we built things, other things would break. Regressions became a daily occurrence.

So we set about doing something we should have done from the start. Writing tests. Please do not follow this example and go back to write tests that you should have from the start. Incorporate tests into your daily life.

For this plugin, I’ve written 3 tests. I’ve written 2 acceptance tests and 1 integration test. It’s a fairly small plugin. And not everything is covered. 100% test coverage is a pipe dream, so don’t strive for it. In the words of Luca Tumedei, start with 1 test because 1 test is better than no tests at all.

In fact, speaking of Luca, he is the go to master when it comes to writing tests and his site is an amazing resource.

Quick definitions. Acceptance tests are something that could theoretically be driven by QA or UAT. They are a series of procedures that a user would go through and verifies the expected outcome. Integration tests look at how data flows through an application. If acceptances tests look at how a user gets from point a to point b, integration tests do the same for data. Unit tests look at only a single function or method to validate outcome. The term “unit testing” is thrown around a lot for what really should be integration testing. In fact, in most cases, I don’t use unit tests.

Setup

Codeception is what we are using as a testing framework. Some people like PHPUnit. Codeception is basically an enhanced version of that. You’ll need a JDK installed. I used Oracle’s OpenJDK. After that, you’ll need to install Selenium Standalone Server. This runs on Java (thus needing JDK) and is a popular standalone testing server. Note: I’m linking to an older version because of incompatibilities with PHP. These incompatibilities may be fixed in the future, but for now, I’m linking to version 3.4.0. Finally, you’ll need ChromeDriver.

With these three things installed, from your terminal, execute this command to run the acceptance testing suite.

vendor/codeception/codeception/codecept run acceptance

// Output:
➜  photogramappy git:(master) ✗ vendor/codeception/codeception/codecept run acceptance
Codeception PHP Testing Framework v2.5.2
Powered by PHPUnit 6.5.13 by Sebastian Bergmann and contributors.
Running with seed:


Acceptance Tests (2) -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
✔ Geo_CoordinateCest: Verify that the Latitude Field can be saved (8.55s)
✔ Geo_CoordinateCest: Verify that the Latitude Field can be saved (8.28s)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Time: 23.37 seconds, Memory: 30.00MB

OK (2 tests, 2 assertions)

Pretty simple, right? 2 tests, 2 passes. But what about integration tests? Practically, I’ll usually write more of these but as a point of example (and because 1 test is better than no tests, amirite?):

vendor/codeception/codeception/codecept run integration

// Output
➜  photogramappy git:(master) ✗ vendor/codeception/codeception/codecept run integration
Codeception PHP Testing Framework v2.5.2
Powered by PHPUnit 6.5.13 by Sebastian Bergmann and contributors.
Running with seed:


Integration Tests (1) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
✔ TechnosailorPhotogramappyGoogle_MapsMapTest: Test_google_maps_url_is_constructed_correctly (0.05s)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Time: 2.38 seconds, Memory: 30.25MB

OK (1 test, 4 assertions)

All this test does is verify that the Google Maps URL is formed correctly in this class. Note: This series doesn’t actually touch on the Google Maps integration because it’s a pretty basic user experience. It takes the latitude and longitude from our metaboxes, and constructs a URL for the Google Maps Static API. It’s pretty low-tech.

One other sidenote here that is important to note. The Google Maps class uses a second class I briefly touched on earlier in the series. It uses the Default class (that is injected into the Maps class via DI (yay!). You might look at that class and wonder why I don’t use a simple array to setup default values. Simply, because arrays are ad hoc, do not have a predefined structure, are harder for IDEs to autocomplete, or typehint against. Arrays have their purpose, but in many cases, an array, while simpler, isn’t up to the job like a class is. The more you know! (Oh and look at this! And this! And this!

So as I bring this series to an end, I hope it’s been useful for other developers, particularly WordPress developers. One of the parts of my job over the years that I’ve enjoyed the most is mentoring. People used to comment about how verbose I can be in communicating. Sometimes I over-communicate, but that is a trait that comes from wanting people to understand why things should be done in a particular fashion (although I’m not always right).

I also want to showcase my work, and why I do it the way I do it. So if you’re looking for an experienced PHP and WordPress developer with over 14 years in the industry, who has a penchant for teamwork, forward-thinking development practices, and who has 2 adorable dogs… hire me.

Using Modern PHP with WordPress: Part 1 – Photogramappy

This series of articles is meant to demonstrate how to use Modern PHP approaches, technologies, and best practices in parallel with WordPress. WordPress, to its credit, is beginning to push adoption of PHP 7 and will require it by the end of 2019. That is a massive move, but I recommend PHP 7.2 at a minimum. PHP 7 has reached its End of Life (EOL).

Over the past few years, I have worked with WordPress in a modern way. Approaches to Dependency Injection, Composer-driven projects, and containerization is all part of what has made my best WordPress development life this one.

I wrote a simple plugin that I affectionately have called – but granted, annoying to spell – Photogramappy. The idea is built on a personal project idea I had several years ago.

You see, I live in Baltimore and it gets a bad rep. From crime to decidedly smaller, and more quaint than neighboring Washington, D.C., many perceive it through the ideas of The Wire. Fun fact: Baltimoreans, generally, despise outsiders comparing the city to The Wire.

I digress.

The project was simple, yet turned out to be difficult. Baltimore is a lot like Chicago in the sense that all the action happens out in the neighborhoods, and not downtown. There are over 250 neighborhoods in this small city, known as Charm City. Each has a different sense of self, different cultures, traditions, experiences. I very simply wanted to photograph something unique about each of the neighborhoods as an art project. I wanted to map the location of each photo from each neighborhood, write a small story about the context and map it on a website.

This project never came to fruition, but I decided to revisit the plugin I had initially wrote, and reinvent it in Modern PHP.

I’ve been frustrated by development techniques in the more traditional WordPress world, and the hesitancy of developers to adopt modern PHP as a minimal viable entry point to plugin development.

It requires PHP 7.2, WordPress 4.9.9+, and Composer. For the testing suite, it requires Codeception, Selenium standalone server, OpenJDK, and ChromeDriver.

If you are intrigued by this, please talk to me about how I can help your organization. In Part 2, we will dive into dependency injection and containers.