I've used this PHP/AJAX method of validation for a bit, but I think it could use a little peer review. The basic idea was to create a way of verifying forms in a nice manor using AJAX, but also fall-back to PHP when the user disables JavaScript.
One extra note: jQuery is used throughout the JavaScript files.
field_validation.php
if (isset($_POST['field']) && isset($_POST['value'])) {
require_once('includes/functions.php'); // Allows connection to the database
$field = $_POST['field'];
$value = $_POST['value'];
echo validate_input($field, $value);
}
function validate_input($field, $input) {
switch ($field) {
case 'email':
if ($input != '') {
// We need the validate_email() function, so we now require the file with it in. (Not included as I'm happy with it's validation and this is only used as an example)
require_once ('validate_email.php');
if (validate_email($input) == false) {
return 'Please enter a valid email';
}
} else {
return 'Please enter an email';
}
// Check email address is not already taken
$db = mysqlConnect();
$stmt = $db->stmt_init();
$stmt->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE email = ?)");
$stmt->bind_param('s', $input);
$stmt->execute();
$stmt->bind_result($alreadyTaken);
$stmt->fetch();
$stmt->close();
$db->close();
if ($alreadyTaken) {
return 'Email address already in use, you cannot sign up again';
}
return true;
break;
case 'password':
if (strlen($input) <= 6) {
return 'Please enter a password more than 6 characters in length';
}
return true;
break;
case 'twitter':
if (strlen($input) < 1 ) {
return true;
}
if (strlen($input) >= 16) {
return 'Twitter Usernames cannot be more than 15 cahraters in length (excluding the @ symbol)';
}
$apiurl = 'http://api.twitter.com/1/users/show.xml?screen_name=' . $input;
if (!@fopen($apiurl, 'r'))
{
return 'Username does not exist, please enter a valid username';
}
// Check twitter name is not already taken
$db = mysqlConnect();
$stmt = $db->stmt_init();
$stmt->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE twitter = ?)");
$stmt->bind_param('s', $input);
$stmt->execute();
$stmt->bind_result($alreadyTaken);
$stmt->fetch();
$stmt->close();
$db->close();
if ($alreadyTaken) {
return 'Twitter name already in use, please chose another name';
}
return true;
break;
case 'displayname':
if (strlen($input) < 1) {
return 'Please enter a Display Name';
}
if (strlen($input) > 64) {
return 'Please enter a Display Name shorter than 65 charaters';
}
$db = mysqlConnect();
$stmt = $db->stmt_init();
$stmt->prepare("SELECT EXISTS(SELECT 1 FROM users WHERE display_name = ?)");
$stmt->bind_param('s', $input);
$stmt->execute();
$stmt->bind_result($alreadyTaken);
$stmt->fetch();
$stmt->close();
$db->close();
if ($alreadyTaken) {
return 'Display Name already in use, please chose another name';
}
return true;
break;
case 'termsandconditons':
if ($input != 'on') {
return 'You must accept the Terms and Conditions';
}
return true;
break;
default:
return true;
break;
}
}
function showErrors($errors) {
?>
<div id="errors">
Errors<br>
<ul>
<?
for ($i = 0; $i < count($errors); $i++) {
if (isset($errors[$i]['name'])) {
// Highlight the field
?>
<script>
var cross = '<img src="/images/cross.png">';
$(document).ready(function(){
$('#<? echo $errors[$i]['name']; ?>hint').html(cross);
});
</script>
<?
}
?>
<li>
<? echo $errors[$i]['message']; ?><br >
</li>
<?
}
?>
</ul>
</div>
<?
}
This is included in each of the pages that require validation. I have removed some of the extra cases since they are pretty much the same as the ones shown.
validatefields.js
var cross = '<img src="/images/cross.png">';
var tick = '<img src="/images/tick.png">';
var loading = '<img src="images/loading.gif">';
// Send the data from the form to the PHP file 'jqueryvalidate.php', where they will call the function to validate the data
$('input:text,input:password,input[type=email]').blur(function() {
// Get the input from the form
var fieldid = $(this).attr('id');
var fieldvalue = $(this).attr('value');
$('#' + fieldid + 'hint').html(loading);
$.ajax ({
url: 'field_validation.php',
data: {field: fieldid, value: fieldvalue},
type: 'post',
cache: false,
success: function(message) {
if (message != true) {
// Error was returned, show a cross
$('#' + fieldid + 'hint').html(cross + message);
} else {
// No error, show a tick
$('#' + fieldid + 'hint').html(tick);
}
}
});
});
$('input:checkbox').click(function() {
// Get the input from the form
var fieldid = $(this).attr('id');
if ($(this).attr('checked') == 'checked') {
var fieldvalue = 'on';
} else {
var fieldvalue = 'off';
}
$('#' + fieldid + 'hint').html(loading);
$.ajax ({
url: 'jqueryvalidate.php',
data: {field: fieldid, value: fieldvalue},
type: 'post',
cache: false,
success: function(message) {
if (message != true) {
// Error was returned, show a cross
$('#' + fieldid + 'hint').html(cross + message);
} else {
// No error, show a tick
$('#' + fieldid + 'hint').html(tick);
}
}
});
});
As you can see, this has two different functions for the validation: one for text, password and email fields and another for checkboxes. Could this be tidied up a little?
Finally, an example form (mypage.php)
if($_SERVER['REQUEST_METHOD'] == "POST") {
$errors = array();
// Form has been submitted
foreach($_POST as $key => $value) {
$$key = $value; // Set the title of the field to its name
if (validate_input($key, $value) != 'true') {
// Value is invalid
$error['message'] = validate_input($key, $value);
$error['name'] = $key;
array_push($errors, $error);
}
}
if (!isset($_POST['termsandconditions'])) {
// This could be improved
if (validate_input('termsandconditions', 'off') != 'true') {
// Value is invalid
$error['message'] = validate_input('termsandconditions', 'off');
$error['name'] = 'termsandconditions';
array_push($errors, $error);
}
}
if (count($errors) > 0) {
// Input data failed validation
showErrors($errors);
} else {
// The form has successfully validated
}
<form action="mypage.php" method="POST">
<label for="displayname">*Display Name: </label>
<input type="text" name="displayname" id="displayname" <? if (isset($_POST['displayname'])) echo 'value="' . $_POST['displayname'] . '"' ?> ><br>
<div id="displaynamehint"></div><br>
<label for="email">*Email: </label>
<input type="email" name="email" id="email" <? if (isset($_POST['email'])) echo 'value="' . $_POST['email'] . '"' ?> ><br>
<div id="emailhint"></div><br>
<label for="password">*Password: </label>
<input type="password" name="password" id="password"><br>
<div id="passwordhint"></div><br>
<label for="twitter">Twitter Username: </label>
<span class="twitterinput">@<input type="text" name="twitter" id="twitter" <? if (isset($_POST['twitter'])) echo 'value="' . $_POST['twitter'] . '"' ?> ></span><br>
<div id="twitterhint"></div><br>
<input type="checkbox" name="termsandconditions" id="termsandconditions" <? if (isset($_POST['termsandconditions'])) echo 'checked="checked"'?> ><label for="termsandconditions">*I have fully read, understand and agree to <a href="termsandconditions.php" target="_blank">the terms and conditions</a></label><div id="termsandconditionshint"></div><br>
<input type="submit" name="submit" value="Submit">
</form>
if ($a)
orif ($a === true)
orif (!$a)
orif ($a === false)
(if you know it's a boolean, I'd tend towards the===
options). Pass your DB instance to the validate_input function. You're unnecessarily tying your validate_input function to mysqlConnect(). A random function should not be responsible for setting up a DB connection. And lastly, validate_input is doing way too much. Try to break it into smaller logical functions. \$\endgroup\$