📜 ⬆️ ⬇️

Symfony Components, Event Dispatcher (theory, part 2)

image
Hey. This is the second part of the translation of the Symfony documentation for the Event Dispatcher component , the first part is here . The second part of the translation is a collection of general recipes for using the Event Dispatcher component . To test these examples, you do not need to connect to the symfony framework - as noted in the first part , the components are independent of the framework. Once again I want to note that the code of the symfony components is now being processed for use with symfony 2 (PHP> = 5.3.2). This translation refers to the stable version of the Event Dispatcher component . But, as far as I understood from comparing a stable version of a component with the current one under Symfony 2 , they are functionally a little different, that is, the documentation will be useful using the new version of the component (PHP> = 5.3.2) . So, let's begin.

Walk through the capabilities of the Event Dispatcher object


If you were viewing the sfEventDispatcher class code , you should have noted that the class does not work on the principle of the Singleton design pattern (it does not have the static getInstance () method). This is done on purpose, since you may want to work with several competing event dispatchers in a single PHP request. But it also implies that you need a way to pass a dispatcher to objects that need to interact with events or generate them.

The best practice is to include the event dispatcher object in your objects using the “ dependency injection ” pattern.

: (Fowler) 3 , : (interface injection), , ; (setter injection), (setter), ; (constructor injection), .
  1. : (Fowler) 3 , : (interface injection), , ; (setter injection), (setter), ; (constructor injection), .
  2. : (Fowler) 3 , : (interface injection), , ; (setter injection), (setter), ; (constructor injection), .
  3. : (Fowler) 3 , : (interface injection), , ; (setter injection), (setter), ; (constructor injection), .
You can use constructor injection:
class Foo {
protected $dispatcher = null ;

public function __construct(sfEventDispatcher $dispatcher) {
$ this ->dispatcher = $dispatcher;
}
}

Or installer injection (setter injection):
class Foo {
protected $dispatcher = null ;

public function setEventDispatcher(sfEventDispatcher $dispatcher) {
$ this ->dispatcher = $dispatcher;
}
}
Deciding which of these methods to use is a matter of taste. I prefer to use the constructor injection, since the objects are completely initialized at the creation stage. But when you have a large list of dependencies, using an installer deployment can be a better way, especially for optional dependencies.
')
Note: If you use dependency injection as in our previous two examples, you can easily use the Symfony Dependency Injection container for more elegant management of these objects.

We do something before or immediately after calling the method.


If you want to do something before or immediately after calling the method, you can declare an event, respectively, at the beginning or at the end of the method:
class Foo
{
// :
// ( ):
// static public function do_before($arr){
// echo "before!!!";
// }

public function send($foo, $bar)
{
// -
$ event = new sfEvent($ this , 'foo.do_before_send' , array( 'foo' => $foo, 'bar' => $bar));
// : :
// $this->dispatcher->connect('foo.do_before_send', 'Foo::do_before');
$ this ->dispatcher->notify($ event );

//
// $ret = ...;

// -
$ event = new sfEvent($ this , 'foo.do_after_send' , array( 'ret' => $ret));
$ this ->dispatcher->notify($ event );

return $ret;
}
}

Adding methods to the class


To allow many classes to add methods to each other, you can declare a magic __call () method in the class you want to extend, thus:
class Foo
{
// ...

public function __call($method, $arguments)
{
// 'foo.method_is_not_found'
//
$ event = new sfEvent($ this , 'foo.method_is_not_found' , array( 'method' => $method, 'arguments' => $arguments));

// $method
$ this ->dispatcher->notifyUntil($ event );

// ?
if (!$ event ->isProcessed()) {
throw new sfException(sprintf( 'Call to undefined method %s::%s.' , get_class($ this ), $method));
}

// ,
return $ event ->getReturnValue();
}
}

Now create a class that will store the handler:
class Bar
{
public function addBarMethodToFoo(sfEvent $ event )
{
// 'bar'
if ( 'bar' != $ event [ 'method' ])
{
//
return false ;
}

// ( foo)
$foo = $ event ->getSubject();

// bar
// , ,
// :
// $arguments = $event['arguments'];
$arguments = $ event [ 'parameters' ];

// -
// ...

//
$ event ->setReturnValue($someValue);

//
return true ;
}
}

Finally, we add the new bar method to the Foo class:
$dispatcher->connect( 'foo.method_is_not_found' , array($bar, 'addBarMethodToFoo' ));

Modifying arguments


If you want to allow external classes to modify the arguments passed to the method immediately before it is executed, add a filter event to the beginning of the method:
class Foo
{
// ...

public function render($template, $arguments = array())
{
//
$ event = new sfEvent($ this , 'foo.filter_arguments' );
// : :
// $this->dispatcher->connect('foo.filter_arguments', array(new Bar(), 'filterFooArguments'));
$ this ->dispatcher->filter($ event , $arguments);

//
$arguments = $ event ->getReturnValue();

// ...
// :
// return $arguments;
}
}

In the role of the filter can serve such a class:
class Bar {
public function filterFooArguments(sfEvent $ event , $arguments) {
// :
// $arguments = array('33', '44');
$arguments[ 'processed' ] = true ;
return $arguments;
}
}

That's all for now. Further, I would like to write about how the new versions of components on PHP 5.3.2 differ from their stable counterparts in PHP 5.2 . Or I'll start writing about the Dependency Injection component. I also think it would be interesting to write about how Sensio Labs tests the code (for example, the same Components using PHPUnit). Until new meetings.

Source: https://habr.com/ru/post/90829/


All Articles