Widget classes dependency injection 02

Getting hacky.

Context

In my earlier post I had outlined a possible alternative for the construction and development of WordPress widget classes.
The effort is to allow for an easier dependency injection and thus allow for a test like this one to be written

public function tests_not_connected_user_get_connect_cta_and_url(){
    $user = $this->prophesize('myplugin_User');
    $user->is_connected()->willReturn(false);

    $messages = $this->prophesize('myplugin_UserMessages');
    $messages->get_register_cta()->shouldBeCalled();

    $urls = $this->prophesize('myplugin_UserUrls');
    $urls->get_register_url()->shouldBeCalled();

    $sut = new myplugin_CTAWidget($user->reveal(), $messages->reveal(), $urls->reveal());
    $sut->widget();
}

for this method

/**
 * Outputs the content of the widget
 *
 * @param array $args
 * @param array $instance
 */
public function widget( $args, $instance ) {

    if $this->user->is_connected(){
        $user_type = $this->user->get_type();
        $message = $this->user_messages->get_cta_for($user_type);
        $url = $this->user_urls->get_for($user_type);
    } elseif ($this->user->is_pending_registration()) {
        $message = $this->user_messages->get_pending_registration_cta();
        $url = $this->user_urls->get_pending_registration_url();
    } else {
        $message = $this->user_messages->get_register_cta();
        $url = $this->user_urls->get_register_url();
    }

    echo sprint_f('<a href="%s">%s</a>', $url, $message);
}

defined in the myplugin_CTAWidget class.

Starting from the constructor

I start by modifying the widget class constructor

public function __construct(
    myplugin_UserInterface $user,
    myplugin_UserMessagesInterface $user_messages,
    myplugin_UserUrlsInterface $user_urls
) {
    $this->user = $user;
    $this->user_messages = $user_messages;
    $this->user_urls = $user_urls;

    $widget_ops = array( 
        'class_name' => 'my_widget',
        'description' => 'My Widget is awesome',
    );
    parent::__construct( 'my_widget', 'My Widget', $widget_ops );
}

Leaving things as they are the standard WordPress widget instantiation flow will fail in an error generated by the call to the WP_Widget_Factory::register method

public function register( $widget_class ) {

    $this->widgets[$widget_class] = new $widget_class();

}

The cause being dependencies the class needs not being supplied.

Different flow

In place of registering the widget the usual way

add_action('widgets_init', 'add_my_widgets');
function add_my_widgets(){
    register_widget('myplugin_CTAWidget');
}

I will take an approach that will allow me to instantiate the widget the way I need to

add_action('widgets_init', 'add_my_widget');
function add_my_widget(){
    global $wp_widget_factory;

    $user = new myplugin_User();
    $user_messages = new myplugin_UserMessages());
    $user_urls = new myplugin_UserUrls();

    $widget = new myplugin_CTAWidget($user, $user_messages, $user_urls);

    $wp_widget_factory->widgets['myplugin_CTAWidget'] = $widget;
}

This will instantiate the widget injecting dependencies the way I want and will complete the cycle using a dependency action container.
In the example below I’m using DI52 to manage the flow; see the sudo code below.

// some plugin or bootstrap file

$container = new tad_DI52_Container();

$container['container'] = $container;
$container['user'] = 'myplugin_User';
$container['user-messages'] = 'myplugin_UserMessages';
$container['user-urls'] = 'myplugin_UserUrls';

$container['cta-widget'] = array('myplugin_CTAWidget', '@user', '@user-messages', '@user-urls');

$container['widgets-register'] = array('myplugin_WidgetsRegister', '@cta-widget');

$widget_register = $container['widget-register'];
$widget_register->hook();

// src/WidgetsRegister.php

class myplugin_WidgetsRegister {

    protected $cta_widget;

    public function __construct($cta_widget){
        $this->cta_widget = $cta_widget;
    }

    public function hook(){
        add_action('widgets_init', array($this, 'register_widgets'));
    }

    public function register_widgets(){
        global $wp_widget_factory;

        $wp_widget_factory->widgets[get_class($this->cta-widget)] = $this->cta-widget;
    }
}

Not a great difference but great one for me that have come to appreciate the use of a DI container.