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.