Working with Symfony 2, I created an exception event listener to catch my exceptions. It works good and inside of it I get the code of the exception like this :
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
$code = $exception->getCode()
....
}
For example, the exception I catch is a problem in a twig template. a variable does not exist.
The code of the exception is 0.
What does that mean ?
Where can I find a list of the different codes ?
Thank you
The exception code 0 is the default code in PHP. You can see it in the \Exception constructor.
Main of Symfony Exception have their code to zero because it's not overriden. And it's very difficult to get a complete list of all excetions' codes.
However, concerning your custom exceptions, that you created in your application, you can set their codes with values you want and handle them in your Event Listener.
For example, you could have something like :
class ResourceNotFoundException extends \Exception
{
public function __construct($message = '')
{
return parent::__construct($message, 404);
}
}
As a result, your exception's code for this exception will be 404. Now your application design and the traitment you want to do in your event listener will guide you in you devlepment.
Related
I am trying to understand how to build a event Listener in Symfony and how it works.
so I have looked at the Example calss from Symfony Docs.
<?php
// src/AppBundle/EventListener/AcmeExceptionListener.php
namespace AppBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
class AcmeExceptionListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
// You get the exception object from the received event
$exception = $event->getException();
$message = sprintf(
'My Error says: %s with code: %s',
$exception->getMessage(),
$exception->getCode()
);
// Customize your response object to display the exception details
$response = new Response();
$response->setContent($message);
// 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(Response::HTTP_INTERNAL_SERVER_ERROR);
}
// Send the modified response object to the event
$event->setResponse($response);
}
}
I understand that $event->getException() from GetResponseForExceptionEvent class picks a Event throwns in my application
What i don't quiet understand is how does why $exception can use getMessage() and getCode().
Can someone briefly explain these to me and Maybe a bit more about Symfony Event Listener.
This is a huge topic and I found it hard to understand when I was learning it via Symfony documentation and other online blogs. I will recommend you to view this tutorial from KNP University and believe me you will have a very clear understanding of it.
That has nothing to do with Symfony Events, when yo use $event->getException you are getting an instance of class Exception, this class has those methods defined.
The last question first.
why $exception can use getMessage() and getCode()
Symfony is a framework that use strict Objcet Oriented paradigm. So oftenly a function:
had an object type argument,
return also an object
and an object also can have it's own function and properties
In this example for simplicity we can think, $exception is an Exception object that have properties that can be see in here.
Also to understands how listeners works, it help if you have some knowledge about "Observer Design Pattern". This is a litle explanation.
there is an object called dispatcher (EventDispatcher)
this object had an array contains many listeners object
at point of time the kernel told dispatcher to dispatch
and dispatcher told all of listener to execute functions if exists
And to understanding your code, you create a listeners that active when Exception is thrown from anywhere. So you can choose to or not to show your error message.
I have an application in which my controllers are broken up into specific groups (API, CMS and front-end), this is already set up using Router groups. My question is how would one go about creating custom error handling for each group.
For example, when an exception occurs in any API controller action I would like to send back json with an error code and message, an exception in the CMS would output a detailed error page, and an exception on the front end would send the user to a generic 404 or 500 error page (as appropriate).
I am aware of how I could do this manually in each controller action, but that might get very repetitive. Ideally, I would want to create one handler for each and automatically use it across the board.
I am also aware of the App\Exceptions\Handler class, but if any of the controller groups could return a ModelNotFoundException, for example, how do I differentiate where the exception came from?
Is there another place that this type of exception handler could be inserted?
If you go to app\Exceptions\Handler.php file (which you mentioned) you can do it what you want.
You could for example define your render function this way:
public function render($request, Exception $e)
{
$trace = $e->getTraceAsString();
if ($e instanceof ModelNotFoundException
&& mb_strpos($trace, 'app\Http\Controllers\WelcomeController')
) {
return response()->json('Exception ' . $e->getMessage());
} elseif ($e instanceof ModelNotFoundException) {
return "General model not found";
}
return parent::render($request, $e);
}
and add to imports use Illuminate\Database\Eloquent\ModelNotFoundException;
and now if you throw ModelNotFoundException in your WelcomeController or you for example run another class' method from your WelcomeController and this method will throw ModelNotFoundException you can track it and return json response. In other cases if exception will be instance of ModelNotFoundException you can render your exception in other way and in all other cases you can use standard exception method to render.
This way you don't need to define anything in your controller, you can do it just in Handler and it it's more complicated you could create separate methods or create new classes to handle specific exceptions in the way you want and run them in this handler.
I have exception class:
class MartbooksException extends Exception
{
public function __construct($msg)
{
parent::__construct($msg, 0, null);
echo $msg;
Kohana::$log->add(Log::ERROR, $msg);
}
}
Kohana::$log->add(Log::ERROR, $msg);
Is this all I should do to write logs in application/logs files?
Is this good solution?
To follow the Kohana style I would recommend that you use:
Class Martbooks_Exception Extends Kohana_Exception
as declaration and place the file with name exception.php in classes/martbooks. This follows the style of Kohana.
Extending Kohana_Exception instead of Exception allows you to use variable substitution along the lines of
throw new Martbooks_Exception ('this is a :v', array (':v' => 'variable', ));
As for the defining a __construct()-method, the echo $msg; part would not be my preferred way of solving error handling, any echoing should be done in the block that catches the exception. The same could be argued for in the case of calling Kohana::$log->add(), but if you want log every Martbooks_Exception, your solution is perfectly valid. In that case I would rewrite your code to:
Class Martbooks_Exception Extends Kohana_Exception
{
public function __construct($message, array $variables = NULL, $code = 0)
{
parent::__construct ($message, $variables, $code);
Kohana::$log->add (Log::ERROR, __ ($message, $variables));
}
}
with a definition of __construct() that conforms to Kohana_Exception's __construct().
The only objection that I would have against logging with Log::ERROR level in the constructor is that it assumes that every exception is an application level error, which might be true of some exception types, but it could also be used to signal other meanings. The exact meaning of an exception should be left to the exception handling block.
To add a log message all you need is a single line indeed; Kohana::$log->add($level, $message[, $values]) see the api
Besides that, I don't think this is a valid solution. You had better create your own exception handler, as you can see on this page of the userguide
As well as many other, I'm trying to create my own MVC to improve my knowledge etc. :)
I'd like to ask about handling errors/exceptions in MVC pattern.
Dispatcher is getting information from Router about Route and getting Controller's and method's name from Route. Then dispatcher is trying to create instance of this Controller and execute this method.
Now let's imagine that our method is throwing exception (yes, it is not catched in method because of bad code :)). It could be custom made exception like NotFoundException or ApplicationInternalErrorException or one of SPL excpetions like RuntimeErrorException or InvalidArgumentException. It doesn't matter, really.
Of course, we want Dispatcher catch this exception, log it and respond accordingly exception type: display 404 page if NotFoundException was catched etc.
What is the best practice for doing it? Is Dispatcher a right place for doing it? Maybe the best place is FrontController (but I'm not using this pattern; Dispatcher is creating Controller instance.
Simple code:
class Dispatcher {
public function dispatch() {
$controller = $this->getRouter()->getController();
$method = $this->getRouter()->getMethod();
$args = $this->getRouter()->getArguments();
try {
call_user_func_array(array($controller, $method), array_values($args));
} catch (Exception $e) {
Logger::log($e->__toString);
if ($e instanceof NotFoundPage) {
call_user_func(array('ErrorController', 'notFound'))
} elseif ($e instanceof ...) {
...
} elseif ($e instanceof ...) {
...
} else {
...
}
}
}
}
Is it ok, or it is not a very good practice?
Thank you.
I'm simply using an external file (xml, yaml or anything you like) which contains all the routes for my web application. When a request comes the Router tries to match the request url to one of the routes I have in my file. If there were no match it simply redirects to the 404 page with a 404 header. I think that you don't need try/catch blocks for this.
I took the idea from Symfony.
Working on a symfony application that uses nusoap (is this the best method for integrating soap work with php/symfony?) for taking credit card payments.
I've simplified an example of my code below.
What I'm struggling with is the best way to handle exceptions. The example below only has 1 custom exception (where should my custom exceptions reside within the directory structure of symfony? (lib/exception?)) But what happens when there are several different types of exceptions that handle a specific error? It's not very elegant to have a try/catch block with 20 odd exceptions.
I'm also not sure of where I should be throwing and catching. I need to set some user flashes to alert the user of any problems, so I figure the catching should be done in the actions controller rather than within the class that handles the soap call.
Could anyone please advise where I might be going wrong?
I hate messy code/solutions and want to stick to the DRY principle as much as possible. I think I might also be missing some built in symfony functionality that might help with this but whenever I search I usually find examples that are for symfony 1.2, I'm using 1.4.
Some examples would be great, thanks.
lib/soap_payment.class.php
class SoapPayment
{
public function charge()
{
/*assume options are setup correctly for sake of example*/
try
{
$this->call();
}
catch (SoapPaymentClientFaultException $e)
{
/* should this be caught here? */
}
}
private function call()
{
$this->client->call($this->options);
if ($this->client->hasFault())
{
throw new SoapPaymentClientFaultException();
}
}
}
apps/frontend/payment/actions/actions.class.php
class paymentActions extends sfActions
{
public function executeCreate(sfWebRequest $request)
{
/* check form is valid etc */
$soap_payment = new SoapPayment();
try
{
$soap_payment->charge();
}
catch (SoapPaymentClientFaultException $e)
{
/* or throw/catch here? */
$this->getUser()->setFlash('error', ...);
$this->getLogger()->err(...);
}
/* save form regardless, will set a flag to check if successful or not in try/catch block */
}
}
One not very well known feature of Symfony is that exceptions can manage the content sent in a response. So you could do something like this:
class SoapException extends sfException
{
public function printStackTrace() //called by sfFrontWebController when an sfException is thrown
{
$response = sfContext::getInstance()->getResponse();
if (null === $response)
{
$response = new sfWebResponse(sfContext::getInstance()->getEventDispatcher());
sfContext::getInstance()->setResponse($response);
}
$response->setStatusCode(5xx);
$response->setContent('oh noes'); //probably you want a whole template here that prints the message that was a part of the SoapException
}
}
If you need a cleaner handling of SOAP exceptions, like setting flashes, etc. you'll probably have to catch each exception. One idea here might be to create a generic SoapException class that is extended by more specific SoapExceptions so you don't have to catch a bunch of different types. The above code may be a useful fallback mechanism as well.
Finally, yes, you should place custom exceptions in lib/exception.