Using the Symfony 2 Event Dispatcher - php

How, if at all, should a Symfony 2 bundle developer use the event dispatcher(s) that ship with a stock Symfony 2 system?
I've been digging around in the source for Symfony's event dispatcher, and some of what I've seen has me a little confused as to how I, a third party bundle creator, should use the event dispatcher that ships with Symfony.
Specifically, I noticed that a stock Symfony system has two event dispatcher services. The event_dispatcher and the debug.event_dispatcher. Which service the HttpKernel uses is dependent on environment, and driven by the generated dev or prod container file.
//dev kernel instantiation uses `debug.event_dispatcher` service
new \Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel(
$this->get('debug.event_dispatcher'),
$this,
$this->get('debug.controller_resolver')
);
//prod kernel instantiation uses `event_dispatcher` service
new \Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel(
$this->get('event_dispatcher'),
$this,
new \Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver($this, $this->get('controller_name_converter'), $this->get('monolog.logger.request', ContainerInterface::NULL_ON_INVALID_REFERENCE)));
So far this all makes sense — as it's the debug.event_dispatcher that implements functionality in the web profile's event's tab, including the ability to see which listeners were called, and which listeners were not called.
However, I noticed most (if not all) third party bundles use a hard coded event_dispatcher service call. For example, the JMS/JobQueueBundle uses the following
$this->dispatcher = $this->getContainer()->get('event_dispatcher');
Events dispatched like this fire correctly, but the debug.event_dispatcher doesn't know about them, which means the Symfony web profiler will incorrectly list a called listener as uncalled. Additionally, it's not clear how a bundle author could avoid this, as they don't have the advantage of generating a container file and the HTTP Kernel object doesn't expose an accessor for the protected dispatcher object.
So, is this a bug in Symfony?
Or is the event_dispatcher service only intended for Kernel events, meaning all those bundle authors are misusing it?
Or (the most likely candidate), is it something else I'm missing or haven't considered?

It looks like the scenario I described above doesn't apply to that latest version of Symfony (2.4.1). Specifically, in 2.4.1, the generated app container file
app/cache/dev/appDevDebugProjectContainer.php
contains the following
$this->aliases = array(
//...
'event_dispatcher' => 'debug.event_dispatcher',
//...
);
That is, unlike the Symfony 2.3.6 project I was working on, the event_dispatcher service has been aliased to the debug.event_dispatcher service (when Symfony runs in development mode). This means when other bundles ask for an event_dispatcher service in dev mode, they're really getting a debug.event_dispatcher service. This lets the the debug.event_dispatcher know about all the events, and it can correctly report on which ones were dispatched.
While it's not a concrete answer, it does indicate the Symfony team was/is aware of the issue, which leads me to believe it's their intention for Bundle developers to use the event_dispatch service fro their own events.

Related

What is the advantage of using a service in symfony

I have a question, why symfony use a service instead of "use" directly into a controller.
I have a class named DisplayManager and I declare this class as a service.
Why not use DisplayManager directly into a controller using "use DisplayManager"
Thanks
The advantage of thinking about "services" is that you begin to think about separating each piece of functionality in your application into a series of services. Since each service does just one job, you can easily access each service and use its functionality wherever you need it. Each service can also be more easily tested and configured since it's separated from the other functionality in your application. This idea is called service-oriented architecture and is not unique to Symfony or even PHP. Structuring your application around a set of independent service classes is a well-known and trusted object-oriented best-practice. These skills are key to being a good developer in almost any language.
http://symfony.com/doc/current/service_container.html
Questions that suggest define your class as service:
My class is used in several places in my application?
My class has many dependencies?
There is a good example for these topics here:
http://symfony.com/doc/current/service_container.html#what-is-a-service-container
Performance Advantage:
The service has a default condition that allows to the related class instantiate it only once.
Imagine a class with 5 dependencies (and these as well with their dependencies) and has been used in 20 parts of our system, what if we do not define this class as a service?
However define your class as a service is not required, this will always depend on the architecture of your system and the mission to be fulfilled its class, in addition to the dependencies you have.
Controllers are made only to take requests (http) and produce a response (http). No more. Never.
Services are totally independent classes that you can use everywhere. Moreover, with services, you can take advantage of DependencyInjection.
Services are more testable.
Services (like controllers and whatever) must have only a responsibility.
And so on ...

Global logging service

I have written my own logging class that I defined as a service and placed inside the AppBundle\Services namespace. I can access it easily inside the controller when I want to log something, but what about accessing it from other services?
I'd have to pass the logging service as a dependency injection, but what if I have more than 100 services defined (services, modules, event listeners etc. etc.), each of them having their own dependencies? It would create a mess.
I've been also thinking about extending some core service that defines the logging service, but then again - all my services, modules, event listeners, would have to extend one core class.
What's the best approach to solve this?
May be a good approach would be to rethink responsibilities in order to avoid the creation of too many services.
About the fact of consumming service itself, I think there are no problem to reuse as much as you need along the application lifecycle. In fact, symfony will handle the instantiation and you will be only consumming it as a service.
Another approach will be to create base classes for all your core objects, let these base classes to handle log services and final classes will log in a implicit way. This will not save service calls but almost will leave you to handle it manually.
If you think you will use your log feature in several projects then I recommend you to move to vendors folder and use it as external module, synchronised via composer to your github account. It will be like your own 3rd party product.
If you are not a symfony friend then you can break the law and create your own singleton pattern available by autoloading, but I think you should take advantage of the powerfull symfony service structure.

How Symfony 2 Security Bundle registers to Kernel Events

First of all I'm not a Symfony 2 expert, but I'm trying to understand how the symfony SecurityBundle actually works.
I saw in the docs that a basic implementation to a Session could be registering a EventListener to Kernel events.
Then i started looking at the more complex SecurityBundle to understand it, but I can't find any configuration in security.yml or method inside the Bundle that registers to any event.
I know the Bundle works but I would like to understand how it binds to the Request->Response flow to intercept and filter Requests.
Thank in advance...
The symfony security component has only one kernel listener, of course, on request and as Cerad said in a comment "The various services and registrations are done dynamically since they can be different for different firewalls", basically it's like a custom listener(1) in a kernel listener(2), the definition of kernel listener is in SecurityBundle(3), the magic of creation services from app/config/security.yml is in SecurityExtension(4) of SecurityBundle.
Basically to integrate symfony security in a non symfony project you need to add SecurityBundle in your AppKernel, so the hard part is to have an AppKernel that coexists with other systems.
(1)Symfony\Component\Security\Http\Firewall\ListenerInterface
(2)Symfony\Component\Security\Http\Firewall
(3)/vendor/symfony/symfony/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml
(4)Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension

Extending of Symfony container

is it possible overwrite/extend Symfony\Component\DependencyInjection\Container::get() method? I want automatic creating service, when it is not contain in container, but class of service exists.
For example:
Name of service is My.MyBundle.Model.FooRepository
Service with this name doesnt exists, but when i call:
$container->get('My.MyBundle.Model.FooRepository');
check class_exists for \My\MyBundle\Model\FooRepository and when its exists, add to container and return it. Dependencies of this new services will be resolve by kutny/autowiring-bundle.
This feature can be extended only for some namespaces or interfaces and in production enviroment can be cached, but for developing will be great helper.
Any idea?
This is not directly answering your question but maybe it's answering your need: if you want to have "auto-wiring" inside your Symfony project, you can use PHP-DI inside Symfony. PHP-DI is an alternative container that can do auto-wiring (which Symfony does not).
Have a look at the Symfony 2 integration documentation to see if it can fit your bill.

How can i better understand service layer in symfony2 in php

I am learning to use symfony2 but i am having hard time in learning what is service layer and where it can be used.
How can i better understand about service layer in symfony2
I'll try my best to explain services, but the Symfony2 docs do a better job than I'll be able to.
At it's core, a service is simply a class. A class becomes a service when it is registered with Symfony2's Dependency Injection Container (or just Container for short.) At this point, the class is part of an application's Service Layer.
A service may have dependencies (and often times, it does), but it is not required to. A service is often used as a dependency for other services. Let me give you an example:
You have a class used to send email, called EmailSender
You have a class that contains the instructions on how to send email over SMTP, called SmtpTransport
EmailSender has a dependency in the form of SmtpTransport. In other words, for EmailSender to be able to do its job, it needs an instance of SmtpTransport to be injected into it. (Note that although EmailSender has a dependency, SmtpTransport does not.)
Say you need to use EmailSender in five different places in your app. In each spot, you may have code like this:
$emailSender = new EmailSender(new SmtpTransport());
$emailSender->send($email)
That's not a lot of code (since EmailSender only has one dependency), but what happens when you decide that instead of using SMTP, EmailSender should use a transport class for sendmail? You'd have to edit each block of code in order to update it.
The alternative is to register EmailSender and its dependencies as services.
Registration looks like this:
// YourApp/YourBundle/Resources/config/services.yml
services:
smtp_transport:
class: YourApp\YourBundle\Email\SmtpTransport
email_sender:
class: YourApp\YourBundle\Email\EmailSender
arguments:
- #smtp_transport
Now, to use it (from a container aware context):
$container->get('email_sender')->send($email);
Notice how much simpler and cleaner it is to actually use the class to send emails? Sure, you have to do the work to register the service first, but that's trivial and only has to be done once.
Imagine that EmailSender has three dependencies, instead of one. By using services, you only need to modify the service registration without having to change your implementation code (the code used to send the email).
In short, the service layer acts as a central repository for classes that a) have one or more dependencies b) need to be used in a 'global' scope. By fetching these service classes from the Dependency Injection Container, you end up with less redundant code and an easier way to make wide reaching changes.
I may be wrong, but I think Symfony2 is the first PHP framework to implement a dependency injection container. As such, a lot of PHP devs may be unfamiliar with the concept of services. I definitely suggest making sure you understand it fully, as you'll see that it's a very powerful tool. Plus, the concept of services is in no way limited to Symfony2 or even PHP - you'll be able to take advantage of this concept in a wide variety of frameworks and languages.
Try Fabien's series of articles on the subject: Dependency Injection.

Categories