Delegate factories #4145

Merged
merged 6 commits into from Apr 15, 2013

Projects

None yet

3 participants

@Ocramius
Member

I've been working on this concept for some time, but this is basically a cleaned up version of #2995

Why do we need this?

The idea is to be able to build delegates of any existing service. This can be used to build "decorators" for service instantiation and for proxying.

Delegation

Imagine that you have a DbConnection and a LoggedDbConnection object. A LoggedDbConnection wraps around a "real" DbConnection instance like following, but provides additional API that sends queries to a logger:

$connection = new LoggedDbConnection(new DbConnection());

Currently, our problem is that such a wrapper requires us to completely re-define the factory that instantiates DbConnection. There's no way to re-use logic from such a factory. We have to rewrite it and be aware of its internal logic:

    'db' => function ($sl) {
        // rewrite all ze logic here
        return new LoggedDbConnection($dbConnection);
    },

Proxying

We can proxy any service (with the assumption that we know its class name or implemented interface) and make it "lazy", so that its dependencies don't get instantiated when we request them. This allows us to build lightweight containers, as explained in https://github.com/Ocramius/ProxyManager/blob/957de8648d64d2d0a3777fec793b52c198d39c82/docs/lazy-loading-value-holder.md. Also, we don't have to worry about how the original service is instantiated, since the original factory is still existing.

Example

In the following example, I've simply replaced the original service (instance of stdClass) with a new one that contains the original service in property tab:

<?php

use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\DelegateFactoryInterface;

require_once __DIR__ . '/vendor/autoload.php';

class FooDelegateFactory implements DelegateFactoryInterface
{
    public function createDelegateWithName(
        ServiceLocatorInterface $serviceLocator,
        $name,
        $requestedName,
        $callback
    ) {
        $foo      = new \stdClass();
        $foo->tab = $callback(); // php5.4, heh

        return $foo;
    }
}

$serviceManager = new ServiceManager();
$serviceManager->setFactory('foo', function() {
    $foo = new \stdClass();
    $foo->bar = 'baz';

    return $foo;
});
$serviceManager->setService('foo-delegate-factory',new FooDelegateFactory());
$serviceManager->setDelegate('foo', 'foo-delegate-factory');

var_dump($serviceManager->get('foo')->tab->bar); // baz

Implementation flaws

  • Additional method call to doCreate, since I splitted create to allow this kind of behavior
  • doCreate is public, since I cannot use a closure and call ->bindTo($this) in PHP 5.3
  • It may not be clear at first, but the delegate factory is a service (which is a huge advantage, but is a bit different from what we're used to see)

Documentation

A documentation PR has been provided at zendframework/zf2-documentation#825

This was referenced Mar 29, 2013
@Ocramius
Member
Ocramius commented Apr 3, 2013

Actually, the factory does not produce delegates, but delegators, as explained in http://en.wikipedia.org/wiki/Delegation_pattern. I will change this after having prepared the docs PR.

@Ocramius Ocramius closed this Apr 12, 2013
@Ocramius Ocramius reopened this Apr 12, 2013
@Ocramius
Member

Good to go

@prolic
Contributor
prolic commented Apr 13, 2013

+1 on this!

@weierophinney weierophinney added a commit that referenced this pull request Apr 15, 2013
@weierophinney weierophinney Merge branch 'feature/4145' into develop
Close #4145
e6717d5
@weierophinney weierophinney merged commit b531502 into zendframework:develop Apr 15, 2013

1 check passed

default The Travis build passed
Details
@Ocramius Ocramius deleted the Ocramius:feature/eager-services branch Mar 16, 2014
@weierophinney weierophinney added a commit to zendframework/zend-servicemanager that referenced this pull request May 15, 2015
@weierophinney weierophinney Merge pull request zendframework/zendframework#4145 from Ocramius/fea…
…ture/eager-services

Delegate factories
f0f60b9
@weierophinney weierophinney added a commit to zendframework/zend-servicemanager that referenced this pull request May 15, 2015
@weierophinney weierophinney Merge branch 'feature/4145' into develop 692b1d8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment