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.
Related
I am trying to validate an input using a form, through the use of an if statement.
if (isset($_POST['weekly-rate']))
{
$weekly_rate = $_POST['weekly-rate'];
if(!isset($_POST['weekly-rate']))
{
$error_messages[]= 'Weekly rate was not set';
}
else
{
$weekly_rateOK = true;
}
}
else
{
$error_messages[] = 'Weekly rate was not set...';
}
When I run this it gives doesn't give me the output I want, which is Weekly rent was not set. Am I incorrect in thinking that
if(!isset($_POST['weekly-rate']))
{
$error_messages[]= 'Weekly rate was not set';`
Means, if an input is not set, run the error message, weekly rate was not set.
However all I receive is nothing
Your understanding of isset() is correct. However, the form always posts the field back to your backend code which triggers isset() to be true all the time. You may look at using empty() instead.
A simplified version would look like this
$weekly_rateOK = !empty($_POST['weekly-rate']);
if (!$weekly_rateOK) {
$error_messages[] = 'Weekly rate was not set...';
}
You may need to add an integer check if needed.
If you are using a text input/select with a name, then the input/select is always posted and is set, but with an empty string. If it is a checkbox or radio buttons, then they will be set only if the checkbox is checked or a radio button is selected.
I have written a sample validation function, inspired by Laravel. You can check it here, and extend it by adding additional cases:
function validate ($rule_bag, $input) {
$flag = true;
$error_bag = [];
foreach ($rule_bag as $item => $rules){
$rules = is_array($rules) ? $rules : array_filter(explode(',', $rules));
foreach($rules as $rule){
$rule = trim($rule);
switch(mb_strtolower(trim($rule))){
case 'required': {
// checking isset then empty to be compatible with php <= 5.4
if (!isset($input[$item]) || empty($input[$item]))
{
$flag = false;
!isset($error_bag[$item])?$error_bag[$item]=[]:null;
$error_bag[$item][] = $rule;
}
break;
}
default: {
if (isset($input[$item])){
try {
if (!preg_match($rule, $input[$item])){
$flag = false;
!isset($error_bag[$item])?$error_bag[$item]=[]:null;
!isset($error_bag[$item]['regex'])?$error_bag[$item]['regex']=[]:null;
$error_bag[$item]['regex'][] = $rule;
}
}
catch(Exception $e){
echo $e->getMessage();
}
}
}
}
}
}
return $flag ? $flag : $error_bag;
}
I am submitting a form, using the following function (prize.php):
loadmodule('validate'); //This just loads the validate.php function.
$validate = new Validate;
if($_POST)
{
$validateForm = $validate->validateForm();
switch($validateForm)
{
case 1:
$error = 'You\'re not logged in..';
$stop = true;
break;
//If no error = success.
if($validateForm['code'] == "100"){
$won = $val['prize'];
$type = $val['type'];
$success = 'You have won! The prize was '.$won.' '.$type.'';
die($success);
}
}
die($error);
}
This is the function to validate the form (validate.php):
function validate()
{
global $userdata;
if(!is_array($userdata))
return 1; // User not logged in - return error code one.
//If no error, lets show a success message.
$prize = "100";
$text = "dollars";
return array("code"=>"100","prize"=>"$prize","type"=>"$text");
}//If won
}
The above code returns:
Notice: Undefined variable: error in /home/.../public_html/pages/prize.php on line 27
Although, it shouldn't throw an error there, since the die($success) should be triggered by the code 100.
What am I doing wrong?
$error = '';
$stop = false;
switch($validateForm){
case 1:
$error = 'You\'re not logged in..';
$stop = true;
break;
}
//If no error = success.
if($validateForm['code'] == "100"){
$won = $val['prize'];
$type = $val['type'];
$success = 'You have won! The prize was '.$won.' '.$type.'';
die($success);
}
first guess is that the if($validateForm['code'] == "100"){ is ment to be outside the switch.
$validateForm = $validate->validateForm();
returns an array.. later on your're doing a if ($validateForm==1) in the switch.. when $validateForm is an array.
you might have better luck with a simple is_array() if statement than the whole switch
I'd like some help please, if its possible.
I have created two functions in order to display some messages when is set a $_GET after a redirect.Here's the code:
function display(){
if(isset($_GET['cnf_upd']) && $_GET['cnf_upd'] == '1'){
$value = "The update was successful!";
$type = "confirm";
construct_the_div($value, $type);
}
if(isset($_GET['err_upd']) && $_GET['err_upd'] == '1'){
$value = "The Update failed.";
$type = "error";
construct_the_div($value, $type);
}
if(isset($_GET['cnf_del']) && $_GET['cnf_del'] == '1'){
$value = "Deleted completely.";
$type = "confirm";
construct_the_div($value, $type);
}
if(isset($_GET['err_del']) && $_GET['err_del'] == '1'){
$value = "Unable to delete.";
$type = "error";
construct_the_div($value, $type);
}
}
function construct_the_div($value, $type){
// creating a div to display the message results
$div = "<div class=\"{$type}Msg\">\n";
$div .= "<p>{$value}</p>\n";
$div .= "</div><!-- end of {$type}Msg -->\n";
echo $div;
}
What I'd like to make is to try to improve the display function, as it gets longer and longer, so that there whould be only one (or two at most) if statement(s) if possible. So the value of the GET will be dynamicly inside the if condition and also if it has the preffix 'cnf_' it wil be a 'confirmMsg' and if it has the preffix 'err_' it wil be a 'errorMsg'.
Is it possible to make something like this???
function display() {
$messages = array(
'cnf_upd' => 'The update was successful!',
'cnf_err' => 'The Update failed.!',
// ...
// add all error and confirm there
// ...
);
foreach($_GET as $key => $value) {
if(strpos($key, 'cnf_')===0) {
$type = 'confirm';
$value = isset($messages[$key])
? $messages[$key]
: $key;
construct_the_div($value, $type);
}
if(strpos($key, 'err_')===0) {
$type = 'error';
$value = isset($messages[$key])
? $messages[$key]
: $key;
construct_the_div($value, $type);
}
}
}
The approach is not correct, it seems that only one message should occur at once (there cannot be "deleted completely" and "unable to delete" at once).
Try construct the parameters this way: ?msg=upd&msgType=cnf
function display(){
if (isset($_GET['msg']) && isset($_GET['msgType']))
{
$messages = array('cnf_upd'=>'The update was successful!',
'err_upd'=>'The update failed!',
'cnf_del'=>'The deletion was successful!',
'cnf_upd'=>'The deletion failed!',
);
if (isset($messages[$_GET['msgType'].'_'.$_GET['msg']))
construct_the_div($messages[$_GET['msgType'].'_'.$_GET['msg']], htmlspecialchars($_GET['msgType']));
}
there is still much to improve, but for start this is cleaner and safer.
I'm going to propose a different solution. Instead of setting different parameters in $_GET based on the message to be sent, set one parameter and parse its value.
// Start by setting integer constants:
define(CNF_UPD, 1);
define(ERR_UPD, 2);
define(CNF_DEL, 3);
define(ERR_DEL, 4);
Then when you set the value un $_GET, use the constant:
// Build the URL with a deletion error...
header("Location: http://example.com/script.php?msg=" . ERR_DEL);
Finally, use a switch to parse them
if (isset($_GET['msg'])) {
switch ($_GET['msg']) {
case CNF_UPD:
// Updated...
break;
case ERR_UPD:
// failed...
break;
// etc...
default:
// invalid code.
}
}
If you use a pattern of confirm/error/confirm/error for your integer constants, you can determine which it is by taking $_GET['msg'] % 2. Odd numbers are confirmations, evens are errors. There are of course many other ways you could lay this out, I just happen to have typed them in the alternating order you used. You could also do positive integers for confirmations and negatives for errors, for example.
$type = $_GET['msg'] % 2 == 1 ? $confirm : $error;
This is easily expanded to use multiple messages as well. Since they are integer values, you can safely construct a comma-separated list and explode() them when received.
$messages = implode(array(ERR_DEL,CNF_UPD));
header("Location: http://example.com/script.php?msg=$messages");
Unless you can somehow generate $value and $type based on the $_GET parameter (which I can't see how you would do), you could do something like:
$messages = array();
$messages[] = array('id' => 'cnf_upd', 'value' => 'The update was successful!', 'type' => 'Confirm');
$messages[] = array('id' => 'err_upd', 'value' => 'The Update failed.', 'type' => 'error');
...
foreach ($messages as $message) {
if(isset($_GET[$message['id']]) && $_GET[$message['id']] == '1'){
construct_the_div($message['value'], $message['type']);
}
}
I have a validation function which returns either true or false.
However, I want it to provide info as to what the problem is, when there is one.
Let's say the function is like this:
function is_valid($val) {
$result = true;
if( rule_1_not_met ) $result = false;
if( rule_2_not_met ) $result = false;
return $result;
}
Which is used like this
$val = $_GET['some_param'];
if(!is_valid($val)) $out .= 'Not so helpful feedback.';
...
I thought I could change it like this:
function is_valid($val) {
$result = array(true, array());
if( rule_1_not_met ) $result[1][] = 'Reason 1';
if( rule_2_not_met ) $result[1][] = 'Reason 2';
if(count($result[1]) > 0) $result[0] = false;
return $result;
}
And use it like this:
$val = $_GET['some_param'];
$validation_result = is_valid($val);
if(!$validation_result[0]) $out .= implode('<br/>', $validation_result[1]);
...
My question is
Am I in, for unexpected results with this?
Are there better ways to achieve this?
P.S. Would make this community wiki
You are in the right track but I would like to do this in this way
function is_valid($val,&$mes) {
$result = true;
if( rule_1_not_met ) { $mes[]='message one'; $result = false; }
if( rule_2_not_met ) { $mes[]='Message two'; $result = false; }
return $result;
}
$mes=array();
if(isvalid($val,$mes) ===false) $out .= implode('<br/>', $mes);
In my opinion, your proposed solution works fine. The only problem with it is that you have to remember that $validation_result[0] is the status and $validation_result[1] contains the messages. This might be OK with you but it will be hard to maintain if other people use your code. One thing you can do is when you call your function, you can use array destructuring to at least store the results with meaningful variable names. For example:
[$valid, $errors] = is_valid($val);
if(!$valid) $out .= implode('<br/>', $errors);
For the reason mentioned above, I like Brad Thomas's solution of creating a specialized class that contains the messages and status. Since the properties are named, you don't have to guess how to access the validation information. Also, most good IDEs will autocomplete when you try to access their properties.
I also have an alternate solution. Instead of including a boolean true or false. Just return the array of messages. The caller would just have to check if the returned array has a non-zero number of errors. Here is an example:
function get_errors($val) {
$errors = array();
if( rule_1_not_met ) $errors[] = 'Reason 1';
if( rule_2_not_met ) $errors[] = 'Reason 2';
return $errors;
}
Then the caller would use it like this:
$val = $_GET['some_param'];
$validation_result = get_errors($val);
if (count($validation_result) > 0) $out .= implode('<br/>', $validation_result);
You could use a Result object that encapsulates return data, a message and a status.
i.e.
class Result( $bResult, $sMessage, $mData ) {
public function __construct() {
$this->bResult = $bResult;
$this->sMessage = $sMessage;
$this->mData = $mData;
}
}
In Your code:
$result = new Result(true, 'some helpful message here', null);
$reasons = array();
function is_valid($val)
{
global $reasons;
if ( rule_1_not_met ) $reasons[] = 'Reason 1';
if ( rule_2_not_met ) $reasons[] = 'Reason 2';
if ( count($reasons) == 0 )
return TRUE;
else
return FALSE;
}
if (!is_valid($condition))
{
echo 'Was not valid for these reasons<br />';
foreach($reasons as $reason)
echo $reason, '<br>';
}
else
echo 'Is valid!';
This question is old, and makes a showcase of bad and outdated practices. Using global is frowned upon, and using references in this context is just the same.
Only Cave Johnson's answer makes it straight, but still the usage could be confusing. A better solution would be to write a class, but not as silly one as in the Brad Thomas's answer.
class NumberValidator
{
protected $errors;
public function validate($number)
{
if(!is_numeric($number))
{
$this->errors[] = "The value provided is not numeric";
return false;
}
if($number < 10)
{
$this->errors[] = "The number is less than 10";
return false;
}
return true;
}
public function getErrors()
{
return $this->errors;
}
}
and then it can be used like this
$validator = new NumberValidator();
if($validator->validate($number)) {
/*success*/
}
and then $validator->getErrors() can be used elsewhere
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.