Developing a plugin using DI and TDD 01

Implementing a DI52 powered PHP 5.2 compatible plugin.

Why?

To demonstrate how and why a dependency injection container can be used to develop a PHP 5.2 compatible WordPress plugin that sticks to WordPress minimum requirements and tries to follow some modern programming techniques.

What?

The plugin will be simple in its functions:

  • add a shortcode that allows the site admin to add an “I’d like this” button
  • add a shortcode that allows the site admin to add an “I’d like this” button that shows the number of people that would like something
  • the admin should be able to add a short description to each button in a post to have contextual feedback
  • the admin should be shown a counter for each “I’d like this” in the post edit screen

It should allow someone publishing a post to have user feedback on one or more ideas expressed in the post; as an example someone editing a post should be able to write this:

Hot and sticky roads, unbearable temperatures: let’s replace road lights with shower heads [idlike]

And have the following result on the page I’d like this button on twentysixteen

Yes: useless.

What’s to wire?

Before any code is put in place I’ll define what’s to attach where in the WordPress flow:

  • a shortcode handler in charge of rendering the shortcode markup on the page, one for each version of the shortcode
  • a metabox that will show up on the post edit screen and display either a list of “I’d like this” button clicks or an empty placeholder message
  • an ajax call handler to handle the user click as we do not want the page to reload

I will probably find more dependencies along the way but that’s enough by now.

Compose me something

I’m using Composer to manage the project dependencies and will init it first thing

composer init

After some routine questions I will have the following composer.json file produced for me:

{
    "name": "lucatume/idlikethis",
    "description": "Add an \"I'd like this\" button anywhere.",
    "type": "wordpress-plugin",
    "license": "GPL 2.0",
    "authors": [
        {
            "name": "Luca Tumedei",
            "email": "luca@theaveragedev.com"
        }
    ],
    "minimum-stability": "stable",
    "require": {}
}

DI52 will be required as a dependency

composer require lucatume/di52

it will, in turn, require the composer-php52 package that will allow me to generate PHP 5.2 compatible autoload files for the project.
This will require a small addition to the composer.json file to have the PHP 5.2 version generated

{
  "name": "lucatume/idlikethis",
  "description": "Add an \"I'd like this\" button anywhere.",
  "type": "wordpress-plugin",
  "license": "GPL 2.0",
  "authors": [
    {
      "name": "Luca Tumedei",
      "email": "luca@theaveragedev.com"
    }
  ],
  "minimum-stability": "stable",
  "require": {
    "lucatume/di52": "^1.2"
  },
  "scripts": {
    "post-install-cmd": [
      "xrstf\\Composer52\\Generator::onPostInstallCmd"
    ],
    "post-update-cmd": [
      "xrstf\\Composer52\\Generator::onPostInstallCmd"
    ],
    "post-autoload-dump": [
      "xrstf\\Composer52\\Generator::onPostInstallCmd"
    ]
  }
}

and a composer update to have the vendor/autoload_52.php put in place.

Plugin file

The plugin file will grow over time but for the time being it’s doing nothing more but define the plugin and call the autoload file.

<?php
/**
 * Plugin Name: I'd like this
 * Plugin URI: http://theAverageDev.com
 * Description: Add an "I'd like this" button anywhere.
 * Version: 1.0
 * Author: theAverageDev
 * Author URI: http://theAverageDev.com
 * License: GPL 2.0
 */

include dirname(__FILE__) . '/vendor/autoload_52.php';

Tests

I’m not going anywhere without putting some tests in place and it’s time to get hold of the testing suite

composer require --dev lucatume/wp-browser

after a long dependency resolution Composer will download the wp-browser test suite in the vendor folder; scaffolding the tests is matter of another command

wpcept boostrap

I jump to the codeception.yml file to configure the modules to work in my current set up and specify my database and domain details.
I will run WordPress unit tests using the WPLoader module: that’s destructive on the database it works on so I will have a database set up just for it; another database will serve the site I can experiment on “by hand”.
In the end:

  • wp is the database that’s powering the local wp.dev installation
  • wp-tests is the database that will be used by WPLoader (and shredded by it)

Here it’s the updated codeception.yml file set up to match my local set up:

actor: Tester
paths:
    tests: tests
    log: tests/_output
    data: tests/_data
    helpers: tests/_support
settings:
    bootstrap: _bootstrap.php
    colors: true
    memory_limit: 1024M
modules:
    config:
        Db:
            dsn: 'mysql:host=127.0.0.1;dbname=wp-tests'
            user: root
            password: root
            dump: tests/_data/dump.sql
        WPBrowser:
            url: 'http://wp.dev'
            adminUsername: admin
            adminPassword: admin
            adminUrl: /wp-admin
        WPDb:
            dsn: 'mysql:host=127.0.0.1;dbname=wp-tests'
            user: root
            password: root
            dump: tests/_data/dump.sql
            populate: true
            cleanup: true
            url: 'http://wp.dev'
            tablePrefix: wp_
        WPLoader:
            wpRootFolder: /Users/Luca/Sites/wp
            dbName: wp-tests
            dbHost: 127.0.0.1
            dbUser: root
            dbPassword: root
            wpDebug: true
            dbCharset: utf8
            dbCollate: ''
            tablePrefix: wp_
            domain: wp.dev
            adminEmail: admin@wp.dev
            title: 'WP Tests'
            phpBinary: php
            language: ''
            plugins: ["idlikethis/idlikethis.php"]
            activatePlugins: ["idlikethis/idlikethis.php"]
        WPWebDriver:
            url: 'http://wp.dev'
            browser: phantomjs
            port: 4444
            restart: true
            wait: 2
            adminUsername: admin
            adminPassword: admin
            adminUrl: /wp-admin

A run of the test suite will confirm the settings are ok and properly set up

codecept run

I’d like this tests trial run

The error notice is due to WordPress trying to perform a deprecated operation on a new MySql version, nothing to worry about.
In the modules > config > WPLoader > plugins and activatePlugins module I’m instructing WPLoader to include the main plugin file and activate it before the tests.

Next

Now that scope and tests are in place it’s time to start developing the plugin and use some dependency injection for the goodness of testing.