Feature/delegate factories #825
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
.. _zend.service-manager.delegator-factories: | ||
|
||
Delegator service factories | ||
=========================== | ||
|
||
``Zend\ServiceManager`` can instantiate `delegators`_ of requested services, decorating them | ||
as specified in a delegate factory implementing the `delegator factory interface`_. | ||
|
||
The delegate pattern is useful in cases when you want to wrap a real service in a `decorator`_, | ||
or generally intercept actions being performed on the delegate in an `AOP`_ fashioned way. | ||
|
||
Delegator factory signature | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
A delegator factory has following signature: | ||
|
||
.. code-block:: php | ||
|
||
namespace Zend\ServiceManager; | ||
|
||
interface DelegatorFactoryInterface | ||
{ | ||
public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback); | ||
} | ||
|
||
The parameters passed to the ``DelegatorFactoryInterface#createDelegatorWithName`` factory are following: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. are the following |
||
|
||
- ``$serviceLocator`` is the service locator that is used while creating the delegator for the requested service | ||
|
||
- ``$name`` is the canonical name of the service being requested | ||
|
||
- ``$requestedName`` is the name of the service as originally requested to the service locator | ||
|
||
- ``$callback`` is a `callable`_ that is responsible of instantiating the delegated service (the real service instance) | ||
|
||
A Delegator factory use case | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
|
||
A typical use case for `delegators`_ is to handle logic before or after a method is called. | ||
|
||
In the following example, an event is being triggered before ``Buzzer::buzz()`` is called and some output text | ||
is prepended. | ||
|
||
The delegated object ``Buzzer`` (original object) is defined as following: | ||
|
||
.. code-block:: php | ||
:linenos: | ||
|
||
class Buzzer | ||
{ | ||
public function buzz() | ||
{ | ||
return 'Buzz!'; | ||
} | ||
} | ||
|
||
The delegator class ``BuzzerDelegator`` has following structure: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. has the following |
||
|
||
.. code-block:: php | ||
:linenos: | ||
|
||
use Zend\EventManager\EventManagerInterface; | ||
|
||
class BuzzerDelegator extends Buzzer | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extending is required to respect the LSP |
||
{ | ||
protected $realBuzzer; | ||
protected $eventManager; | ||
|
||
public function __construct(Buzzer $realBuzzer, EventManagerInterface $eventManager) | ||
{ | ||
$this->realBuzzer = $realBuzzer; | ||
$this->eventManager = $eventManager; | ||
} | ||
|
||
public function buzz() | ||
{ | ||
$this->eventManager->trigger('buzz', $this); | ||
|
||
return $this->realBuzzer->buzz(); | ||
} | ||
} | ||
|
||
To use the ``BuzzerDelegator``, you can run following code: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. run the following |
||
|
||
.. code-block:: php | ||
:linenos: | ||
|
||
$wrappedBuzzer = new Buzzer(); | ||
$eventManager = new Zend\EventManager\EventManager(); | ||
|
||
$eventManager->attach('buzz', function () { echo "Stare at the art!\n"; }); | ||
|
||
$buzzer = new BuzzerDelegator($wrappedBuzzer, $eventManager); | ||
|
||
echo $buzzer->buzz(); // "Stare at the art!\nBuzz!" | ||
|
||
This logic is fairly simple as long as you have access to the instantiation logic of the | ||
``$wrappedBuzzer`` object. | ||
|
||
You may not always be able to define how ``$wrappedBuzzer`` is created, since a factory for it may be | ||
defined by some code to which you don't have access, or which you cannot modify without introducing further | ||
complexity. | ||
|
||
Delegator factories solve this specific problem by allowing you to wrap, decorate or modify any existing service. | ||
|
||
A simple delegator factory for the ``'buzzer'`` service can be implemented as following: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the single quotes here are needed. |
||
|
||
.. code-block:: php | ||
:linenos: | ||
|
||
use Zend\ServiceManager\DelegatorFactoryInterface; | ||
use Zend\ServiceManager\ServiceLocatorInterface; | ||
|
||
class BuzzerDelegatorFactory implements DelegatorFactoryInterface | ||
{ | ||
public function createDelegatorWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName, $callback) | ||
{ | ||
$realBuzzer = call_user_func($callback); | ||
$eventManager = $serviceLocator->get('EventManager'); | ||
|
||
$eventManager->attach('buzz', function () { echo "Stare at the art!\n"; }); | ||
|
||
return new BuzzerDelegator($realBuzzer, $eventManager); | ||
} | ||
} | ||
|
||
You can then instruct the service manager to handle the service ``'buzzer'`` as a delegate: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think the single quotes here are needed. |
||
|
||
.. code-block:: php | ||
:linenos: | ||
|
||
$serviceManager = new Zend\ServiceManager\ServiceManager(); | ||
|
||
$serviceManager->setInvokableClass('buzzer', 'Buzzer'); // usually not under our control | ||
|
||
// as opposed to normal factory classes, a delegator factory is a | ||
// service like any other, and must be registered: | ||
$serviceManager->setInvokableClass('buzzer-delegator-factory', 'BuzzerDelegatorFactory'); | ||
|
||
// telling the service manager to use a delegator factory to handle service 'buzzer' | ||
$serviceManager->addDelegator('buzzer', 'buzzer-delegator-factory'); | ||
|
||
// now, when fetching 'buzzer', we get a BuzzerDelegator instead | ||
$buzzer = $serviceManager->get('buzzer'); | ||
|
||
$buzzer->buzz(); // "Stare at the art!\nBuzz!" | ||
|
||
You can also call ``$serviceManager->addDelegator()`` multiple times, with the same or different delegator | ||
factory service names. Each call will add one decorator around the instantiation logic of that particular | ||
service. | ||
|
||
.. _`AOP`: http://en.wikipedia.org/wiki/Aspect-oriented_programming | ||
.. _`decorator`: http://en.wikipedia.org/wiki/Decorator_pattern | ||
.. _`callable`: http://www.php.net/manual/en/language.types.callable.php | ||
.. _`delegators`: http://en.wikipedia.org/wiki/Delegation_pattern | ||
.. _`delegator factory interface`: https://github.com/zendframework/zf2/tree/master/library/Zend/ServiceManager/DelegatorFactoryInterface.php |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
has the following