zendframework/zf2 — pull request #4145 by Ocramius (2013-03-29 15:50:12+00:00)
Title: Delegate factories

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](http://en.wikipedia.org/wiki/Delegation_pattern) 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:

```php
$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:

```php
'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/master/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
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)