PHP multiple if / elseif and error messaging / handling best practices - php

This is a problem I come accross fairly regularly and I've never found / figured out a best practices situation. Exceptions are probably the way to go however the application I'm working makes no use of them so I'm trying to stick to the currently used methods.
What is the best way to lay out if statements, returns, messages etc. in the event that 3, 4, 5 or more different conditions are required to be checked and either an error message is set or the processing continues. Is it best practice to have all error checking physically at the start of the code?
Here's an example with some real-world type conditions.
function process($objectId,$userId,$newData)
{
$error = '';
if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId))
{
if($this->isValid($newData))
{
if($object->isWriteable())
{
if($object->write($newData))
{
// No error. Success!
}
else
{
$error = 'Unable to write object';
}
}
else
{
$error = 'Object not writeable';
}
}
else
{
$error = 'Data invalid';
}
}
else
{
$error = 'Object invalid';
}
return $error;
}
OR
function process($objectId,$userId,$newData)
{
$error = '';
if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId))
{
$error = 'Object invalid';
}
elseif(!$this->isValid($newData))
{
$error = 'Data invalid';
}
elseif(!$object->isWriteable())
{
$error = 'Object not writeable';
}
elseif(!$object->write($newData))
{
$error = 'Unable to write to object';
}
else
{
// Success!
}
return $error;
}
It's clear to me that in this case option 2 is the way to go. It's much clearer. Now, we can make it a bit more complex:
function process($objectId,$userId,$newData)
{
$error = '';
if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId))
{
$this->setValidationRules();
$parent = $object->getParentObject();
$parent->prepareForChildUpdate();
if($this->isValid($newData,$parent))
{
$newData = $this->preProcessData($newData);
if($object->isWriteable())
{
// doServerIntensiveProcess() has no return value and must be done between these two steps
$this->doServerIntensiveProcess();
if($object->write($newData))
{
// No error. Success!
$parent->childUpdated();
}
else
{
$error = 'Unable to write object';
}
}
else
{
$error = 'Object not writeable';
}
}
else
{
$error = 'Data invalid';
}
}
else
{
$error = 'Object invalid';
}
return $error;
}
OR this which has some issues with it
function process($objectId,$userId,$newData)
{
$error = '';
if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId))
{
$error = 'Object invalid';
}
// Is it wrong to hate multi-line conditionals?
elseif(!$this->setValidationRules() || (!$parent = $object->getParentObject()) ||
!$parent->prepareForChildUpdate() || !$this->isValid($newData,$parent))
{
$error = 'Data invalid';
}
elseif((!$newData = $this->preProcessData($newData)) || !$object->isWriteable())
{
$error = 'Object not writeable';
}
// Where does doServerIntensiveProcess() with no return value go??
elseif(!$object->write($newData))
{
$error = 'Unable to write to object';
}
else
{
// Success!
$parent->childUpdated();
}
return $error;
}
I'm just not sure of the best way to handle this nested if-this-then-do-that-then-if-this-then-do-that kind of functionality. Thank you indvance for any insight you can provide!

What I tend to do to keep code clean is like so:
function process($objectId,$userId,$newData)
{
$object = $this->getObject($objectId);
if($object === false)
{
return "message";
}
if($object->userOwnsObject($userId) === false)
{
return "message";
}
if($this->setValidationRules() === false)
{
return "unable to set validation rules";
}
if(false !== ($parent = $object->getParentObject()))
{
return "unable to get parent object";
}
/*... etc ...*/
//if your here the all the checks above passed.
}
by doing it this way your also saving resources as your your directly returning in place, the code looks cleaner and no need for 2 many nests
but if your building the function from scratch I don't see why you cant use exceptions in in your new code, it will not interfere with the current app, and makes live simpler
function process($objectId,$userId,$newData)
{
if(false !== ($parent = $object->getParentObject()))
{
throw Exception("unable to get parent object");
}
/*... etc ...*/
}
and
try
{
$this->process(....);
}
catch(Exception $e)
{
show_error_page('invalid.php',$e);
}
or another way is to create an error handling class with a static method called InternalError like so
abstract class Error
{
public static InternalError(Exception $Ex)
{
Logger::LogException($Ex);
//Then flush all buffers and show internal error,
}
}
so instead of of the show_error_page above you can do:
try
{
$this->process(....);
}
catch(Exception $e)
{
Error::InternalError($e); //this provides user with an interface to report the error that has just been logged.
}
this way all your Exception(s) are logged and can be viewed within your administration system, meaning you can track errors faster and not rely on members to visibly see errors, but get a nice apology with an email form asking them to describe what they was trying to do, the error ID will be attached to the form, so you can trace the user to the error.
That's the best form of error handling IMO.

What about this?
function process($objectId,$userId,$newData)
{
if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId))
return 'Object invalid';
elseif(!$this->isValid($newData))
return 'Data invalid';
elseif(!$object->isWriteable())
return 'Object not writeable';
elseif(!$object->write($newData))
return 'Unable to write to object';
// Success!
}
For the more complex example::
function process($objectId,$userId,$newData)
{
if(!($object = $this->getObject($objectId)) || !$object->userOwnsObject($userId))
return 'Object invalid';
$this->setValidationRules();
$parent = $object->getParentObject();
$parent->prepareForChildUpdate();
if(!$this->isValid($newData,$parent))
return 'Data Invalid';
$newData = $this->preProcessData($newData);
if(!$object->isWriteable())
return 'Object not writable';
// doServerIntensiveProcess() has no return value and must be done between these two steps
$this->doServerIntensiveProcess();
if(!$object->write($newData))
return 'Unable to write object';
// No error. Success!
$parent->childUpdated();
return '';
}

I would completely omit
if($object->isWriteable())
{
if($object->write($newData))
{
// ..
}
}
And throw Exceptions when calling object-write() (without a return value) instead. Same for the other examples
if ($something->isValid($data)) {
$op->doSomething($data); // throws XyException on error
}
If you really want to use such constructs, you can also use the swtch-Statement
switch (true) {
case !$something->isValid($data):
$errors = "error";
break;
// and so an
}
But I really recommend Exceptions.

I don't think it is wrong per se to write multi-line conditionals, but you can make it more readable by putting conditions into a variable and use that in your if-statment. I think the elseif constuct is better.

Related

How to check my statement but like individually if it is coded as one

This is a function i am currently using for registering of user account in php.
function checkpost($input, $mandatory, $pattern) {
$inputvalue=$_POST[$input];
if (empty($inputvalue)) {
printmessage("$input field is empty");
if ($mandatory) return false;
else printmessage("but $input is not mandatory");
}
if (strlen($pattern) > 0) { //Checks for Input Validation
$ismatch=preg_match($pattern,$inputvalue);
if (!$ismatch || $ismatch==0) { // If is not match or is match = 0
printmessage("$input field wrong format <br>");
if ($mandatory) return false;
// header("location: registerform.php");
}
}
return true;
}
$checkall=true;
$checkall=$checkall && checkpost("name",true,""); //Mandatory
$checkall=$checkall && checkpost("email",true,"/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i");
$checkall=$checkall && checkpost("password",true,"/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,}$/");
$checkall=$checkall && checkpost("nric",true,""); //Mandatory
$checkall=$checkall && checkpost("mobile",true,"");
$checkall=$checkall && checkpost("address",true,"");
I tried doing this way of method to check for the statement I have about but I am unsure how can i check like individually like email, password,nric. Is there something wrong with my ifelse cause when I do
// if (!$checkall) { The default error
// printmessage("Error checking inputs<br>Please return to the registration form");
// die();
// }
This will work but I want to check each individual field and print it out. Please help me I'm struggling right now
This is the one I tried but the regex statement suddenly does not work and it will print out the wrong echo statement even if the email format is correct. Seeking help please help me thank you in advance
if (!$checkall=$checkall && checkpost("email",true,"/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i")) {
echo "Invalid email";
die();
}
elseif (!$checkall && checkpost("password",true,"")) {
echo "Invalid password";
die();
}
You could reorder your code for some prettier logic by wrapping the validation into a try/catch block. This will validate each entry, but throw an exception on the first error. When one happens the helper variable $hasErrors is set to true. So you know after validation if any one failed or not.
$hasErrors = false;
try {
if (!checkpost("name", true, "")) throw new Exception("Name is missing");
if (!checkpost("email", true, "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,})$/i")) throw new Exception("Wrong email format");
if (!checkpost("password", true, "/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,}$/")) throw new Exception("Password insecure");
if (!checkpost("nric", true, "")) throw new Exception("Nric?");;
if (!checkpost("mobile", true, "")) throw new Exception("Mobile missing");
if (!checkpost("address", true, "")) throw new Exception("Address missing");
} catch (Exception $e) {
$hasErrors = true;
echo "Error: " . $e->getMessage();
}
if (!$hasErrors) {
echo "Success";
}
Note: You don't need the message for exception at all or print it! I've added it for demonstration purpose.

Header Redirect after form Validation in PHP

I am trying this code as part of form processing:
<?php
if(isset($_POST['senderEmail']))
{
try
{
require '_php/_security/validation.php'; //SEE BELOW
$rules = array(
'senderEmail' => 'validEmail',
'emailTextbox' => 'validTextbox',
);
$validation = new Validation();
if ($validation->validate($_POST, $rules) == TRUE) {
require("_php/database/dbProcessing.php"); //Form Proccessing for database inclusion
}
else {
foreach($validation->emailErrors as $error){
$emailErrors[] = $error;
$_SESSION['$emailErrors'] = $emailErrors;
header('Location:indexmobile.php#emailErrors');
die('ABORT!');
}
}
}
catch (PDOException $e)
{
$error = 'Error adding elements to database: ' . $e->getMessage();
echo "Error: " . $error;
exit();
}
exit();
}
?>
The validation.php where I do my validation has this:
<?php
class Validation {
public $errors = array();
public function validate($data, $rules) {
$valid = TRUE;
foreach ($rules as $fieldname => $rule) {
$callbacks = explode('|', $rule);
foreach ($callbacks as $callback) {
$value = isset($data[$fieldname]) ? $data[$fieldname] : NULL;
if ($this->$callback($value, $fieldname) == FALSE) $valid = FALSE;
}
}
return $valid;
}
public function validEmail($value, $fieldname) {
$valid = !empty($value);
if ($valid == FALSE) {
$this->emailErrors[] = "The $fieldname is required";
return $valid;
} else {
$valid = filter_var($value, FILTER_VALIDATE_EMAIL);
if ($valid == FALSE) $this->emailErrors[] = "The $fieldname needs to be a valid email";
return $valid;
}
}
public function validTextbox($value, $fieldname) {
$valid = !empty($value);
if ($valid == FALSE) {
$this->emailErrors[] = "The $fieldname is required";
return $valid;
} else {
$whitelist = '/^[a-zA-Z0-9 ,\.\+\\n;:!_\-#]+$/';
$textarea = strip_tags($value);
$textarea = mysql_real_escape_string($textarea);
$valid = preg_match($whitelist, $textarea);
if ($valid == FALSE) $this->errors[] = "The $fieldname contains invalid characters";
return $valid;
}
}
}
Upon using this, Im have issues with the redirect (I think). It seems further that Im having errors in validation. My questions are thus:
Am I doing the header redirect correctly? I've read that " header() must be called before any actual output is sent,.." So is this the reason why this redirect is incorrect? how to make a redirect if i need to show/send something to the redirected page?
function validTextbox always ends up an error that the field is empty. Why so?
Is my entire process of form validation a good way of validating form fields (which i learned from watching an online tutorial)? What is a better way?
Is there something wrong with error reporting in this case?
Thank you for those who replies. I am new to PHP and trying my best to learn the language.
1 - There are several ways to pass on a message to the page you are redirecting to. One is through $_GET like this
$message="Some message for the next page.";
$message=urlencode($message);
header("Location:page.php?message=".$message);
then on page.php
if(!empty($_GET['message']))
{
$_GET['message'];
}
similarly you can also use the session (less secure)
$_SESSION['message']='some other message';
then on page.php
if (!empty($_SESSION['message']))
{
echo $_SESSION['message'];
unset($_SESSION['message']);
}
2 - I would have to see what you are passing to your validate function. You should do a var_dump of $_POST and add that to your question.
3 - It depends on your criteria. If you are just checking for emptiness its overkill. I don't know what text you need / consider valid, but a regex is a reasonable way of enforcing validation.
4 - See #2.

PHP Catch Error

I need to be able to catch an error. I have the following code
if($call['status_id'] != '' && $call['datetime_required'] != '')
{
//do stuff
}
else
{
// tell them how it failed
}
How would I go about displaying the section on which ti failed. So for example I can return a dynamic error message ie
return 'You did not fill in the field $errorField';
Where
$errorField
Is the if check on which it failed.
UPDATE
I currently code like so
if($call['status_id'] != '')
{
if ($call['datetime_required'] != '')
{
//do stuff
}
else
{
// tell them it failed due to the second condition
}
}
else
{
// tell them it failed due to the first condition
}
But would like to do the check in one line and change the message depending on where ti failed.
Note #jack had already posted his answer before this update.
I'm not sure I fully understand you, you mean something like this?
function check($call, $req_fields) {
$failed = array();
foreach($req_fields as $field) {
if(!$call[$field]) {
$failed[] = $field;
}
}
if(count($failed)) {
return join(', ', $failed) . ' are required.';
}
return 'success?' ;
}
$message = check($call, array('status_id', 'datetime_required'));
if($call['status_id'] != '')
{
if ($call['datetime_required'] != '')
//do stuff
}
else
{
// tell them it failed due to the second condition
}
}
else
{
// tell them it failed due to the first condition
}

How to exit if statement and continue to else

This is a long shot question, but is there a way in php to exit an "if" statement and continue on to the "else" statement if an error occurs inside the if block?
example
if ($condition == "good")
{
//do method one
//error occurs during method one, need to exit and continue to else
}
else
{
//do method two
}
Of course it is possible to do a nested if inside the first if, but that seems hacky.
TIA
try {
//do method one
//error occurs during method one, need to exit and continue to else
if ($condition != "good") {
throw new Exception('foo');
}
} catch (Exception $e) {
//do method two
}
I would just use a function so you don't duplicate code:
if ($condition == "good") {
//do method one
//error occurs during method one
if($error == true) {
elsefunction();
}
} else {
elsefunction();
}
function elsefunction() {
//else code here
}
Should that be possible?
Anyway you might consider changing it to.
$error = "";
if ($condition == "good") {
if (/*errorhappens*/) { $error = "somerror"; }
}
if (($condition != "good") || ($error != "") ) {
//dostuff
}
You could modify methodOne() such that it returns true on success and false on error:
if($condition == "good" && methodOne()){
// Both $condition == "good" and methodOne() returned true
}else{
// Either $condition != "good" or methodOne() returned false
}
Assuming that methodOne returns false on error :
if !($condition == "good" && methodOne())
{
//do method two
}
you really need this? i think no... but you can hack..
do{
$repeat = false;
if ($condition == "good")
{
//do method one
$condition = "bad";
$repeat = true;
}
else
{
//do method two
}
}while( $ok ) ;
I advise on methods to separate...
I find using switches instead of if...else are handy for doing this: Omitting a break statement makes the switch fall through to the next case:
switch ($condition) {
case 'good':
try {
// method to handle good case.
break;
}
catch (Exception $e) {
// method to handle exception
// No break, so switch continues to default case.
}
default:
// 'else' method
// got here if condition wasn't good, or good method failed.
}
if ($condition == "good") {
try{
method_1();
}
catch(Exception $e){
method_2();
}
}
else {
method_2();
}
function method_2(){
//some statement
}

Defined error message in the PHP

How do I "make" error messages in best practices? Right now I just have $error = ''; and then just a lot of if(){}s that do .= 'Message<br>', and then if (!empty($error)) { echo $error; }, which I assume is a terrible thing.
I guess you want to show error to user. I usually use an array.
$success = true;
$messages = array();
if($something_wrong == true){
$success = false;
$messages[] = 'something wrong';
}
if($morething_wrong == true){
$success = false;
$messages[] = 'more thing wrong';
}
if(!$success){
foreach($messages as $message){
echo $message.'<br />';
}
}
Basically if things goes wrong, I'll always assign the message as the new last component of $messages. In the end of execution, I show all of the messages.
Hope this helps
one way to do it:
function err($err){
switch($err){
case 0: $message.="brain fried"; break; // comment if you want
case 1: $message.="head ache"; break;
case 2: $message.="segment fault big time"; break;
}
return $message;
}
And
if (something is wrong){$message=err(3);}
Just a way to group things all together.

Categories