Etsy sync WordPress plugin 04

My dependency injection solution for the Etsy WordPress plugin.

Anonymous constructors

The use of dependency injection is a requirement for code testability that I’ve decided to implement using setter and factory methods: any class that’s not modeling a data object will not declare a __construct method and will expose instead an instance method making its minimum requirements clear.
This allows a class like ELH_ShopRetriever to be constructed in tests without any side effects and have total control over it.

class ELH_ShopRetriever extends ELH_AbstractSyncStep {

    public static function instance( ELH_KeychainInterface $keychain, ELH_ApiInterface $api ) {
        $instance           = new self();
        $instance->keychain = $keychain;
        $instance->api      = $api;

        return $instance;

    public function run() {
        // TODO: Implement run() method.

Its parent abstract class defines setter methods common to any synchronization step and will leave the specific implementation of the run method for the class to implement.

abstract class ELH_AbstractSyncStep implements ELH_ApIUserInterface, ELH_KeychainUserInterface, ELH_StepInterface

     * @var ELH_KeychainInterface
    protected $keychain;

     * @var ELH_ApiInterface
    protected $api;

     * @var ELH_StatusInterface
    protected $status;

     * @var ELH_StepInterface
    private $next;

    public function set_api(ELH_ApiInterface $api)
        $this->api = $api;

    public function set_keychain(ELH_KeychainInterface $keychain)
        $this->keychain = $keychain;

    public function set_next(ELH_StepInterface $next)
        $this->next = $next;

   public function set_status(ELH_StatusInterface $status){
       $this->status = $status;

    abstract public function run();

Injecting dependencies

Anonymous constructors are fine in testability terms but require a knowledge of the object at creation time that’s cumbersome and too informed on the object inner workings to be a good thing.
Being this a test project for me I’ve decided to use a the PHP 5.2 compatible dependency injection container to get the plugin rolling and be able to later configure the dependencies without touching the code.
Right now the main plugin file does little but set up the dependencies and start the plugin

 * Plugin Name: Etsy Little Helper
 * Plugin URI:
 * Description: An Etsy tool for WordPress websites.
 * Version: 1.0
 * Author: theAverageDev
 * Author URI:
 * License: GPL2

require 'vendor/autoload_52.php';

define( 'ELH_PLUGIN_FILE', __FILE__ );

$di_container = ELH_DI::instance();
ELH_Main::instance( $di_container )->hook();

while the ELH_DI class wraps the tad_DI52_Container class in a singleton configuration to be able to use it as a service locator should the need arise; both anti-patterns and both knowingly and willingly broken.

class ELH_DI {

     * @var tad_DI52_Container
    protected static $container;

     * @return tad_DI52_Container
    public static function instance() {
        if ( empty( self::$container ) ) {
            self::$container = new tad_DI52_Container();

        return self::$container;

     * @return ELH_DI
    public function set_dependencies() {
        $this->set_ctor( 'keychain', 'ELH_Keychain' );
        $this->set_ctor( 'api', 'ELH_Api' );
        $this->set_ctor( 'sync_strategy_selector', 'ELH_SyncStrategySelector' );

        // Sync steps
        $dependencies = array( '@keychain', '@api' );
        $this->set_ctor( 'listings_retriever', 'ELH_ListingsRetriever::instance', $dependencies );
        $this->set_ctor( 'shop_retriever', 'ELH_ShopRetriever::instance', $dependencies )
             ->set_next( '@listings_retriever' );

        // Sync set up
        $dependencies = array( '@sync_strategy_selector' );
        $this->set_ctor( 'synchronizer', 'ELH_Synchronizer::instance', $dependencies )
             ->set_first_step( '@shop_retriever' );

        return $this;



I will test the ELH_Main class to make sure it properly hooks to schedule the synchronization.

I appreciate your input