Since a short period of time I'm working with Try Catch in PHP. Now, every time a new error is thrown you get a fatal error on the screen, this isn't really user friendly so I was wondering if there's a way to give the user a nice message like an echo instead of a fatal error.
This is the code I have now:
public static function forceNumber($int){
if(is_numeric($int)){
return $int;
} else {
throw new TypeEnforcerException($int.' must be a number');
}
}
public function setStatus($status) {
try {
$this->status = TypeEnforcer::forceInt($status);
} catch (TypeEnforcerException $e) {
throw new Exception($e->getMessage());
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
This is best solved with a frontend controller that is able to catch all uncatched exceptions:
<?php
require('bootstrap.php');
try {
$controllerService->execute($request);
} catch (Exception $e) {
$controllerService->handleControllerException($e);
}
You can then write code to return the internal server error because an exception signals an exceptional case so it normally is an 500 internal server error. The user must not be interested what went wrong other than it just didn't work out and your program crashed.
If you throw exceptions to give validation notices you need to catch those in a different layer (and you're probably doing it wrong if you use exceptions for that).
Edit: For low-level functions, because PHP is loosely typed, if a function expects and int, cast to intDocs:
public static function forceNumber($int){
$int = (int) $int;
return $int;
}
this will actually force the integer. In case the cast is not possible to do (e.g. $int it totally incompatible) PHP will throw the exception for you.
The example is a bit akward because by the method's name you use it to validate some number and provide an error if not (here wrongly with an exception). Instead you should do some validation. If you expect wrong input, it's not an exceptional case when wrong input is provided, so I would not use exceptions for that.
Related
Is it possible to catch an exception in an generator and just yield the next value? I tried something similar like the code in the example below, but it stops on an exception and does not yield the next value as "expected".
function generator(){
foreach($aLotOfWork as $task){
try {
$promise = doSomethingThatCanFailBadly($task);
yield $promise;
} catch (Exception $e) {
echo "oh.. there is an error, but I don't care and continue";
}
}
}
IMHO This is not a duplicate of (php: catch exception and continue execution, is it possible?), because this person just wanted to know how to catch an exception in php and go on. In my case I already catch all exceptions, but the generator stops and does not go on as intended.
Your code is right and it will capture the exception and continue, check this out:
$ cat so.php
<?php
function doSomethingThatCanFailBadly($task) {
if ($task == 3) {
throw new Exception();
}
return $task;
}
function generator(){
$aLotOfWork = array(1,2,3,4,5);
foreach($aLotOfWork as $task){
try {
$promise = doSomethingThatCanFailBadly($task);
yield $promise;
} catch (Exception $e) {
echo "oh.. there is an error, but I don't care and continue\n";
}
}
}
foreach (generator() as $number) {
echo "$number\n";
}
?>
$ php so.php
1
2
oh.. there is an error, but I don't care and continue
4
5
Have a look at your error stack trace. Maybe what's happening is that something inside your doSomethingThatCanFailBadly method is producing an exception but it is also catching it and forcing the quit with die() or exit() before it ever gets to your catch block. There's not much you can do in that case. You could maybe use register_shutdown_function and see if that helps, but that is starting too look messy.
You would need to save to output of the generator to an array, and if during individual yields an exception is thrown, with right exception handling, it simply will not get into the array.
In your example the doSomethingThatCanFailBadly throws an exception, then the program flow gets to the catch branch. So actually doSomethingThatCanFailBadly has no return value that could be assigned to $promise - so there's nothing to yield at that point.
#palako explained the problem very nicely, but he does not really provide a solution for my case. I use this generator to generate Promises and the consumer (Guzzle's EachPromise-method) seems to stop working on an Exception. So I replaced the throw new SomeException('error message') statement by return RejectedPromise('error message').
I have to admit that this is a very situation-specific solution, but you can do something similar in other settings too: just return an object instead of using exceptions.
When should I use Exception, InvalidArgumentException or UnexpectedValueException?
I don't know the real different between them as I always used Exception.
Different exceptions just give you more granularity and control over how you catch and handle exceptions.
Consider a class where you are doing many things - e.g. getting input data, validating input data and then saving it somewhere. You might decide that if the wrong or empty arguments are passed to the get() method, you might throw an InvalidArgumentException. When validating, if something is out of the ordinary or doesn't match up you could throw an UnexpectedValueException. If something totally unexpected happens you could throw a standard Exception.
This becomes useful when you are catching, as you can handle different types of exceptions in different ways. For example:
class Example
{
public function get($requiredVar = '')
{
if (empty($requiredVar)) {
throw new InvalidArgumentException('Required var is empty.');
}
$this->validate($requiredVar);
return $this->process($requiredVar);
}
public function validate($var = '')
{
if (strlen($var) !== 12) {
throw new UnexpectedValueException('Var should be 12 characters long.');
}
return true;
}
public function process($var)
{
// ... do something. Assuming it fails, an Exception is thrown
throw new Exception('Something unexpected happened');
}
}
In the above example class, when calling it you could catch multiple types of exceptions like so:
try {
$example = new Example;
$example->get('hello world');
} catch (InvalidArgumentException $e) {
var_dump('You forgot to pass a parameter! Exception: ' . $e->getMessage());
} catch (UnexpectedValueException $e) {
var_dump('The value you passed didn\'t match the schema... Exception: ' . $e->getMessage());
} catch (Exception $e) {
var_dump('Something went wrong... Message: ' . $e->getMessage());
}
In this case you get an UnexpectedValueException like this: string(92) "The value you passed didn't match the schema... Exception: Var should be 12 characters long.".
It should also be noted that these exception classes all end up extending from Exception anyway, so if you don't define special handlers for the InvalidArgumentException or others then they will be caught by Exception catchers anyway. So really, why not use them?
I'm trying to query a marketplace for an account that matches an e-mail address, and when it can't find a result, it's raising an uncaught exception despite my try/catch block.
try {
$vendor = $this->marketplace ->accounts ->query()
->filter(Balanced\Account::$f->email_address->eq($this->vendor['email']))
->one();
$this->balanced_vendor = $vendor;
return true;
} catch (Balanced\Exceptions\HTTPError $e) {
$this->notify('no-vendor', $e);
}
What might i be doing wrong ?
Thanks !
Looks like the Balanced\Core\Query class throws both Balanced\Exceptions\MultipleResultsFound and Balanced\Exceptions\NoResultFound from its one() method, not Balanced\Exceptions\HTTPError.
To fix your immediate problem, you should change your catch directive to:
} catch (Balanced\Exceptions\MultipleResultsFound $e) {
// handle multiple results..
} catch (Balanced\Exceptions\NoResultsFound $e) {
$this->notify('no-vendor', $e);
}
From the looks of it though, you attempted to use the Balanced\Exceptions\HTTPError as a catch all, which can be considered a lacking feature of the client. What I've done, is I've filed a Github issue for you that suggest all exceptions inherit from a base Balanced exception.
I hope this helps.
if ($disponivel === 0)
{
$razao = $check->cd->reason;
$mensagem = "the domain isn't available. Reason: ".$razao;
}
elseif($disponivel === 1)
{
$mensagem = "the domain doesn't exist - free to register.";
}
return $mensagem;
}
else
{
throw new EppCommandsExceptions('Domain isn't supported - '.$result->msg, $codigo);
}
Do you see those $mensagem strings? They are also error messages and my question is, instead of having $mensagem displaying some error messages, can we use several throw exceptions instead?
Update:
I DO NOT mean to throw the exceptions all at once. Each exception at his time.
Thanks in advance,
MEM
You can't throw multiple, but since PHP 5.3 you can supply previous to Exception's constructor to create a linked list of exceptions.
For example, here's a 3-item chain:
$a = new Exception('Exception a');
$b = new Exception('Exception b', 0, $a);
throw new Exception('Exception c', 0, $b);
Then, in your exception handler, you can traverse through the chain with getPrevious
do {
printf("%s:%d %s (%d) [%s]\n", $e->getFile(), $e->getLine(), $e->getMessage(), $e->getCode(), get_class($e));
} while($e = $e->getPrevious());
You mean something like
else {
throw new XException(...);
throw new YException(...);
throw new ZException(...);
}
... and all of them are thrown "at once"?
No, thats not possible and wouldn´t make too much sense imho. How should the client code catching these exceptions look?
Furthermore you shouldn´t use exceptions as a replacement for normal flow control structures, exceptions should only handle, well, exceptional errors, like not being able to connect to a database etc.
You could implement another custom exception class which takes an array of errors as an argument, and the client code can do things like:
catch(SomeException $e) {
$messages = $e->getErrorMessages();
}
As I don´t speak the language your language, I cant really tell what you are trying to do in that code bit you posted, otherwise I could suggest something more specific.
EDIT/UPDATE:
#MEM thanks for updating your code with english error messages. Are you implementing something like a domain registration service?
Of course, this is a bit of a difficult topic as everbody has his preferences, but I wouldn´t throw an exception if e. g. the user tried to register a domain thats already been used by someone else. This is not an exceptional state, this is to be excepted. I would make a validation class/method which collects these error messages which in turn get displayed to the user.
When would I throw an exception in an app like yours? I don´t know much about domain registration, but if you are fetching the info whether a domain is free or not from a remote server/webservice and this webservice is down, then I would throw an exception. It gets caught in the Controller (I image a MVC web app) which in turn replies to the client with a "Server down, please try again later" message.
you can throw and catch different exceptions.
If all you want to do is print the error, you can throw same type of exceptions aswell. But I'm going to assume you need different approach to each case
function myFunction() {
if($a) {
throw new AException('A Error');
} else if($b) {
throw new BException('B Error');
} else if($c) {
throw new CExceptıon('C Error');
}
}
try {
myFunctıon();
} catch (AException $aException) {
echo $aException->getMessage();
} catch (BException $bException) {
echo "this is a terrible case, don't do that again pls";
$mydbobject->rollback();
} catch (CException $cException) {
mailDevTeam("the server made a boo boo");
}
Of course, just subclass the base exception for every message.
Which would you recommend?
Return an error code, such as E_USER_ERROR from a function, and determine proper message higher up:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//This?
return E_USER_ERROR;
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
Execute trigger_error() from the function, with a specific error message:
function currentScriptFilename()
{
if(!isset($_SERVER['SCRIPT_FILENAME']))
{
//Or this?
trigger_error('$_SERVER[\'SCRIPT_FILENAME\'] is not set.', E_USER_ERROR);
}
else
{
$url = $_SERVER['SCRIPT_FILENAME'];
$exploded = explode('/', $url);
return end($exploded);
}
}
I am not sure if I will regret having put a bunch of error messages in my functions further down the line, since I would like to use them for other projects.
Or, would you recommend something totally different?
Do not mix the matters.
Error notification and error handling are different tasks.
You have to use both methods simultaneously.
If you think that $_SERVER['SCRIPT_FILENAME'] availability is worth an error message, you can use trigger error. However PHP itself will throw a notice if you won't check it.
If you want to handle this error, just check this function's return value.
But I would not create a special function for this task.
So,
if (!$filename = basename($_SERVER['SCRIPT_FILENAME']) {
// do whatever you want to handle this error.
}
would be enough
Exceptions could be useful to handle errors, to know if we had any errors occurred.
A simple example:
try {
$filename = basename($_SERVER['SCRIPT_FILENAME'])
if (!$filename) throw new Exception("no filename");
$data = get_some_data_from_db() or throw new Exception("no data");
$template = new Template();
//Exception could be thrown inside of Template class as well.
}
catch (Exception $e) {
//if we had any errors
show_error_page();
}
$template->show();
3.Use exceptions.
If this is the route you are going, I'd rather recommend throwing Exceptions rather then returing an E_ERROR (E_USER_ERROR should be used), as this is just an integer, and possibly a totally valid return for your function.
Advantages:
- Throwing of an Exception cannot be interpreted as anything else then an error by mistake.
- You keep the possibility to add a descriptive error message, even though you don't handle the error at that point/
- You keep a backtrace in your Exception.
- You can catch specific exceptions at specific points, making the decision where in your project a specific type of error should be handled a lot easier.
If not using exceptions which you should be, use trigger_error().
If it is an error you'd like to deal with, try returning FALSE like a lot of the in built functions do.
If you do use exceptions, catch them like this
try {
whatever()
} catch (Exception $e) {
// handle however
}