I am currently trying to validate a form server side, the way it works is all the data is put into and array, with the form field as the key and and the field data as the value, however I need to check that all the keys have a value associated with other wise I want the submittion to stop and the user having to edit there details and then re-submit, is there a quick check I can run rather that break the array apart and check that or check before it is put in the array with a switch or losts of if statements?
Sico87,
More of than not you don't want to test all of the fields simultaneously. For instance you may have a contact field that contains a phone-number entry option that isn't mandated, and rejecting the submission for that reason would be problematic.
In many cases, it's easier to test your fields individually so you can give each the special attention it needs. You'll want to give special attention to email addresses, and special attention to birthdays.
Treating all equal could cause very serious problems in the long-run.
function fieldEmpty($value) {
return trim($value) == '';
}
if (count(array_filter($array, 'fieldEmpty'))) {
die('bad');
}
One option is to do a bit of both.
Have a separate array of field options with the field name as the key.
$fieldTypes = array('nameFirst' => 'name',
'nameLast' => 'name',
'phone' => 'phone',
'email' => 'email');
foreach($input as $key => $value) {
switch($fieldTypes[$key]) {
case 'name':
//run $value against name validation
break;
case 'phone':
//run $value against phone validation
break;
case 'email':
//run $value against email validation
break;
default:
//code here for unknown type
}
}
Now, that could be used in any number of ways, and easily expanded to include things like if the field is required or not, or even error messages. By turning the $fieldTypes array into a multidimensional array, or an array of objects containing the data.
This way, if you decide to add a field, it would probably not involve much change in the validation code.
How about something like
<?php
$arrayFilled = true;
foreach($array as $key=>$value) {
$arrayFilled = trim($value) == '' ? false : $arrayFilled;
if($arrayFilled === false) { break; }
}
if($arrayFilled === false) {
echo "missing data";
}
else {
echo "filled array";
}
you may want to check for more than just an empty string but I'll leave that to you
Related
Is there a safe way to auto assign the keys in a posted array? Below are two examples of wrong ways...
foreach( $_POST as $key => $value ) {
$$key = $value;
}
or
extract($_POST)
Is there a better way, or is it best to code:
$foo = $_POST('foo');
$bar = $_POST('bar');
....
for all 50 inputs on my form?
(the posted info will be inserted into a database).
One more cautious way of extracting all input fields at once is:
extract( $_POST, EXTR_OVERWRITE, "form_" );
This way all your input variables will be called $form_foo and $form_bar at least. Avoid doing that in the global scope - not because global is evil, but because nobody ever cleans up there.
However, since mostly you do that in a localized scope, you can as well apply htmlentities if for example you need all fields just for output:
extract(array_map("htmlspecialchars", $_POST), EXTR_OVERWRITE, "form_");
There is not a single reason to do it.
To handle user inputs an array is 100 times better than separate variables
I like an approach where you let dynamic getters and setters in a class do all the work for you. Here's how I would code it.
First, create a bass class to hold data:
class FormParameterHandler {
protected $parameters;
public function __construct($associative_array) {
$this->parameters = array();
foreach($associative_array as $key => $value) {
$this->{$key} = $value;
}
}
public function __get($key) {
$value = null;
if(method_exists($this, "get_$key")) {
$value = $this->{"get_$key"}();
} else {
$value = $this->parameters[$key];
}
return $value;
}
public function __set($key, $value) {
if(method_exists($this, "set_$key")) {
$this->{"set_$key"}($value);
} else {
$this->parameters[$key] = $value;
}
}
}
Next, create a specific class to use for some specific form where there is something special to validate. Use your freedom as a programmer here to implement it any way you want to. And remember, since we're using reflection to look for setter methods, we can write specific setter methods for known problem areas, like e.g. to check for equal passwords in a "register user" form:
class RegisterFormParameterHandler extends FormParameterHandler {
private $passwords_are_equal = null;
public function __construct($register_form_parameters) {
parent::__construct($register_form_parameters);
}
public function has_equal_passwords() {
return $this->passwords_are_equal;
}
public function set_password($password) {
$this->parameters['password'] = $password;
$this->compare_passwords();
}
public function set_password_repeat($password_repeat) {
$this->parameters['password_repeat'] = $password_repeat;
$this->compare_passwords();
}
private function compare_passwords() {
if(isset($this->parameters['password']) && isset($this->parameters['password_repeat'])) {
$this->passwords_are_equal = ($this->parameters['password'] === $this->parameters['password_repeat']);
}
}
}
Finally, use the derived class in a "register user" flow, to easily find out if the two entered passwords match:
$registerFormParameterHandler = new RegisterFormParameterHandler($_POST);
if($registerFormParameterHandler->has_equal_passwords()) {
print "are equal";
//register user
} else {
print "are not equal";
}
You can test this by creating an HTML form that has one input field with the name "password", and another input field with the name "password_repeat".
To access any of the form data, use your form data object variable name, followed by the access operator "dash larger than" -> , followed by the name of the parameter. In the example above, if there was an input field named "user_name", it would be accessed through a call to
$registerFormParameterHandler->user_name
Rr, if you have defined the name of the field you want to get in some other variable, use reflection:
$registerFormParameterHandler->{$settings['form_data_user_name']}
Have fun! :)
A safe way to extract variables into the local scope is not to. You're injecting variables into your local scope, which is a problem however you do it. Even if you limit the variables to only a few select ones that won't clash with other variable names in the scope now, if you start adding elements to your form you may be in trouble later.
Arrays are specifically for holding an unlimited amount of named values without crowding the variable namespace. Use them! You may have to type a little more, but that's par for the course.
While it is best to refer to them with $_POST['variablename'], it is possible to expand only the variables you are expecting.
$expected = array('name', 'telephone', /* etc */);
foreach ($_POST as $key => $value) {
if (!in_array($key, $expected)) {
continue;
}
${$key} = $value;
}
Or, I prefer this:
foreach ($_POST as $key => $value) {
switch ($key) {
case 'name':
case 'telephone':
/* etc. */
${$key} = $value;
break;
default:
break;
}
}
The answer to your question depends on the computer, language, and security knowledge of the programmer. The opening sequence of processing $_POST is kind of like the opening move in a game of chess. Many use foreach loops without realizing that foreach will make a copy of the contents of $_POST the way you have it used (Programming PHP: Chapter 5, p.128-129). Wouldn't it be funny if you caused a buffer overflow simply by using foreach!
One commenter implied that everything should just be worked with inside of the $_POST superglobal. There are some merits to this... However, forgetting cache memory for a moment, access to array values is slower than direct access to a variable.
Since you have fifty (50) controls to validate (with potentially large contents), I might not want to take that array access performance hit more than 50 times (the original access hits). Moreover, if you are concerned about writing secure input validation routines, keeping your dirty laundry (non-validated input) separate from your clean (validated input) laundry is a good idea. That said, you may need a clean array anyway (hence the $_POST advocate's response), but at least you are reducing risk in the process by keeping the hopefully good separate from the potentially bad.
Is there a safe way to auto assign the keys in a posted array?
I might start like this:
Function library for this example.
function all_controls_submitted($controls) {
$postKeys = array_keys($_POST);
foreach($controls as $key) {
if(! array_key_exists($key, $postKeys)) {
return false;
}
}
return true;
}
function all_controls_set($controls) {
foreach($controls as $key) {
if(! isset($_POST[$key])) {
return false;
}
}
return true;
}
if(is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER[REQUEST_URI]) && $_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = true;
} elseif (is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/contact.php') {
$newForm = false;
$controlNames = array('firstName', 'lastName', 'e-mail', 'company', 'subject', 'message', 'captcha');
define('NUM_CONTROLS_EXPECTED', count($controlNames)); //Seven (7)
if(is_array($_POST) && count($_POST) === NUM_CONTROLS_EXPECTED && all_controls_submitted($controlNames) && all_controls_set($controlNames)) {
//Begin input validation
}
} else {
header('location: http://www.nsa.gov');
}
Notice that I prep things with the $controlNames array, therefore I do not have to ask $_POST for the keys. After all, I should know them! :-) The two user-defined functions, all_controls_submitted() and all_controls_set() are two fundamental questions that should be asked before trying to use any values in $_POST (I say, anyway). Admittedly, I do use $_POST in all_controls_submitted(), but only to obtain the names of the controls submitted, not the values.
$postKeys = array_keys($_POST);
However, who is to say that the control names themselves could not be poisonous and in need of input validation?!! Same goes for values in $_SERVER. See, a chess game.
Is there a safe way to auto assign the keys in a posted array? I cannot tell you for certain, but perhaps something like the code above could help? You'd have the keys, at least.
Programming PHP: Chapter 5, p.125
Try this inbuilt method/function filter_input
Reference URL: php.net function filter input
Is it efficient to simply use a long series of nested if / else statements in PHP to validate user input from an HTML form?
So say there's two fields, I'm just approaching it like this:
if ( field one is formatted right ) {
if ( field one is long enough ) {
if ( field two is ok ) {
do stuff;
}
else {
echo "field two is not ok!";
}
}
else {
echo "field one is not long enough!";
}
}
else {
echo "field one is not formatted right!";
}
I hope you get the idea.
So when there are several fields and a lot of validation to do, and different kinds of validation for each field, this ends up being quite long.
Is this a bad way to deal with this?
what i usually do is something like:
$error_msg = '';
if (field1 not valid)
$error_msg .= 'Field One is not valid.<br>';
if (field2 not valid)
$error_msg .= 'Field two is not valid.<br>';
if ($error_msg == '')
{
// DO ALL YOUR VALID SUBMISSION STUFF HERE
}
I usually think its better to notify the user of all their errors at once instead of one at a time to annoy them.
That code should work, and one way to determine its efficiency would be to use profiling to actually measure how long it takes.
An alternative way to validate would be something like this:
function handle_submission(){
$field_1_validation_result=validate_field_1_value($field_1_value);
if($field_1_validation_result!==true)exit($field_1_validation_result);
$field_2_validation_result=field_2_validation_result($field_2_value);
if($field_2_validation_result!==true)exit($field_2_validation_result);
//Everything is right
do stuff
}
function validate_field_1_value($value){
if ( !field one is formatted right )return "field one is not formatted right";
if ( !field one is long enough )return "field one is not long enough";
return true;
}
function validate_field_2_value($value){
if( !field two is ok)return "field two is not ok"
return true;
}
Thinking forward there are definitely better ways you can approach this. For a start you can instead try to create something like a validation class with pre-defined validation methods.
Take for (a very rough) example:
class Validator {
public function validate($args) {
$errors = array();
//Make sure to do any santising you need too
foreach($args as $field=>$data) {
if ($response = $field($data))
$errors[$field] = $response;
//You could create break code here if you wanted
//to check if the form was invalid without detailed feedback
}
if (empty($errors)) {
return false;
}
return $errors;
}
private function email_field($data) {
$valid = true;
//perform a validation on email here, generate an $err_msg if it's not valid
if ($valid)
return false;
return $err_msg
}
}
$validator = new Validator();
//You probably want to explode or loop through the error messages
//Or for extra brownie points, use the key of each element in the array to place
//the error message intelligently within your HTML!
$validation_errs = $validator->validate($_POST);
if ($validation_errs)
print_r($validation_errs);
Combined with your form...
<form method="post" action="">
<input type="text" name="email_field" />
</form>
This could then be combined to provide your validation. The reason this may be better is that you could use this class throughout your site, meaning you don't have to duplicate your validation logic, and more importantly you don't have to have trails of nested logic for each form. You simply pass the post variables, your class will automatically run the validation that it requires, and give you back either an all clear (unintuitively, "false" here, sorry) or an array of validation error messages paired with the field name they came from.
Furthermore this then becomes useful when dealing with combining serverside and clientside validation, allowing you to use AJAX to call very specific, single field, validation queries to your PHP over AJAX and returning you the response that can be used (as long as you are doing enough with your output to ensure it is valid for AJAX communication).
So, when it comes to efficiency, it's not just about what is important now, or how negligible the performance differences might be...it's about how much time it will save you later, when your form changes, when your validation logic has to be altered, and when you need to reuse the same validation logic over and over again.
I think that formatting like this is easier to read and follow:
if ( field one is not formatted right ) {
echo "field one is not formatted right!"
} else if ( field one is no long enough ) {
echo "field one is not long enough!";
} else if ( field two is not ok) {
echo "field two is not ok!";
} else {
do stuff;
}
This is what I do most of the times:
$Errors = array();
if (field one is not formatted right)
$Errors[] = "Field one is not formatted right.";
if (field one is not long enough)
$Errors[] = "Field one is not long enough.";
if (field two is not ok)
$Errors[] = "Field two is not ok.";
if (empty($Errors)) {
do stuff;
} else {
foreach ($Errors as $Error) {
echo '<div class="error">' . $Error . '</div>';
}
}
This is similar to other answers only that I use an array to store the error messages so that I can format them easier.
I have a sign up form with about 10 fields and they are all required to be filled in before processing, so trying to avoid all those if checks I came up with this, is this a good method?
foreach($list as $items) {
if(empty($items)) {
$error[] = "All fields are required.";
break;
}
}
or should I make if(empty($field_1) || empty($field_2) etc.. then output the error?
Assuming that your data is coming from $_GET or $_POST, all data fields will be strings. This means that you should be able to do the check in a single function call:
if (in_array('', $list, TRUE)) {
$error[] = "All fields are required.";
}
This looks for strings which are exactly equal to an empty string. If you want to make the comparisons loose (more or less identical to the check that empty() does) just remove the final TRUE.
EDIT Thinking about it, you don't need the strict comparison. I did this to allow for a legitimate field value of '0' (which empty() would not allow) but this will also be permitted with loose comparisons, since '0' != ''.
ANOTHER EDIT If you want to check that the length of the sting is greater than two, you will have to loop:
foreach ($list as $item) {
if (strlen($item) < 2) {
$error[] = "All fields are required.";
break;
}
}
This will also "clear out 0" assuming that by this you mean "not allow a value to be 0". If you also want to disallow '00' (or any other string that results in 0) you can change the if clause to this:
if (strlen($item) < 2 || (!(int) $item)) {
it's ok. If you just want to show message "All fields are required." without showing which field in blank.
Otherwise it will be more user friendly if you check and show which field is left empty.
It's good idea to put it into loop as you did but please note that this will fail even when user inputs 0 and will pass for string containing only spaces, so you may want to make better checks than empty()
I would approach this with an in_array check.
<?php
$fields=array('name','age','yadayada','something_else');
foreach ($_POST as $key=>$value){
if(in_array($key,$fields) && $value!=''){
$$key=$value;
}else{
$error[$key]='This field is required.';
}
}
?>
So, i have this code that seems to be doing the mechanical work correctly, avoiding to write on tables if one or more fields from a form are left empty, but the echos and messages aren't working as they supposed to:
SOLUTION TO THE PROBLEM:
I don't believe this will be useful to anyone, but just for the record, the solution is simple:
$campos = array('nome','morada','email','telemovel','codigopostal','vat');
foreach ($campos as $key => $campo) {
$campos[$key] = $_GET[$campo];
if(!isset($_GET[$campo])|| empty($_GET[$campo])){
header("Location: ../index.php?erro=".$campo);
$verifica=FALSE;
die();
}else{
$verifica=TRUE;
}
}
This will give me some problems, not what i really wanted but solves the logical problems i was having. Thanks to you all guys.
$campos = array('nome','morada','email','telemovel','codigopostal','vat');
foreach ($campos as $key => $campo) {
$campos[$key] = ($_GET[$campo]);
while(list($key, $campo)= each($campos))
if(!isset($_GET[$campo])|| $_GET[$campo]==""){
echo("não preencheu um dos campos");
$verifica = FALSE;
die();
}else{
echo $_GET[$campo]." \r\n";}
$verifica = TRUE;
}
if($verifica==TRUE){
some irrelevant code.
}
As i said, the code itself is working flawlessly, BUT if i only left 2 empty fields on the form, the echo $_GET[$campo] will be working even though the variable $verifica will be set as FALSE
AN HINT:
One of the fields is making the code to fail, is the second one, so, if i do this:
../phcexport.php?nome=myname&morada=&codigopostal=postcode&email=#.com&vat=123123&telemovel=00800
And ignore the second value from the array, the code will work like a charm, i can try as many combinations as i can as long as i left the second field empty, it will work properly, giving me the error "some field is empty", in this case i know it is, the "morada" is empty, and if i fill it in the code says "its ok, all filled in" BUT the "morada" should be the last to be filled so the code works. Funny... (I'm sorry about all the text to describe the problem, i'm Portuguese)
EDIT2: For the rest of the code i need to use $campos[$key], so attribute a key to the arrays is essencial.
The problem is that you reset $verifica in the else. So regardless if you set it once to FALSE, the last foreach iteration will determine the outcome. But you can "simplify" the whole approach to:
$verify = array_search(0, array_map("strlen", $campos)) === false;
This simply checks for the string length of each array entry. If none of them is 0, then the expression will return true for $verify;
instead of doing $_GET[$campo]=="", you can maybe use the empty function :
empty($_GET[$campo])
This should address all problems related to different data types.
Your also maybe running in a problem because you're using a variable named $campo in two different loops, try changing the variable name in the while loop to see if it works better.
Check the exact value of $campo that is causing the incorrect behaviour, then consult this table to make sure you are using the right kind of comparison
http://www.php.net/manual/en/types.comparisons.php#types.comparisions-loose
try following solution, empty fields can not pass the test
<?php
$campos = array('nome', 'morada', 'email', 'telemovel', 'codigopostal', 'vat');
$verifica = TRUE;
foreach ($campos as $campo) {
if (!isset($_GET[$campo]) || empty($_GET[$campo])) {
echo "não preencheu um dos campos" ;
$verifica = FALSE;
break;
} else {
echo $_GET[$campo] ."\r\n";
}
}
if ($verifica == TRUE) {
// some irrelevant code.
} else {
die();
}
I have a form that allows the user to add information an their leisure. They can add locations via jQuery in my form so when recieving the data I may have 1 location or 10. Each location has attributes like phone, address, etc. In my form the input names are appended with _1 , _2, etc to show its a new set of data. That is working swimmingly and I just can't seem to find these keys when looping through the $_POST array
private function array_pluck($arr,$text)
{
foreach($arr as $key => $item)
{
if(stripos($key,$text) != 0)
{
$found[] = $item;
}
}
return $found;
}
As I understand it if my array has some keys "office_branch_phone_1, office_branch_phone_2" I should be able to put in "office_branch" in my $text param and it will spit out any keys with the "office_branch" in the name. This isn't working however and I'm a bit stumped.
Since stripos will return the index (and it is a 0-based index returned) != 0 is incorrect.
if (stripos($key,$text) !== false)
Would be the correct way to check it. Give that a shot.
EDIT
Note the use of !== instead of != since 0 tends to be considered false if loosely checked the !== will check the actual type, so 0 is a valid return. Just an extra tidbit of information