Custom Error page InvalidArgumentException Symfony2 - symfony

I'm trying to generate a custom error page in symfony2:
I was following this tuto.
However it shows me this:
InvalidArgumentException in YamlFileLoader.php line 145: A service definition must be an array or a string starting with "#" but NULL found for service "kernel.listener.allotaxi_exception_listener" in services.yml. Check your YAML syntax.
my services.yml :
services:
kernel.listener.allotaxi_exception_listener:
class: AlloTaxi\AlloTaxiBundle\Listener\ExceptionListener
arguments: [#templating, #kernel]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
ExceptionListener.php :
namespace AlloTaxi\AlloTaxiBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
class ExceptionListener {
protected $templating;
protected $kernel;
public function __construct(EngineInterface $templating, $kernel)
{
$this->templating = $templating;
$this->kernel = $kernel;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
// provide the better way to display a enhanced error page only in prod environment, if you want
if ('prod' == $this->kernel->getEnvironment()) {
// exception object
$exception = $event->getException();
// new Response object
$response = new Response();
// set response content
$response->setContent(
// create you custom template AcmeFooBundle:Exception:exception.html.twig
$this->templating->render(
'AlloTaxiBundle:Exception:error.html.twig',
array('exception' => $exception)
)
);
// HttpExceptionInterface is a special type of exception
// that holds status code and header details
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
} else {
$response->setStatusCode(500);
}
// set the new $response object to the $event
$event->setResponse($response);
}
}
}
Any idea what could it be? I followed exactly when he did there.

services:
kernel.listener.allotaxi_exception_listener:
class: AlloTaxi\AlloTaxiBundle\Listener\ExceptionListener
arguments: [#templating, #kernel]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
By looking at the services.yml file posted in your question, you are missing the indentation level for "class", "arguments" & "tags". They appear to be at the same level as that of your service name "kernel.listener.allotaxi_exception_listener"
Have a look at above service definition, notice the indentation between service name and class, arguments & tags.
When dealing with yml files one needs to be careful about proper hierarchy

Related

Call container inside ExceptionListener

I am using Symfony and i have created custom ExceptionListener to handle error.
class ExceptionListener
{
protected $templating;
protected $kernel;
public function __construct(EngineInterface $templating, $kernel)
{
$this->templating = $templating;
$this->kernel = $kernel;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
// exception object
$exception = $event->getException();
// new Response object
$response = new Response();
$response->setContent(
// create you custom template AcmeFooBundle:Exception:exception.html.twig
$this->templating->render(
'Exception/exception.html.twig',
array('exception' => $exception)
)
);
// HttpExceptionInterface is a special type of exception
// that holds status code and header details
if ($exception instanceof HttpExceptionInterface) {
$response->setStatusCode($exception->getStatusCode());
$response->headers->replace($exception->getHeaders());
} else {
$this->container->get('monolog.logger.db')->info('something happened 34', [
'foo' => 'bar'
]);
$response->setStatusCode(500);
}
if($exception instanceof FatalThrowableError){
return $this->templating->render(
'Exception/exception.html.twig'
);
}
// set the new $response object to the $event
$event->setResponse($response);
}
}
and in service
kernel.listener.acme_foo_exception_listener:
class: AppBundle\Listener\ExceptionListener
arguments: [#templating, #kernel]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
My aim is to when symfony throws exception i need to log error in database so i have created Logger event as per below link and it works fine when i called in controller but this event doesn't work when i called inside ExceptionListener.
I got following error
Notice: Undefined property:
AppBundle\Listener\ExceptionListener::$container in
can any one help me how i can pass container inside Listener
As said by geoforce your service doesn't know about the container. Quick fix for this by changing the service arguments:
arguments: [#templating, #container]
While changing the listener constructor to:
public function __construct(EngineInterface $templating, ContainerInterface $container)
{
$this->container = $container;
// ...
This should work, but injecting the entire container is quite an overkill and should definitely be done differently. Inject just what you need:
arguments: [#templating, '#monolog.logger.db']
And your constructor:
public function __construct(EngineInterface $templating,
LoggerInterface $logger)
{
$this->logger = $logger;
// ...
Log with $this->logger->info(...).
Since you've said that you're new to Symfony, I'd heavily recommend reading the DI component (http://symfony.com/doc/current/components/dependency_injection.html) docs. Understanding what DI does and how it works is mandatory to work with MVC frameworks like Symfony.
Like the error says, you are trying to access a property that does not exist:
$this->container->get('monolog.logger.db')->info('something happened 34', [
'foo' => 'bar'
]);
The container property is never declared nor assigned. If you want to access your logging service inject it in your service definition, like you did with the templating and kernel services.
Updated service definition:
kernel.listener.acme_foo_exception_listener:
class: AppBundle\Listener\ExceptionListener
arguments: [#templating, #kernel, #monolog.logger.db]
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
And update your class constructor to accept the log service as the third argument.

How to avoid showing the stack trace in Symfony2 while on production?

When a user without the necessary role tries to access a page that requires a higher token, I rightfully get
AccessDeniedHttpException: Token does not have the required roles
But that is followed by the whole stack trace including the file path. E.g.
'file' => string '/var/www/myApp/vendor/jms/security-extra-bundle/JMS/SecurityExtraBundle/Security/Authorization/Interception/MethodSecurityInterceptor.php'
How do I remove the debugging information?
I've made sure I am on prod (app.php) and that
$kernel = new AppKernel('prod', false);
Update
To complete the solution based on Robert's answer, I had to tweak the yml syntax to include a dash
services:
core.exceptlistener:
class: UserBundle\Listener\ExceptionListener
arguments: ['#service_container', '#templating']
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
And then create the twig file at the location specified in onKernelException, ensuring that the file inherited the base twig for the rest of my project
REG: To avoid stack trace you can use Event Listener
Listener Class
<?php
namespace UserBundle\Listener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\Template;
/**
* Created by PhpStorm.
* User: robert
* Date: 20/8/17
* Time: 2:26 PM
*/
class ExceptionListener
{
/**
*
* #var ContainerInterface
*/
private $container;
function __construct($container) {
$this->container = $container;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
// We get the exception object from the received event
$exception = $event->getException();
$response = new Response($this->container->get('templating')->renderResponse('UserBundle:Exception:error403.html.twig',
array('exception'=>$exception->getMessage())));
$event->setResponse($response);
}
}
IN your services.yml
services:
core.exceptlistener:
class: UserBundle\Listener\ExceptionListener
arguments: ['#service_container', '#templating']
tags:
{ name: kernel.event_listener, event: kernel.exception, method: onKernelException }

#mailer and #twig in argument of a service error ServiceCircularReferenceException

I'm trying to put twig like argument of my service but i have always the same error :
ServiceCircularReferenceException in bootstrap.php.cache line 2129
Circular reference detected for service "doctrine.orm.default_entity_manager",path: "doctrine.orm.default_entity_manager -> doctrine.dbal.default_connection -> wh.participant_listener -> wh.participant_notification -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_provider.username -> fos_user.user_manager".`
This is my service.yml file
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
arguments: [#mailer, #twig]
wh.participant_listener:
class: WH\TrainingBundle\EventListener\ParticipantListener
arguments: [#wh.participant_notification]
tags:
- { name: doctrine.event_listener, event: postUpdate }
- { name: doctrine.event_listener, event: postPersist }
My PartcicipantListenerFile
namespace WH\TrainingBundle\EventListener;
use Doctrine\ORM\Event\LifecycleEventArgs;
use WH\TrainingBundle\Notification\Notification;
class ParticipantListener
{
protected $notification;
public function __construct(Notification $notification)
{
$this->notification = $notification;
}
}
This probleme exist only when i pass #wh.participant_notificationin arguments of my second service
Any body has an idea ?
Thank's a lot
I've find a solution, not pretty, but it works :
First i pass the service container in argument of my service
services:
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
arguments: ['#service_container']
wh.participant_listener:
class: WH\TrainingBundle\EventListener\ParticipantListener
arguments: ['#wh.participant_notification']
tags:
- { name: doctrine.event_listener, event: postPersist }
then in my Notification.php class :
use Symfony\Component\DependencyInjection\ContainerInterface as Container;
private $container;
public function __construct(Container $container) {
$this->container = $container;
}
public function subscribValidation ($participant) {
$templating = $this->container->get('templating');
$mailer = $this->container->get('mailer');
...
I can't create protected var $twig because the probleme persiste.
I repeat, its only with twig service (or template).
Maybe another one find a better solution ...
The circular message, while unclear, should guide you.
Doctrine entity manager loads its listeners,wh.participant_notification among them. Your service requires twig which in turns requires a chain of other things, doctrine entity manager among them. This causes the exception above.
One solution to this issue could be to use setter injection
So you can just define your service as:
wh.participant_notification:
class: WH\TrainingBundle\Notification\Notification
calls:
- [setMailer, ["#mailer"]]
- [setTemplating, ["#templating"]]
and add to your Notification class the setter methods
class Notification
{
private $mailer;
private $templating;
public function setMailer(\Mailer $mailer)
{
$this->mailer = $mailer;
}
public function setTemplating(\Symfony\Bundle\TwigBundle\TwigEngine $templating)
{
$this->templating= $templating;
}
...your code...

can't pass argument to event listener

Using Symfony 2.5.3. I'm trying to send a 'welcome' e-mail when someone has succesfully registered(FOS Userbunde), using an EventListener. The event fired is fos_user.registration.success.
So I added a service:
mycustom_user.registration_success:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: [#mycustom_user.mailer]
tags:
- { name: kernel.event_listener, event: fos_user_registration_success, method: onRegistrationSuccess}
The listener itself:
namespace Mycustom\UserBundle\EventListener;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\UserEvent;
use FOS\UserBundle\Event\FormEvent;
use Mycustom\UserBundle\Mailer\Mailer;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RegistrationListener implements EventSubscriberInterface
{
protected $mailer;
public function __construct(Mailer $mailer)
{
$this->mailer = $mailer;
}
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_SUCCESS => 'onRegistrationSuccess',
);
}
public function onRegistrationSuccess(FormEvent $event)
{
$user = $event->getForm()->getData();
$this->mailer->sendWelcomeMessage($user);
$url = $this->router->generate('fos_user_security_login');
$event->setResponse(new RedirectResponse($url));
}
}
The mailer itself contains the rendering of the content of the email, also registered as a service:
mycustom_user.mailer:
class: Mycustom\UserBundle\Mailer\Mailer
arguments: ['#templating']
mycustom_user.mailer is argument for the listener. But somehow I keep getting this error:
Catchable Fatal Error: Argument 1 passed to
Mycustom\UserBundle\EventListener\RegistrationListener::__construct()
must be an instance of Mycustom\UserBundle\Mailer\Mailer, none given,
called in mycustom/app/cache/dev/appDevDebugProjectContainer.php
on line 2214 and defined in mycustom/src/Mycustom/UserBundle/EventListener/RegistrationListener.php line 19
I tried other arguments like #doctrine (and changed the listeners constructor accordingly), but I keep getting the same error. Also the #templating argument to the mailer service doesn't work.
What am I doing wrong here?
So, the problem that I had was that I had two listeners in one class. And the 2nd service definition didn't contain the argument for the constructor. Should be like this:
mycustom_user.registration_initialize:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: ['#mycustom_user.mailer']
tags:
- { name: kernel.event_subscriber, alias: mycustom_user_registration_listener}
mycustom_user.registration_success:
class: Mycustom\UserBundle\EventListener\RegistrationListener
arguments: ['#mycustom_user.mailer']
tags:
- { name: kernel.event_subscriber }

Set parameter for Doctrine filter on every request in Symfony2

I have a Doctrine filter in Symfony2 project. I am trying to set filter's parameter to some value (taken from session) on every request.
The problem is that filter object is created after Symfony's onKernelRequest event, so I can't set it from there. If I try to set it in Doctrine's postConnect event circular dependency is detected:
ServiceCircularReferenceException: Circular reference detected for service "doctrine.orm.private_entity_manager", path: "routing.loader -> assetic.asset_manager -> twig -> translator.default -> doctrine.orm.private_entity_manager -> doctrine.dbal.private_connection -> year_visibility.parameter_setter".
The question is, where (or rather how) should I set filter's parameter?
You can try to define filters manually and pass required parameters at the same time.
services:
app.filter_manager:
class: App\Bundle\AppBundle\Filter\FilterManager
arguments: [#doctrine.orm.entity_manager, #session]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
And in the filter manager class:
// ...
public function __construct(EntityManager $em, SessionInterface $session)
{
$this->em = $em;
$this->session = $session;
}
// ...
public function onKernelRequest()
{
$this->em->getConfiguration()->addFilter('filter_name', 'Filter/Class/Name/With/Ns');
$filter = $this->em->getFilters()->enable('filter_name');
$filter->setParameter('param_name', $this->session->get('param_name'));
}
As seen here: https://stackoverflow.com/a/14650403/244058 ,
you can have an instance of your Filter class at kernel boot.
So, your instance would be available very early.
<?php
class MyBundle extends Bundle
{
public function boot()
{
$em = $this->container->get('doctrine.orm.default_entity_manager');
$conf = $em->getConfiguration();
$conf->addFilter(
'filter_name',
'Doctrine\Filter\TestFilter'
);
// either enable it here, or later in the event listener
$em->getFilters()->enable('filter_name');
}
}
After that, just add a kernel.event_listener that listens on kernel.request and set a filter parameter (something like this):
<?php
class DoctrineSqlFilterConfigurator
{
private $em; // inject the entity manager somehow (ctor is a good idea)
public function onKernelRequest(GetResponseEvent $event)
{
$filter = $this->em->getFilters()->enable('filter_name');
$filter->setParameter('param_name', $event->getRequest()->getSession()->get('param_name'));
}
}

Resources