I have a problem with throwing Symfony 2 exception inside event listner. When I do it I get Uncaught exception (onRequest method of listener). Is there any way to throw exception in listener that can be caught by Symfony.
I was trying to avoid this problem by changing the controller in case of an exception (onController method of listener). Target controller had just one line:
throw $this->createNotFoundException('Test');
The controller swap worked but it also resulted in uncaught exception error.
How to wrap a listener or swapped controller inside symfony exception catcher.
My listener:
namespace AppBundle\Listener;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use AppBundle\Controller\HomeController;
class SessionListener {
private $container;
public function __construct($container)
{
$this->container = $container;
}
public function onController(FilterControllerEvent $event)
{
$controller=new HomeController();
$replacementController= array($controller, 'notFoundAction');
$event->setController($replacementController);
}
public function onRequest(Event $event) {
$session = $this->container->get('session');
$kernel = $this->container->get('kernel');
throw new NotFoundHttpException('Test');
}
}
My services.yml
services:
app.session_listener:
class: AppBundle\Listener\SessionListener
arguments: [#service_container]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onRequest }
- { name: kernel.event_listener, event: kernel.controller, method: onController }
probably with a try catch
public function onRequest(Event $event) {
try{
$session = $this->container->get('session');
$kernel = $this->container->get('kernel');
}catch(NotFoundHttpException ex){
throw new NotFoundHttpException('Test');
}
}
As an alternative you can return a 404 response instead of throwing an exception:
class SessionListener
{
public function onRequest(GetResponseEvent $event)
{
$event->setResponse(new Response('Test', 404));
}
}
Related
I am developping some kind of cronjob monitoring on our symfony2 application.
I created a CommandExecution entity with a 'completed' property.
I'm using console events to create and persist this entity.
My service :
kernel.listener.console:
class: Evo\CronBundle\EventListener\ConsoleListener
arguments: [#doctrine.orm.entity_manager]
tags:
- { name: kernel.event_listener, event: console.command, method: onConsoleCommand }
- { name: kernel.event_listener, event: console.terminate, method: onConsoleTerminate }
- { name: kernel.event_listener, event: console.exception, method: onConsoleException }
and the ConsoleListener:onConsoleCommand() and ConsoleListener:onConsoleTerminate() methods called when a command starts and ends to execute :
public function onConsoleCommand(ConsoleCommandEvent $event)
{
$command = $event->getCommand();
$commandEntity = $this->em->getRepository('EvoCronBundle:Command')->findOneBy(['name' => $command->getName()]);
$commandExecution = new CommandExecution();
$commandExecution->setCommand($commandEntity);
$this->em->persist($commandExecution);
$this->em->flush();
// here I want to pass my entity to the command, so I can get it back in the onConsoleTerminate() method
$command->setCommandExecution($commandExecution);
}
public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
$command = $event->getCommand();
// here, retrieve the commandExecution entity passed in onConsoleCommand() method
$commandExecution = $command->getCommandExecution();
$commandExecution->setCompleted(true);
$this->em->flush();
}
As you can see in these methods, I would like to add a commandExecution property to the Symfony Component Console\Command\Command.php, so I can pass in my commandExecution entity and change its status.
Do I have to override this component ? If yes, how ? Or can I do it in a simpler way ?
Add the commandExecution property in your ConsoleListener
protected $commandExecution = null;
Then set it in your onConsoleCommand() method
public function onConsoleCommand(ConsoleCommandEvent $event)
{
$command = $event->getCommand();
$commandEntity = $this->em->getRepository('EvoCronBundle:Command')->findOneBy(['name' => $command->getName()]);
$commandExecution = new CommandExecution();
$commandExecution->setCommand($commandEntity);
$this->em->persist($commandExecution);
$this->em->flush();
$this->commandExecution = $commandExecution;
}
Then you can access to it in your onConsoleTerminate() method
public function onConsoleTerminate(ConsoleTerminateEvent $event)
{
$command = $event->getCommand();
// here, retrieve the commandExecution entity passed in onConsoleCommand() method
$commandExecution = $this->commandExecution;
$commandExecution->setCompleted(true);
$this->em->flush();
}
Don't forget to test if commandExecution value is null in onConsoleTerminate() method
I have 2 controllers that use a same service:
CONTROLLER 1
namespace MO\FrontendBundle\Controller;
use MO\FrontendBundle\Controller\SuperClass\MOSearchController;
use Symfony\Component\HttpFoundation\JsonResponse;
class ResultsController extends MOSearchController {
public function detailAction() {
$vm = $this->get("vehicles_manager"); // SERVICE IN QUESTION
$result = $vm->getVehicleDetail($idVehicle);
}
}
CONTROLLER 2
namespace MO\FrontendBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MetatagsController extends Controller {
public function metatagsController() {
$vm = $this->get("vehicles_manager"); // SERVICE IN QUESTION
$result = $vm->getVehicleDetail($idVehicle);
}
}
The service is regularly declared in services.yml file:
services:
vehicles_manager:
class: MO\FrontendBundle\Service\VehiclesManager
arguments: [#logger, %vehicles_manager.endpoint%, %vehicles_manager.scope%, %vehicles_manager.consumer%, %form_values.manual_gear_code%]
tags:
- { name: monolog.logger, channel: solrws }
vehicles_memory:
class: MO\FrontendBundle\Service\VehiclesMemory
arguments: [#request_stack]
The problem is that while in the first controller there are no errors, in the second i get the error:
Fatal error: Call to a member function get() on a non-object in C:\Users\d.test\workspace\test\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Controller\Controller.php on line 274
Any ideas?
In your second controller you didn't suffix the method name with Action then, it is not really an end point.
your code for the second controller should be:
namespace MO\FrontendBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MetatagsController extends Controller {
public function metatagsAction() {
$vm = $this->get("vehicles_manager"); // SERVICE IN QUESTION
$result = $vm->getVehicleDetail($idVehicle);
}
}
I've got little problem.
I've overrided all html exception templates in app/Resources/TwigBundle/Resources/Exception...
My problem is, that these error pages are only rendered when I'm on the dev env.
When it comes to prod I'll get something like:
http://i.stack.imgur.com/GHd7t.png
Please help me out.
You can do it by registering a service listening to the kernel.view event.
in your service.yml:
your.kernel_listener:
class: Your\AppBundle\EventListener\KernelListener
arguments: [#kernel]
tags:
- { name: kernel.event_listener, event: kernel.view, method: onKernelView }
in your class KernelListener:
namespace Your\AppBundle\EventListener;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
class KernelListener
{
private $kernel;
public function __construct(Kernel $kernel)
{
$this->kernel = $kernel;
}
public function onKernelView(GetResponseForControllerResultEvent $event)
{
if ($this->kernel->getEnvironment() == 'dev') {
$result = $event->getControllerResult();
$response = new Response(print_r($result, true), 200, array('Content-Type' => 'text/html'));
$event->setResponse($response);
}
}
}
Have a look at this guide.
I'm using the event listener onKernelResponse.
I used :
if (HttpKernelInterface::MASTER_REQUEST != $event->getRequestType()) {
return;
}
It's having a MASTER_REQUEST twice in my action, there is one before the <!DOCTYPE html> <html> <head>etc, and the other one as excepted after the end of the layout.
He is my services.yml :
history.listener:
class: VENDOR\MyBundle\Service\HistoryListener
arguments: [#doctrine.orm.entity_manager, #logger, #history]
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
- { name: kernel.event_listener, event: kernel.response, method: onKernelResponse }
Did I do something wrong?
Finally found the origin of the problem : the debug toolbar !
It actually sends an ajax request, meaning another MASTER_REQUEST..
My solution is to filter on Controller, with a white/black list of controller's names.
UPDATE:
Here is the code I'm using (so you can easily exclude some other controllers if needed).
public function __construct()
{
$this->classesExcluded = array("Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController");
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller) || HttpKernelInterface::MASTER_REQUEST != $event->getRequestType() || in_array(get_class($controller[0]), $this->classesExcluded)) {
return;
}
// ...
}
How can make an Object accessible in a controllers which was set in kernel.controller Event?
I have a onKernelController method which is run before controller and I need some data in a controller which was set in onKernelController.
You can use dependency injection to solve this:
1) Turn your object/class into a service and inject it into the listener.
services:
your_object:
class: Your\Namespace\YourObjectClass
your_listener:
class: Your\Namespace\YourListener
arguments: [ #your_object ]
tags:
- { name: kernel.controller, event: kernel.request, method: onKernelController }
2) Set some property (can be an object aswell) on the injected object
listener class
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
class YourListener
{
protected $object;
public function __construct($object)
{
$this->object = $object;
}
public function onKernelController(FilterControllerEvent $event)
{
// ...
$object->setProperty('some_property_value');
}
}
3.) Get the property inside a container-aware controller (or turn your controller into a service aswell and inject #your_object )
controller
use Symfony\Component\DependencyInjection\ContainerAware;
// or: use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class SomeController extends ContainerAware // or: extends Controller
{
public function someAction()
{
$property = $this->container->get('your_object')->getProperty;
// $property => 'some_property_value'
}