PHP: Should a checking function throw an exception - php

This doesn't necessarily only apply to PHP, but that's my area of concern.
I have been writing a few checking functions recently, that get some argument and then check its validity in various ways. Like, checkXmlString($xml) will check whether the given string contains a well-formed xml document, etc.
The question is, should those functions return a boolean, or throw exceptions and not return anything on success.
So
function checkAbc($arg) { if ($arg is invalid) return false; else return true; }
or rather
function checkAbc($arg) { if ($arg is invalid) throw new Exception(...); }

You could throw a InvalidArgumentException to check if arguments are incorrect but i think for your case if you are writing "checkers" they should return a boolean so you know not to continue operations, for example if the foobar.xml is actually a CSV file you wouldn't want to continue with your operation but you wouldn't want to catch an exeption either
<?php
class Checker {
function validXml($string)
{
if(!(bool)$string) throw new \InvalidArgumentException("Cannot pass empty string as argument", 1);
// Check
// Is valid XML ? Return True : return false
}
}
try {
if(new Checker->validXml($xmlString))
{
// Continue Operation
// return
}
// Notify User of invalidity
// return
} catch (\InvalidArgumentException $e) {
// Log args
//
}

According to almost all books written on subject and not least, according to logic, the name should be the biggest hint of what the function does. In that case, only the first option applies. As Bet Lamed said previously, a function called "check" is not supposed to throw exceptions, just to let you know if the check is ok or not.
If you want exceptions, you might want to rename it to DeserialisationToAbc() or TryParseAbc() or something similar.

The question is, should those functions return a boolean, or throw
exceptions and not return anything on success.
First identify If It is an exceptional situation than use exception .You case does not seems to be exceptional, It is just a condition , so treat it like a condition.If It was like It is correct but fails on certain situation, you may not identify well than you may consider using exception.
Visit these two links and learn more about exception
Exception Best Practices in PHP 5.3
A primer on PHP exceptions

This is of course a bit opinion based, but ask yourself, what do you expect from a function like checkEmail() ? The purpose of the method is to validate something, so you propably expect an answer to this question.
$isValid = checkEmail($arg);
I think most developers expect a bool as return value, it makes the code readable. Wrong values are expected, so one cannot say it is an exception if an invalid argument is passed. To return an error message as well i would use an out parameter:
function checkAbc($arg, &$errorMessage)
{
if ($arg is invalid)
{
$errorMessage = 'The argument is invalid because of...';
return false;
}
else
{
$errorMessage = '';
return true;
}
}

I'm really not quite sure which form is preferrable in general.
On the one hand, in case of an error, you want a useful message, so you end up with mixed returns (true/string), which is ugly, or returning an array (even uglier). An exception gives you that for free.
On the other hand, a function called check...() should not be expected to throw an exception, because finding out that "this is not a valid thingie" is nothing exceptional and not an error.
The third way would be to call it "throwIfFalse", but that's ugly too....
Hmm....
One possible solution:
interface Checker {
public function check();
public function getMessage();
}
class WhateverChecker implements Checker { ... }
class ClientOfChecker {
public function doStuff() {
$checker = new Checker();
if (! $checker->check() )
throw new Exception($checker->getMessage());
}
}
However, that seems incredibly verbose, and, may I say, Javaesque to me.

There is generally 2 types of function regarding your question:
Pure function that tests whether a condition holds.
Function that ensures a condition holds and alters control flow if it does not.
Different programming language may have different conventions about the naming of both types. Take C++ as an example, one common naming is CHECK_XXX for type 2 and IsXXX for type 1.
Here is an example taken from a tutorial of the google-log library:
CHECK(fp->Write(x) == 4) << "Write failed!";
CHECK_NE(1, 2) << ": The world must be ending!";
Another example is the maktaba utility library for Vimscript , where maktaba#value#IsXXX() is used to test whether the argument is of a certain type while maktaba#ensure#IsXXX() is used to ensure IsXXX holds and throws an exception otherwise.
function! TakeAString(name)
" Ensure argument type is String.
let name = maktaba#ensure#IsString(a:name)
endfunction
if maktaba#value#IsString(name)
" Branch if name is a String.
echo name
endif
So here is the point: choose the one that suits your need best and name the function according to the convention of the language. In terms of use cases of both, roughly, use the type 2 to check pre-condition like argument type and use the type 1 in conditional statements.

Related

Using a return keyword with no return value?

I have been noticing in some PHP design patterns, some authors who write code examples, have return inside the method but it doesn't specify return value .
It just says "return"
Can some one please explain me what is the purpose of doing that? Below is an example
Thank you!
function addListItem(ListItem $listItem){
if(in_array($listItem, $this->listitems, true)){
return;
}
$this->listitems[] = $listItem;
}
That's done for side-effects (IO, altering globals, or the arguments passed by reference, or an object property, like in your example -- $this->listitems[] = $listItem;), or to indicate it's impossible to yield a valid result.
return;
is equivalent to
return null;
The return statement will stop the function immediately after it has been called. Because we do not want any value to be returned like integers, string or booleans, we just stop it so the code will not continue.
This can also be compared to break in a for or while loop.
Well for starters, you may hit an if statement that makes the rest of the code in the method unnecessary.
For example:
if(user->log_in == "")
{
show_error_msg();
return;
}
The "technically more correct" way of writing such code is:
if( !in_array($listItem, $this->listitems, true)) {
$this->listitems[] = $listItem;
}
However, sometimes you may have more complex testing, and proceeding in this manner may result in a single if block spanning several lines with many &&s, or nested if statements that fall off the edge of your screen.
Therefore, it is common to see the pattern of your code, namely "check failure conditions and exit the current block if necessary". This means you can have as few or as many conditions as you want, each with their own failure conditions, and if the code manages to reach the end of it then you're good to go.
In some cases, error handling is useful. Something like:
try {
if( failure condition 1) throw new Exception("Error message 1");
if( failure condition 2) throw new Exception("Error message 2");
// ...
do something here;
}
catch(Exception $e) {
// report error here
}

Is it bad practice to add a flag to throw an exception?

I have some methods in a class that I think could be useful for testing stuff, but also in some cases the program may want to halt completely if the check fails. Originally I was wrapping the method calls in if conditions and then throwing an exception, however, I ended up having the same if conditions in many methods and it seemed wasteful, so I added a boolean flag to the check method to have it throw an exception if the check failed. An example:
public function isValidDirection($direction, $throwException = false) {
if(!in_array($direction, $this->getDirections()) && $throwException) {
throw new \InvalidArgumentException(sprintf('Invalid direction value. Valid directions are: "%s"', implode(", ", $this->getDirections())));
}
return in_array($direction, $this->getDirections());
}
Is this a bad idea? I've not come across this kind of pattern before and I'm wondering are there any pitfalls to it?
An exception should be thrown in exceptional circumstances. That means when your code is in a situation that it is not prepared to handle. If your function is a validation function whose job it is to confirm the validity of data, there should hardly be any exceptional error possible. The job of the function is simple: take input, return true or false depending on whether it's valid. If you want to handle the case of invalid input by throwing an exception and you want to DRY that repetitive check, create another function which wraps your validation function. So you'd have two functions: isValidDirection($input) and assertIsValidDirection($input), the latter of which throws an exception if it's not valid and otherwise does nothing.
function isValidDirection($input) {
return ...; // true or false
}
function assertIsValidDirection($input) {
if (!isValidDirection($input)) {
throw new InvalidArgumentException;
}
}
This keeps both functions' responsibilities clear and their implementation simple.
Yeah, it's a bad idea. If the method gets into a state where it can't continue or otherwise shouldn't get into it should throw an exception. It's up to the calling code to handle the exception as it feels is necessary or let the program crash. Otherwise the caller is oblivious to the program failing and will continue and usually make the situation worse/harder to debug.
If, in the case that you don't want to halt your program you can catch the exception and log an error, or handle it some more graceful way depending on your situation. If you don't expect the error at all the program will crash (ideally during testing) and the error won't propagate; you then know exactly where to look to fix it.
So, if I understand it correctly, your code should throw an exception if the $direction isn't in getDirections.
e.g:
public function isValidDirection($direction) {
if(!in_array($direction, $this->getDirections())) {
throw new \InvalidArgumentException(sprintf('Invalid direction value. Valid directions are: "%s"', implode(", ", $this->getDirections())));
}
return in_array($direction, $this->getDirections());
}
(Also, you don't need to call getDirections 3 times, just call it once and store it in a variable)

Proper way to error handle

This is more of a architectural question. I like to know what people's opinion on how to properly handle errors in a fully Object Oriented PHP environment.
For example I have a class that retrieves all user data from the database. So I would use it in this way:
$userData = new UserDataConnection();
$userData->openDatabase();
$userData->retrieveData();
$userData->showData();
$userData->closeDatabase();
At each of these steps, an error could have potentially occurred. So would I return a boolean from each step stating whether the function has successfully executed (hence the error checking is within each function), or do I do a try-catch exception handling around the whole thing? or is there a better way?
Is it also better to always go to some kind of error page when some kind of error occur?
<?php
$ERROR = false;
try {
$userData = new UserDataConnection();
$userData->openDatabase();
$userData->retrieveData();
$DETAILS = $userData->showData();
$userData->closeDatabase();
} catch( Exception $e ) {
$ERROR = $e->getMessage(); // exit;
}
if( $ERROR ) {
$DETAILS = array();
}
?>
If you are using PDO you can throw a PDOException.
It typically boils down to a simple question:
May this function "legitimately" fail or is any failure a sign of something being really wrong?
If a function expects a certain kind of input and should never ever be called with a different kind of input, any failure to supply the correct input is an InvalidArgumentException. This is especially true for functions which do not directly handle user input. This works even better with type hinting.
If a function should always produce a certain result given the correct input, any failure of the function to produce that result is a RuntimeException or LogicException or some other kind of exception.
If a function may or may not produce some kind of result, false is a legitimate return value. Functions which deal with user input and therefore get very arbitrary input values are often fine with returning false.
Some examples:
getDatabaseConnection() is perfectly correct in throwing a RuntimeException or similar if it cannot establish a connection to the database. This is an exceptional circumstance in which case no work can continue.
transformFooIntoBar(Foo $foo) is correct in throwing some form of exception if it cannot return a Bar instance, for whatever reason that may be. The function has a clear purpose and a type checked input value. If it cannot do its job under such clear conditions, something is clearly wrong and must be fixed. Ergo, an exception.
checkIfUserExists($id) may well return false, because its job is to return a thumbs up or thumbs down. A user not existing is not an exceptional circumstance given this job description.
Where to catch a thrown exception depends on where you want to deal with it. Database connection errors should probably be caught at the very top of the call stack, since you probably cannot do anything if the database is down.
On the other hand, if some module is calling some submodule and half expects that submodule to fail and/or has a contingency plan in case it does fail, the module may catch the exception of the submodule and continue on doing its job. For instance getRSSUpdates() makes a call to a HTTP::fetchContents($url). If the URL returns a 404, the HTTP module may throw an exception, since it cannot return contents under these circumstances. The getRSSUpdates() function is prepared for this though and handles this case as "no updates at this time".

Should change this and start using exceptions?

I understood the concept of Exceptions, but I still can't undestand why I should replace the way I do error handling to the Exception way.
Here's a simple code that serves as an example of how I do things without exception:
validate_form_data($data, &$errors=array())
{
//expects $_POST to be passed as argument ($data)
$errors = array();
if(strlen($data['name']) < 3)
$errors[] = "Your name must contain at least 3 characters.";
if($data['age'] > 200)
$errors[] = "You can't be older than 200."
if(count($errors))
return false;
else
return true;
}
///submit_form.php
$errors = array();
if(validate_form_data($_POST, $errors))
{
// do something like sending data to MySQL and output sucess message.
}
else
{
//loop thru the $errors array and display its values so the user knows what mistakes he made
}
My question is: Will using exceptions make my life easier, if so, how?
Exceptions, as their name suggests, should be thrown in exceptional cases in your application. But, since what you're doing is validating data, havinng to deal with invalid data isn't an exceptional case: it's a condition you're expecting. So, using exceptions in that script won't make your life easier, and it isn't even correct.
The only thing I would change is the way you return the errors:
function validate_form_data($data)
{
$errors = array();
if (strlen($data['name']) < 3) {
$errors[] = "Your name must contain at least 3 characters.";
}
if ($data['age'] > 200) {
$errors[] = "You can't be older than 200.";
}
return $errors;
}
I disagree that exceptions imply unexpected problems. For one thing, your code is specifically checking for the problem so it's definitely expected. From the definition of exception,
anything excluded from or not in conformance with a general rule, principle, class, etc.
they are known cases that must be excluded but accounted for. They are designed for situations that require leaving the normal flow of execution and make it easier to handle the problem non-locally.
If the method that detects the error isn't going to handle it, i.e. decide what course of action to take, it can throw an exception so that the caller can either handle it or let it propagate to their caller, and so on. You can do this by returning an error code, but that forces every caller in the execution stack to check for and return an error code. PHP automatically rethrows exceptions to the caller if a method doesn't catch them.
Since the purpose of the method above is to check user input for validity, it makes more sense to return the result of that check: yes or no. Also, you're probably going to use that result in the code that calls the method so there's no need to let the exception bubble up the stack.

Multiple return values to indicate success/failure.

I'm kind of interested in getting some feedback about this technique I picked up from somewhere.
I use this when a function can either succeed or fail, but you'd like to get more information about why it failed. A standard way to do this same thing would be with exception handling, but I often find it a bit over the top for this sort of thing, plus PHP4 does not offer this.
Basically the technique involves returning true for success, and something which equates to false for failure. Here's an example to show what I mean:
define ('DUPLICATE_USERNAME', false);
define ('DATABASE_ERROR', 0);
define ('INSUFFICIENT_DETAILS', 0.0);
define ('OK', true);
function createUser($username) {
// create the user and return the appropriate constant from the above
}
The beauty of this is that in your calling code, if you don't care WHY the user creation failed, you can write simple and readable code:
if (createUser('fred')) {
// yay, it worked!
} else {
// aww, it didn't work.
}
If you particularly want to check why it didn't work (for logging, display to the user, or do whatever), use identity comparison with ===
$status = createUser('fred');
if ($status) {
// yay, it worked!
} else if ($status === DUPLICATE_USERNAME) {
// tell the user about it and get them to try again.
} else {
// aww, it didn't work. log it and show a generic error message? whatever.
}
The way I see it, the benefits of this are that it is a normal expectation that a successful execution of a function like that would return true, and failure return false.
The downside is that you can only have 7 "error" return values: false, 0, 0.0, "0", null, "", and (object) null. If you forget to use identity checking you could get your program flow all wrong. Someone else has told me that using constants like an enum where they all equate to false is "ick".
So, to restate the question: how acceptable is a practise like this? Would you recommend a different way to achieve the same thing?
I agree with the others who have stated that this is a little on the WTFy side. If it's clearly documented functionality, then it's less of an issue, but I think it'd be safer to take an alternate route of returning 0 for success and integers for error codes. If you don't like that idea or the idea of a global last error variable, consider redefining your function as:
function createUser($username, &$error)
Then you can use:
if (createUser('fred', $error)) {
echo 'success';
}
else {
echo $error;
}
Inside createUser, just populate $error with any error you encounter and it'll be accessible outside of the function scope due to the reference.
As long as it's documented and contracted, and not too WTFy, then there shouldn't be a problem.
Then again, I would recommend using exceptions for something like this. It makes more sense. If you can use PHP5, then that would be the way to go. Otherwise you don't have much choice.
A more common approach I have seen when exceptions aren't available is to store the error type in a 'last_error' variable somewhere and then when a failure happens (ie it returns false) look up the error.
Another approach is to use the venerable unix tool approach numbered error codes - return 0 for success and any integer (that maps to some error) for the various error conditions.
Most of these suffer in comparison to exceptions when I've seen them used however.
Just to respond to Andrew's comment -
I agree that the last_error should not be a global and perhaps the 'somewhere' in my answer was a little vague - other people have suggested better places already so I won't bother to repeat them
how acceptable is a practice like this?
I'd say it's unacceptable.
Requires the === operator, which is very dangerous. If the user used ==, it leads to a very hard to find bug.
Using "0" and "" to denote false may change in future PHP versions. Plus in a lot of other languages "0" and "" does not evaluate to false which leads to great confusion
Using getLastError() type of global function is probably the best practice in PHP because it ties in well with the language, since PHP is still mostly a procedural langauge. I think another problem with the approach you just gave is that very few other systems work like that. The programmer has to learn this way of error checking which is the source of errors. It's best to make things work like how most people expect.
if ( makeClient() )
{ // happy scenario goes here }
else
{
// error handling all goes inside this block
switch ( getMakeClientError() )
{ case: // .. }
}
Often you will return 0 to indicate success, and 1, 2, 3, etc. to indicate different failures. Your way of doing it is kind of hackish, because you can only have so many errors, and this kind of coding will bite you sooner or later.
I like defining a struct/object that includes a Boolean to indicate success, and an error message or other value indicate what kind of error occurred. You can also include other fields to indicate what kind of action was executed.
This makes logging very easy, since you can then just pass the status-struct into the logger, and it will then insert the appropriate log entry.
Reinventing the wheel here. Using squares.
OK, you don't have exceptions in PHP 4. Welcome in the year 1982, take a look at C.
You can have error codes. Consider negative values, they seem more intuitive, so you would just have to check if (createUser() > 0).
You can have an error log if you want, with error messages (or just arbitrary error codes) pushed onto an array, dealt with elegance afterwards.
But PHP is a loosely typed language for a reason, and throwing error codes that have different types but evaluate to the same "false" is something that shouldn't be done.
What happens when you run out of built-in types?
What happens when you get a new coder and have to explain how this thing works? Say, in 6 months, you won't remember.
Is PHP === operator fast enough to get through it? Is it faster than error codes? or any other method?
Just drop it.
When exceptions aren't available, I'd use the PEAR model and provide isError() functionality in all your classes.
Ick.
In Unix pre-exception this is done with errno. You return 0 for success or -1 for failure, then you have a value you can retrieve with an integer error code to get the actual error. This works in all cases, because you don't have a (realistic) limit to the number of error codes. INT_MAX is certainly more than 7, and you don't have to worry about the type (errno).
I vote against the solution proposed in the question.
It does make sense that a successful execution returns true. Handling generic errors will be much easier:
if (!createUser($username)) {
// the dingo ate my user.
// deal with it.
}
But it doesn't make sense at all to associate meaning with different types of false. False should mean one thing and one thing only, regardless of the type or how the programming language treats it. If you're going to define error status constants anyway, better stick with switch/case
define(DUPLICATE_USERNAME, 4)
define(USERNAME_NOT_ALPHANUM, 8)
switch ($status) {
case DUPLICATE_USERNAME:
// sorry hun, there's someone else
break;
case USERNAME_NOT_ALPHANUM:
break;
default:
// yay, it worked
}
Also with this technique, you'll be able to bitwise AND and OR status messages, so you can return status messages that carry more than one meaning like DUPLICATE_USERNAME & USERNAME_NOT_ALPHANUM and treat it appropriately. This isn't always a good idea, it depends on how you use it.
I like the way COM can handle both exception and non-exception capable callers. The example below show how a HRESULT is tested and an exception is thrown in case of failure. (usually autogenerated in tli files)
inline _bstr_t IMyClass::GetName ( ) {
BSTR _result;
HRESULT _hr = get_name(&_result);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _bstr_t(_result, false);
}
Using return values will affect readability by having error handling scattered and worst case, the return values are never checked by the code. That's why I prefer exception when a contract is breached.
If you really want to do this kind of thing, you should have different values for each error, and check for success. Something like
define ('OK', 0);
define ('DUPLICATE_USERNAME', 1);
define ('DATABASE_ERROR', 2);
define ('INSUFFICIENT_DETAILS', 3);
And check:
if (createUser('fred') == OK) {
//OK
}
else {
//Fail
}
Other ways include exceptions:
throw new Validation_Exception_SQLDuplicate("There's someone else, hun");),
returning structures,
return new Result($status, $stuff);
if ($result->status == 0) {
$stuff = $result->data;
}
else {
die('Oh hell');
}
I would hate to be the person who came after you for using the code pattern you suggested originally.
And I mean "Came after you" as in "followed you in employment and had to maintain the code" rather than "came after you" "with a wedgiematic", though both are options.
In my opinion, you should use this technique only if failure is a "normal part of operation" of your method / function. For example, it's as probable that a call suceeds as that it fails. If failure is a exceptional event, then you should use exception handling so your program can terminate as early and gracefully as possible.
As for your use of different "false" values, I'd better return an instance of a custom "Result"-class with an proper error code. Something like:
class Result
{
var $_result;
var $_errormsg;
function Result($res, $error)
{
$this->_result = $res;
$ths->_errorMsg = $error
}
function getResult()
{
return $this->_result;
}
function isError()
{
return ! ((boolean) $this->_result);
}
function getErrorMessage()
{
return $this->_errorMsg;
}
Look at COM HRESULT for a correct way to do it.
But exceptions are generally better.
Update: the correct way is: define as many error values as you want, not only "false" ones. Use function succeeded() to check if function succeeded.
if (succeeded(result = MyFunction()))
...
else
...

Categories