When throwing exceptions in the following auth class example is it recommended to throw a different exception for each situation to be handled, eg:
addUser(...) {
// normal database code here...
switch(TRUE) {
case ($username_exists):
throw new UserExists('Cannot create new account. Account username ' . $un . ' already exists.');
case ($email_exists):
throw new EmailExists('Cannot create new account. Account email ' . $email . ' already exists.');
}
}
//to be called externally by...
try {
$auth->adduser(...);
} catch (UserExists) {
$output = 'That username is already taken.';
} catch (EmailExists) {
$output = 'That email is already being used.';
} catch (AuthException $e) {
$output = $e->getMessage();
}
echo $output;
}
or is it recommended to throw a general "type" of exception with a unique exception code? eg...
addUser(...) {
// normal database code here...
switch(TRUE) {
case ($username_exists):
throw new AuthException('Cannot create new account. Account username ' . $un . ' already exists.', 10);
case ($email_exists):
throw new AuthException('Cannot create new account. Account email ' . $email . ' already exists.', 20);
}
}
//to be called externally by...
try {
$auth->adduser(...);
} catch (AuthException $e) {
switch($e->getCode()) {
case 10:
$output = 'That username is already taken.';
break;
case 20:
$output = 'That email is already being used.';
break;
default:
$output = $e->getMessage();
}
echo $output;
}
I ask because I'm new to exceptions and both solutions seem equally viable. Perhaps there are other solutions entirely?
Interesting side-note: I didn't realize I was asking a "what's your preference" question until after receiving a few answers. I anticipated a single recommended method.
I wouldn't say one way is right and one way is wrong - and asking a "is this way recommended versus this way" can easily stir up a large pot if the question hits the right nerve in a large group.
Regarding your question specifically, both are valid and acceptable ways to throw different types of exceptions - and I've seen both quite frequently.
In large-scale applications in just about every language, however, I see the bottom method more often than not - and it's also my own personal style/preference. I think that throwing a single "type" of exception, AuthException, and specifying an exception-code is very clear and concise.
If you want it to be more descriptive (from a programming standpoint), you can use an pseudo-enum* setup for the codes to give a user-friendly description:
class AuthExceptionCode {
const USER_EXISTS = 0;
const EMAIL_EXISTS= 1;
// ....
}
To throw the exception with the code:
throw new AuthException('error message', AuthExceptionCode::USER_EXISTS);
If you have an actual custom-Exception class, i.e. - you extend Exception, you can place the codes direction in the class itself:
class AuthException extends Exception {
const MISC = -1;
const USER_EXISTS = 0;
const EMAIL_EXISTS= 1;
public function __construct($message, $code = self::MISC) {
switch ($code) {
case self::USER_EXISTS:
// example on how to access the codes from within the class
// ...
* Enumerations are not native to PHP, so using const is the easiest method (without using a 3rd-party class/plugin.
In general, I think the best practice is that an exception should encompass a single exceptional condition. Take the SPL exceptions, for example. Would you throw InvalidUserArgumentException and InvalidEmailArgumentException? Nope. You'd throw the SPL InvalidArgumentException and just change the exception message based on the details (e.g. "Invalid user" or "Invalid email"). That said, IMHO, you should use a single AuthException and just vary the message (like you did in your 2nd example), only instead of using codes and a switch, just output the exception message directly:
try {
$auth->adduser(...);
} catch (AuthException $e) {
$output = $e->getMessage();
}
echo $output;
I prefer the top way where you have a separate catch statement for each exception that can occur, but it depends.. If you find yourself have really long exception class names where it's difficult to understand their meaning, then I would group them up a bit.
Related
I don't understand how to properly create and return useful error messages with PHP to the web.
I have a class
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
elseif(itFooedUp)
return ERR_IT_FAILED;
elseif(itFooedOut)
return ERR_IT_TIMED_OUT;
}
}
And another class that uses this class to do something useful, then return the result to the user. I am just wondering where I put the string value for all my error messages.
class Bar {
public function doFooeyThings(stuff){
$res = $myFoo->fooItUp();
// now i need to tell the user what happened, but they don't understand error codes
if($res === Foo::OK_IT_WORKED)
return 'string result here? seems wrong';
elseif ($res === Foo::ERR_IT_FAILED)
return Foo::ERR_IT_FAILED_STRING; // seems redundant?
elseif($res === Foo:ERR_IT_TIMED_OUT)
return $res; // return number and have an "enum" in the client (js) ?
}
}
You should avoid returning error states whenever possible. Use exceptions instead. If you've never used exceptions before you can read about them here
There multiple ways you can utilize exceptions in your example. You could create custom exceptions for every error or for every category of error. More on custom exceptions here or you could create an instance of the default Exception class supplying it the error messages as strings.
The code below follows the second approach:
class Foo {
const OK_IT_WORKED = 0;
const ERR_IT_FAILED = 1;
const ERR_IT_TIMED_OUT = 3;
public function fooItUp(){
if(itFooed)
return OK_IT_WORKED;
else if(itFooedUp)
throw new Exception("It failed")
else if(itFooedOut)
throw new Exception("Request timed out");
}
}
I'm sure you can think of some more elegant messages than the ones I used. Anyway, you can then go ahead and handle those exceptions on the caller method using try/catch blocks:
class Bar {
public function doFooeyThings(stuff){
try
{
$res = myFoo->fooItUp();
}
catch(Exception $e)
{
//do something with the error message
}
}
}
Whatever exception is thrown from fooItUp will be "caught" by the catch block and handled by your code.
Two things you should also consider are:
It's best not to show your users detailed information about errors because those information could be used by users with malicious intent
Ideally you should have some kind of global exception handling
One solution is to use exceptions in conjunction with set_exception_handler().
<?php
set_exception_handler(function($e) {
echo "Error encountered: {$e->getMessage()}";
});
class ErrorMessageTest
{
public function isOk()
{
echo "This works okay. ";
}
public function isNotOkay()
{
echo "This will not work. ";
throw new RuntimeException("Violets are red, roses are blue!! Wha!?!?");
}
}
$test = new ErrorMessageTest();
$test->isOk();
$test->isNotOkay();
The set_exception_handler() method takes a callable that will accept an exception as its parameter. This let's you provide your own logic for a thrown exception in the event it isn't caught in a try/catch.
Live Demo
See also: set_exception_handler() documentation
Is it possible to display all catches instead of only one? In the example below, what if both the username and email are wrong, can I output both instead of just one.
try {
// Code here
}
catch(WrongUsername $e) {
echo 'Your username is wrong.'
}
catch(WrongEmail $e) {
echo 'Your email is wrong too.'
}
You can probably loop thru all the validation rules and cathc all errors.
$errors = [];
foreach($rules as $rule){
try{
validate($input, $rule);
}catch(Exception $e){
$errors[] = $e->getMessage();
}
}
But then I don't see any reason to use try-catch at all.
This doesn't really make a lot of sense within PHP - it's dynamically, not fixed typed, and does not support overloading. In your example is 'WrongEmail' the class of the exception or the exception message? Is there a benefit to sub-classing exceptions?
If it's the message property of the exception, then change it to use something more informative...
try {
...
if (!preg_match($pattern, $username))
throw new Exception('Your username is wrong.');
...
} catch ($e) {
echo $e->message;
}
If you have a good reason for sub-classing your exceptions...
try {
...
} catch ($e) {
switch (get_class($e)) {
case 'WrongUsername':
echo 'Your username is wrong.';
break;
case 'WrongEmail':
echo 'Your email is wrong.'
break;
...
}
}
But exceptions are part of a flow control structure - when you throw an exception you change the flow of execution. A try{} block will never raise more than one exception. In terms of programming style it's debatable whether you should actually use exceptions at all for this kind of operation.
If you want to check a set of pre-conditions and deal with each failed requirement then don't use this construct.
I have a class Person.
I want to add error handling into my script, so that say, the user enters an incorrect email address the script will tell them. Usually not a problem at all, but now I am using OO classes I am in unfamiliar territory.
So. I guess I want to know how to handle multiple exceptions. Or do I need to try each line of code one at a time and catch each line? This seems slightly excessive. Ideally I'd like to do the following:
try {
$people[$new]->set_fullname($_POST['name']);
$people[$new]->set_active(true);
$people[$new]->set_add1(rEsc($_POST['add1']));
$people[$new]->set_add2(rEsc($_POST['add2']));
$people[$new]->set_add3(rEsc($_POST['add3']));
$people[$new]->set_add4(rEsc($_POST['add4']));
$people[$new]->set_postcode(rEsc($_POST['postcode']));
$people[$new]->set_phone(rEsc($_POST['phone']));
$people[$new]->set_email(rEsc($_POST['email']));
} catch {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
But in my error handling, How can I catch multiple errors? I'd like to push all the error messages into an array and display them each nicely in the webpage. As far as I can see on php.net it seems that I can only catch one error message at a time.
Do I really have to try {} catch {} each line of code?
Imho this shouldn't throw exceptions in the first place. Simply loop through the fields and add the possible errors to some $errors array.
Users screwing up fields is not an exceptional case. I don't even think the user object should be able to validate an emailaddress. That seems to be like a responsibility of the Form.
Also am I wondering what that rEsc function is you are using. Not only are you using a global function which makes it virtually impossible to swap it out for some other function in the future (tight coupling), but also the name is chosen badly. Also do I fail to see why you would want to escape stuff in that place (I guess that is what the thing does). Only escape / sanitize data when you are using it. And I'm wondering for what you are escaping your data, because if it is for database input there are far better ways.
try {
$people[$new]->set_fullname($_POST['name']);
$people[$new]->set_active(true);
$people[$new]->set_add1(rEsc($_POST['add1']));
$people[$new]->set_add2(rEsc($_POST['add2']));
$people[$new]->set_add3(rEsc($_POST['add3']));
$people[$new]->set_add4(rEsc($_POST['add4']));
$people[$new]->set_postcode(rEsc($_POST['postcode']));
$people[$new]->set_phone(rEsc($_POST['phone']));
$people[$new]->set_email(rEsc($_POST['email']));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} catch (EmailFormatException $em) {
echo 'Caught exception: '. $e->getMessage();
}
Just continue it like that
Here's how I would design this:
Create a validate() method on the Person class that verifies every property and returns an array of strings that explain the errors to the user. If there are no errors, have the method return null.
Do not use exceptions at all. They are slow; they complicate code maintenance (and you're seeing the symptoms in the approach you've taken so far)
Remove the custom methods for setting properties of the Person object. PHP is not Java. Set the properties directly.
Putting this all together:
class Person {
public $name;
public $address1;
public $address2;
public function validate() { }
}
And then your code:
$obj = new Person();
$obj->name = "Bob";
$obj->address1 = "1 Elm St.";
$validationResult = $obj->validate();
if ( $validationResult != null) { // there were errors
print_r($validationResult);
}
You can make a foreach statement that sets the data that needs validation with try/catch inside the loop in order to populate an array with the errors, like that:
$errors = [];
foreach (['field1', 'field2', ...] as $field) {
try {
$method = "set_{$field}";
$people[$new]->$method(rEsc($_POST[$field]));
} catch (Exception $e) {
$errors[] = $e->getMessage();
}
}
I am trying to understand what the best approach would be to handle Exceptions in the following scenario:
I have a class employee:
class employee extends person {
private $salary;
private $baseSalary = 6.5;
function __construct($f, $m, $l, $a,$fsalary=0){
if(!is_numeric($fsalary)){
throw new Exception("Age supplied is not a number", 114);
}
parent::__construct($f, $m, $l, $a);
$this->salary=$fsalary;
}
function GetDetails(){
return parent::GetName().
"<br/>".
$this->salary;
}
function __toString(){
return $this->GetDetails();
}
}
And using this:
try{
if(!$f = new employee("Sarah", "Sebastian", "Pira", "abc")){
throw new Exception();
}
else {
echo $f;
}
}
catch (Exception $e){
echo "<br/>";
echo var_dump($e);
}
Now I would think it would be a good idea to throw an exception in the class and then use just one catch block in all the scripts that would be using an employee object - But this doesn't seem to work - I need to have a try catch block within the class - Is this the correct way of looking at this?
Thanks
I think what you're saying is that you want to do something like this:
try {
class Employee extends Person {
// ...blah blah...
}
}
catch(Exception $e) {
// handle exception
}
...and then be able to insantiate it in other classes, without explicitly catching any exceptions:
// try { << this would be removed
$employee = new Employee();
// }
// catch(Exception $e) {
// (a whole bunch of code to handle the exception here)
// }
You can't do that, because then the try/catch block in the class will only catch any exceptions that occur when defining the class. They won't be caught when you try to instantiate it because your new Employee line is outside the try/catch block.
So really, your problem is that you want to be able to re-use a try/catch block in multiple places without re-writing the code. In that case, your best solution is to move the contents of the catch block out to a separate function that you can call as necessary. Define the function in the Employee class file and call it like this:
try {
$employee = new Employee();
$employee->doSomeStuff();
$employee->doMoreStuffThatCouldThrowExceptions();
}
catch(Exception $e) {
handle_employee_exception($e);
}
It doesn't get rid of the try/catch block in every file, but it does mean that you don't have to duplicate the implementation of the exception-handling all the time. And don't define handle_employee_exception as an instance method of the class, do it as a separate function, otherwise it will cause a fatal error if the exception is thrown in the constructor because the variable won't exist.
You should read more about Exceptions in PHP.
You can handle exceptions within the methods of the class, sure. But you should rethink how you want to do this and... why.
Good practice is also creating own exception class, so you are able to distinguish exceptions thrown by your module / class from the exceptions thrown by something else. It looks like that (see more):
class EmployeeModule_Exception extends Exception {}
and when it comes to throwing exception:
// the second parameter below is error code
throw new EmployeeModule_Exception('some message', 123);
Catching is similar, only the below example will catch only your module's exceptions:
try {
// some code here
} catch (EmployeeModule_Exception $e) {
// display information about exception caught
echo 'Error message: ' . $e->getMessage() . '<br />';
echo 'Error code: ' . $e->getCode();
}
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.