DI52 decorator pattern support 02

Decorator pattern support added to the di52 library.

New methods

With version 1.2.5 I’ve added decorator pattern support to the di52 package; two new methods are now available to support it.
A closure based solution common to many dependency injection containers has always been available with a syntax like this (from the package README file):

class A implements SomeInterface {
    public function out(){
        return 'foo';
    }
}

class B implements SomeInterface {

    protected $interface;

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

    public function out(){
        return $this->interface->out() . 'bar';
    }
}

class C implements SomeInterface {

    protected $interface;

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

    public function out(){
        return 'baz' . $this->interface->out();
    }
}

$container->bind('SomeInterface', function($container){
    $base = $container->make('A');

    return new C(new B($base));
});

The same could not be said, due to its lack of closure support, for a PHP 5.2 compatible solution.
Again from the README file the last lines can now be written like this:

$decoratorsChain = array('C', 'B', 'A');
$container->bindDecorators('SomeInterface', $decoratorsChain);

or like this to have the decorator chain treated as a singleton:

$decoratorsChain = array('C', 'B', 'A');
$container->singletonDecorators('SomeInterface', $decoratorsChain);

A more practical example

Examples based around classes with sudo names like A,B and C are fine if I can understand the pattern in the first place.
A more practical example might be one where I have a base PostRepository class implementing the PostRepositoryInterface in the WordPress echosystem:

class PostRepository implements PostRepositoryInterface {
    public function getPosts(array $args = array()){
       return get_posts($args);
    }
}

I have a first CachedPostRepository decorator:

class CachedPostRepository implements PostRepositoryInterface{

    private $repository;
    private $cache;

    public function __construct(PostRepositoryInterface $repository, CacheInterface $cache){
        $this->repository = $repository;
        $this->cache = $cache;
    }

    public function getPosts(array $args = array()){

        sort($args);
        $cacheKey = serialize($args);

        $cached = $this->cache->fetch($cacheKey)

        if(empty($cached){
            $posts = $this->repository->getPosts($args);
        }

        $this->cache->store($cacheKey, $posts);

        return $posts;
    }
}

and an UserAccessPostRepository decorator:

class UserAccessPostRepository implements PostRepositoryInterface{

    private $repository;

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

    public function getPosts(array $args = array()){
        $args = $this->filterAccessiblePostTypes($args);

        return false != $args ? $this->repository->getPosts($args) : array();
    }

    protected filterAccessiblePostTypes(array $args){
        $postTypes = (array)$args['post_type'];
        $args['post_type'] = array_intersect($postTypes, $this->allowedPostTypesForUser());

        return count($args['post_type']) ? $args : false;
    }

    protected function allowedPostTypesForUser(){
        return (array)get_user_meta(get_current_user_id(), '_allowedPostTypes');
    }
}

The power of the decorator pattern is that decorators can be inserted and removed from the chain without the limits a concrete implementation based on inheritance would pose.
In the application bootstrap file I could then do this and conditionally add decorators as required by the application:

$container = new tad_DI52_Container();

$decoratorsChain = array();

if(get_option('_appShouldCache')){
    $decoratorsChain[] = 'CachedPostRepository';
}

if(get_option('_appShouldRestrictAccess')){
    $decoratorsChain[] = 'UserAccessPostRepository';
}

$decoratorsChain[] = 'PostRepository';

$container->bindDecorators('PostRepositoryInterface', $decoratorsChain);

// later

$postRepository = $container->make('PostRepositoryInterface');

Next

I’ve got a pair of other itches I’d like to scratch and that I will tackle next: custom bindings and performance.