Front to Back 02

Laying out a foundation for the plugin.

The recipe

After laying out a first iteration scope and declared that my inspiration source for this project is the Perch CMS it’s time to list the recipes for the practical part.
To cover the Test Driven Development development process I will rely on Codeception, the wp-browser add-on module and Function Mocker.
I will develop with PHP version 5.3 in mind to make my life easier and be able to tap into the powers of Composer and Packagist.
I will finally use dependency injection and a Dependency Injection Container to avoid intricate globals and constants and make my testing life easier in return.
To handle the copious meta box needs I will use the CMB2 meta box framework.

The Plugin object

The main file in a WordPress standard plugin is usually in charge of defining some accessory methods, classes and functions and kickstarting the plugin functions.
I’ve pushed a first version of the plugin to GitHub and will just point out two files that are fundamental to the plugin flow

<?php
/**
 * Plugin Name: Front to Back
 * Plugin URI: http://theAverageDev.com
 * Description: Create post meta fields editing front-end templates.
 * Version: 1.0
 * Author: theAverageDev
 * Author URI: http://theAverageDev.com
 * License: GPL 2.0
 */

include 'src/functions/version_compat.php';

if ( version_compare( phpversion(), '5.3', '<' ) ) {
    add_action( 'admin_notices', 'ftb_php_version_notice' );

    return;
}

// Composer autoload file
require_once __DIR__ . '/vendor/autoload.php';


// CMB2 initialization
require_once __DIR__ . '/vendor/webdevstudios/cmb2/init.php';

// Finally plugin init
require_once __DIR__ . '/init.php';

I’m graciously handling the case where the PHP version is not at least the 5.3 one and then loading the Composer generated autoload file.
I’m loading the CMB2 plugin from the vendor folder as I’m using Composer to pull it and finally starting the plugin main file.

<?php
use tad\FrontToBack\OptionsPage;
use tad\FrontToBack\Templates\Creator;
use tad\FrontToBack\Templates\Filesystem;
use tad\FrontToBack\Templates\MasterChecker;

require_once __DIR__ . '/src/functions/commons.php';

// grabs the instance of the plugin that's globally stored    
$plugin = ftb();

/**
 *  Variables
 */
$plugin->set( 'path', __DIR__ );
$plugin->set( 'url', plugins_url( '', __FILE__ ) );

$templates_extension = 'php';
$plugin->set( 'templates/extension', $templates_extension );
$plugin->set( 'templates/master-template-name', "master.{$templates_extension}" );

/**
 * Initializers
 */
$plugin->set( 'templates/default-folder', function () {
    return WP_CONTENT_DIR . '/ftb-templates';
} );

$plugin->set( 'options-page', function () {
    return new OptionsPage();
} );

$plugin->set( 'master-template-checker', function () {
    return new MasterChecker();
} );

$plugin->set( 'templates-filesystem', function () {
    $templates_folder = ftb_get_option( 'templates_folder' );
    $templates_folder = $templates_folder ?: ftb()->get( 'templates/default-folder' );

    return new Filesystem( $templates_folder );
} );

$plugin->set( 'templates-creator', function () {
    return new Creator( ftb()->get( 'templates-filesystem' ) );
} );

/**
 * Kickstart
 */
/** @var OptionsPage $optionsPage */
$optionsPage = $plugin->get( 'options-page' );
$optionsPage->hooks();

/** @var MasterChecker $masterTemplateChecker */
$masterTemplateChecker = $plugin->get( 'master-template-checker' );
$masterTemplateChecker->hooks();

/** @var Creator $templatesCreator */
$templatesCreator = $plugin->get( 'templates-creator' );
$templatesCreator->hooks();

The plugin class will take care of keeping instances of the relevant classes in a register and lazy loading them upon first request; by default classes will be stored as singletons but the third parameters of the set method will allow for new instances to be returned on each call.

<?php
namespace tad\FrontToBack;

class Plugin {

    /**
     * @var array
     */
    protected $register;

    /**
     * @var array
     */
    protected $singletons;

    /**
     * Plugin constructor.
     */
    public function __construct() {
    }

    public function set( $key, $value, $singleton = true ) {
        $this->register[ $key ] = $value;
        if ( $singleton ) {
            $this->singletons[] = $key;
        }
    }

    public function get( $key ) {
        if ( ! array_key_exists( $key, $this->register ) ) {
            return null;
        }
        if ( is_callable( $this->register[ $key ] ) ) {
            $value = call_user_func( $this->register[ $key ] );
            if ( array_key_exists( $key, $this->singletons ) ) {
                $this->register[ $key ] = $value;
            } else {
                return $value;
            }
        }

        return $this->register[ $key ];
    }
}

The plugin is doing little but I will delve deeper as development progresses.