Alexander Steshenko | On Software Engineering

Dependency Injection and IOC container in Procedural programming

A couple of funky ideas on managing dependencies in a non-object oriented application.

Dependencies management

Popular ways to manage dependencies in code are global variables, the registry pattern, service locator. These are all accessible in procedural programming, too.

Using Registry pattern with Zend Framework’s Zend_Registry class:

class ArticlesController extends Zend_Controller_Action
{
    public function listAction()
    {
        $db = Zend_Registry::get('db'); // fetching the dependency
        $articles = $db->fetchAll("select * from articles");

        $this->view->assign('articles', $articles);
    }
}

and this is how it’d look without objects:

function list_action() {
    $db_link = registry\get('db_link'); // fetching the dependency
    $articles = db\fetch_all($db_link, "select * from articles");

    return view\render_template('articles\list.phtml', 
                                ['articles' => $articles]);
}

Looks very similar. This is how implementation of registry\get function would look like:

namespace framework\registry;

function &_get_registry_storage() {
    static $registry = [];
    return $registry;
}

function key_exists($key) {
    return array_key_exists($key, _get_registry_storage());
}

function get($key) {
    if (!key_exists($key)) {
        trigger_error("No entry is registered for key '$key'",
            E_USER_ERROR);
    }
    return _get_registry_storage()[$key];
}

function set($name, $value) {
    _get_registry_storage()[$name] = $value;
}

Not as verbose as Zend_Registry.

However, using these approaches comes at a cost and sometimes it isn’t acceptable. What is almost always acceptable is the Dependency Injection.

Dependency Injection

The same code but using the just out of the oven Zend Framework 2(still in development) and dependency injection:

namespace Articles\Controller;

use Zend\Mvc\Controller\ActionController,
    Zend\View\Model\ViewModel;

class ArticlesController extends ActionController
{
    /**
     * @var \Zend\Db\Adapter\Adapter
     */
    protected $db;

    public function __construct(\Zend\Db\Adapter\Adapter $db)
    {
        $this->db = $db;
    }

    public function listAction()
    {
        // see, the dependency is already there:
        $articles = $this->db->fetchAll("select * from articles");
        return new ViewModel(array(
            'articles' => $articles
        ));
    }
}

what procedural variant could look like:

namespace controllers\articles;

function list_action($db_link) {
    // same here, no need to fetch the dependency, we pass it as a parameter:
    $articles = db\fetch_all($db_link, "select * from articles");

    return view\render_template('articles\list.phtml',
        ['articles' => $articles]);
}

As we don’t have objects in this case - the only thing we ever depend on is data. And when I say “Dependency Injection in procedural programming” I really mean data to “Data Injection”. A function may depend on data prepared in a special manner, a data structure, by passing the data through a set of functions. For instance, this is how $db could be created:

$config = new DbConfig(‘db-config-file.ini’);
$db = new Db($config);
$controller = new ArticlesController($db);
$response = $controller->listAction();

and this would be the procedural variant:

$config = load_db_config_data(‘db-config-file.ini’);
$db_link = connect_to_db($config);
$response = controllers\articles\list_action($db);

Although if you think of data structures as objects without behavior, there is no difference.

IoC Container

This is the answer for OOP, a special object whose job is to create services for you and resolve/inject all dependencies they need. Usually, you configure the container so it knows the hierarchy of objects in the app and then you get services you need from it in an upper level of your application.

The new web-framework ZF2 above already has the container built-in so you only need to configure it, with something like this:

'di' => array(
    'instance' => array(
        'alias' => array(
            'articles' => 'Application\Controller\ArticlesController',
        ),
        'Db' => array(
            'parameters' => array(
                'db_config' => 'db-config',
            )
        ),
        // ...
    )
)

This is very convenient, particularly because this configuration is not hardcoded in the application and you can switch configurations per environment.

Procedural IoC Container

This is my little idea for an IOC container without objects:

function execute($function, $di_config) {
    $params = func_get_args();
    array_shift($params);
    array_shift($params);

    if (isset($di_config[$function])) {
        $new_params = [];
        foreach ($di_config[$function] as $prepare) {
            $prepare_function = array_shift($prepare);
            $new_params[] = call_user_func_array(__FUNCTION__,
                array_merge([$prepare_function, $di_config], $prepare));
        }
        $params = array_merge($new_params, $params);
    }

    return call_user_func_array($function, $params);
}

As we don’t have objects the container doesn’t need to create anything. Instead it should call a function on our behalf, automatically injecting dependencies (data dependencies) as function parameters. If some dependencies need to be prepared (i.e. to get some data you need to run some other functions first) - the container does that as well.

The container itself is a function, too. The first parameter is the function you want to call, the second parameter is the IOC configuration and you may provide additional parameters for the function that are known (i.e don’t need to be resolved by the container).

Here is what configuration for the container might look like, check out the comments:

$di_config = [
    // let's say we want to call the list_action controller, that lists articles
    'list_action' => [
        // it needs one parameter to be provided 
        // and that parameter is the result of calling this function:
        ['connect_to_db'],
        // ... you could list more dependencies here
    ],
    'connect_to_db' => [
        // to connect to db you need to get the db configuration, which is still unknown
        // what you do know is what function knows how to get it:
        ['get_db_config']
    ],
    'get_db_config' => [
        // to get db config you need to load application configuration from
        // the file config.php which IS known
        ['load_config', 'config.php'] // - first parameter must be a function, then go the params
    ],
];

And this is how you’d use it:

/**
 * Retrieves db configuration from application config
 *
 * @param $config
 * @return string
 */
function get_db_config($config) {
    echo "get_db_config($config) called\n";

    return 'db_config';
}

/**
 * Loads application configuration from file specified
 *
 * @param $config_file
 * @return string
 */
function load_config($config_file) {
    echo "load_config($config_file) called\n";

    return 'config';
}

/**
 * Established connection to database
 *
 * @param $db_config
 * @return string
 */
function connect_to_db($db_config) {
    echo "get_db_config($db_config) called\n";
    return 'db_link';
}

/**
 * Lists articles on a html page...
 *
 * @param $db_link
 * @return string
 */
function list_action($db_link) {
    echo "list_action($db_link) called\n";
    return 'response';
}

$di_config = [
    'list_action' => [
        ['connect_to_db'],
    ],
    'connect_to_db' => [
        ['get_db_config']
    ],
    'get_db_config' => [
        ['load_config', 'config.php']
    ],
];

echo execute('list_action', $di_config);

// Output:
/*
        load_config(config.php) called
        get_db_config(config) called
        get_db_config(db_config) called
        list_action(db_link) called
        response 
*/

I could say that essentially this is currying + configuration.


Comments and feedback are much appreciated. See contact details here