My first contact form

I read one of SitePoint’s articles regarding anti-spam and user authentication. I have made a custom e-mail contact form using the “honeypot” method of hiding a field with CSS that only a spam bot should be able to see (and subsequently try to fill). This is for a client whose site will be low traffic and only PHP is being utilized on the site (no SQL of any kind).

I’m relatively new to PHP so I would appreciate it if someone could take some time to give me some feedback.

The contact form: http://nupper.heliohost.org/version3/contact.php

Here’s the PHP:


//Initialize $errors array and $vars
$fn = $ln = $e = $e_raw = $m = NULL;
$errors = array();
//http://www.regular-expressions.info/email.html
$regex_email = '!^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$!i';

if(isset($_POST['submitted']) AND empty($_POST['e_tob'])) {	//Begin SUBMITTED 'if'
	//Validate first name
	if(get_magic_quotes_gpc()) {
		if(!empty($_POST['fn'])) {
			$fn = strip_tags(trim(stripslashes($_POST['fn'])));
		} else {
			$errors['fn'] = ' You forgot to enter your first name.';
		}
	} else {
		if(!empty($_POST['fn'])) {
			$fn = strip_tags(trim($_POST['fn']));
		} else {
			$errors['fn'] = ' You forgot to enter your first name.';
		}
	}

	//Validate last name
	if(get_magic_quotes_gpc()) {
		if(!empty($_POST['ln'])) {
			$ln = strip_tags(trim(stripslashes($_POST['ln'])));
		} else {
			$errors['ln'] = ' You forgot to enter your last name.';
		}
	} else {
		if(!empty($_POST['ln'])) {
			$ln = strip_tags(trim($_POST['ln']));
		} else {
			$errors['ln'] = ' You forgot to enter your last name.';
		}
	}

	//Validate e-mail
	if(get_magic_quotes_gpc()) {
		if(!empty($_POST['e'])) {
			$e_raw = strip_tags(trim(stripslashes($_POST['e'])));
			if(preg_match($regex_email, $e_raw)) {
				$e = $e_raw;
				$e_raw = NULL;
			} else {
				$errors['e_regex'] = ' Please enter a valid e-mail address.';
			}
		} else {
			$errors['e'] = ' You forgot to enter your e-mail address.';
		}
	} else {
		if(!empty($_POST['e'])) {
			$e_raw = strip_tags(trim($_POST['e']));
			if(preg_match($regex_email, $e_raw)) {
				$e = $e_raw;
				$e_raw = NULL;				
			} else {
				$errors['e_regex'] = ' Please enter a valid e-mail address.';
			}
		} else {
			$errors['e'] = ' You forgot to enter your e-mail address.';
		}
	}

	//Validate message
	if(get_magic_quotes_gpc()) {
		if(!empty($_POST['m'])) {
			$m = strip_tags(trim(stripslashes($_POST['m'])));
		} else {
			$errors['m'] = ' You forgot to enter a message.';
		}
	} else {
		if(!empty($_POST['m'])) {
			$m = strip_tags(trim($_POST['m']));
		} else {
			$errors['m'] = ' You forgot to enter a message.';
		}
	}
} elseif(isset($_POST['submitted']) AND !empty($_POST['e_tob'])) {	//!trela toB
	header('Location: http://www.youtube.com/watch?v=e1dvSlvZLG8');
	ob_end_clean();
	die();
}	//End SUBMITTED 'if'

if(isset($_POST['submitted']) && !$errors) {
	//Set timezone for Edmonton
	date_default_timezone_set('America/Edmonton');
	//Prepare the e-mail
	$time = date('g:i a (T)');
	$date = date('l F j, Y');
	$fullname = $fn . ' ' . $ln;
	$body = "blah blah blah";
	$body = wordwrap($body, 70);
	//Send the e-mail
	mail('me@email.com', 'Contact Form Submission', $body, "From:no-reply@email.com");
	echo "\	\	\	\	\	";
	echo '<p style="margin:15px 0;">You have successfully sent your message!</p>';
	//Reset $vars & $_POST array to reset the form data
	$fn = $ln = $e = $e_raw = $m = NULL;
	$_POST = array();
} elseif(isset($_POST['submitted']) && $errors) {
	echo "\	\	\	\	\	";
	echo '<p style="margin:15px 0; color:#FFFF00;">Oops! You made a small mistake in the form!</p>';
} else {
	echo "\	\	\	\	\	";
	echo '<p style="margin:15px 0;">Please fill out every field in the form below.</p>';
}

The HTML form:

<form action="" method="post">
					<p><input type="text" name="fn" value="<?php if_isset($fn); ?>" size="20" maxlenght="40" /><span style="color:#FFFF00; font-weight:900;"><?php if_isset('fn', $errors, 'TRUE'); ?></span></p>
	
					<p><input type="text" name="ln" value="<?php if_isset($ln); ?>" size="40" maxlength="40" /><span style="color:#FFFF00; font-weight:900;"><?php if_isset('ln', $errors, 'TRUE'); ?></span></p>
	
					<p><input type="text" name="e" value="<?php if_isset($e); ?>" size="40" maxlength="80" /><span style="color:#FFFF00; font-weight:900;"><?php if_isset('e', $errors, 'TRUE'); if_isset('e_regex', $errors, 'TRUE'); ?></span></p>
	
					<p class="retlif_tob">Confirm E-mail: <input type="text" name="e_tob" size="40" maxlength="80" /></p>
					<br />
					<p><textarea name="m" rows="10" cols="60"><?php if_isset($m); ?></textarea><span style="color:#FFFF00; font-weight:900; vertical-align:top;"><?php if_isset('m', $errors, 'TRUE'); ?></span></p>
					<br />
					<input type="hidden" name="submitted" value="TRUE" />
					<p><input type="submit" name="submit" value="Send E-mail" id="submit" alt="Send E-mail to the Clay Guys" title="Send E-mail to the Clay Guys" /></p>
				</form>

The custom function used in the HTML form:

// if_isset() -- Custom if(isset()) function
function if_isset($var, $array = NULL, $true='FALSE') {
	if($true == 'FALSE'){
		if(isset($var)) echo $var;
	} else {
		if(isset($array[$var])) echo $array[$var];
	}
}

Thank you :smiley:

How about reducing some duplication?

The following code:


if(get_magic_quotes_gpc()) {
	if(!empty($_POST['e'])) {
		$e_raw = strip_tags(trim(stripslashes($_POST['e'])));
		if(preg_match($regex_email, $e_raw)) {
			$e = $e_raw;
			$e_raw = NULL;
		} else {
			$errors['e_regex'] = ' Please enter a valid e-mail address.';
		}
	} else {
		$errors['e'] = ' You forgot to enter your e-mail address.';
	}
} else {
	if(!empty($_POST['e'])) {
		$e_raw = strip_tags(trim($_POST['e']));
		if(preg_match($regex_email, $e_raw)) {
			$e = $e_raw;
			$e_raw = NULL;				
		} else {
			$errors['e_regex'] = ' Please enter a valid e-mail address.';
		}
	} else {
		$errors['e'] = ' You forgot to enter your e-mail address.';
	}
}

can easily be updated to the following:


$e = filter_input(INPUT_POST, 'e', FILTER_VALIDATE_EMAIL);
if ($e === false) {
	$errors['e_regex'] = ' Please enter a valid e-mail address.';
} else {
	if(get_magic_quotes_gpc()) {
		$e = stripslashes($e);
	}
	$e = strip_tags(trim($e);
	if (empty($e)) {
		$errors['e'] = ' You forgot to enter your e-mail address.';
	}
}

Yeah, good point. I’ve been wanting to clean that up a bit.

Thanks :smiley:

I would combine all the code involving get_magic_quotes_gpc(), so you basically get:


if(isset($_POST['submitted']) AND empty($_POST['e_tob']))
{
   if (get_magic_quotes_gpc())
   {
      $_POST['ln'] = stripslashes($_POST['ln']);
      $_POST['fn'] = stripslashes($_POST['fn']);
      // etc
   }
 
   // check fn

   // check ln
}

That would take away a lot of repetition in the code, since you now have each block twice. Once for when get_magic_quotes_gpc() is enabled, and once for where it is disabled.
My approach above just counter-effects the effects of get_magic_quotes_gpc() and after that assumes it is disabled (or counter-effected, which has the same result).

Wow, thanks! I haven’t learned filter inputs yet, but I’ll give it a go.

So, given that it’s a low traffic site and there’s no SQL whatsoever, is this an decent way to filter spam bots (using the honeypot method)?

Thanks again! :smiley:

filter_input has no issues with utf-8 data. It also has a wide range of [url=“http://www.php.net/manual/en/filter.filters.php”]filters to validate and sanitize the data in different ways.

Is filter input utf8 safe? So if you have your page/form data encoded in utf8 will filter_input break it using ISO or something?

Sorry for the necropost, but I was bored and took another shot at trimming some more fat off this script.

It’s been tested and it still functions as intended. Except for using filters, any more suggestions on getting this SOB even thinner? :slight_smile:

<?php
// cleanUserInput() -- Cleans user input dependent on whether MQ is enabled
function cleanUserInput($array) {
	// Initialize clean array
	$cleanInputArray = array();
	// Run through each element in the array
	foreach($array as $key => $value) {
		if(get_magic_quotes_gpc()) {
			$value = strip_tags(trim(stripslashes($value)));
			$cleanInputArray[$key] = $value;
		} else {
			$value = strip_tags(trim($value));
			$cleanInputArray[$key] = $value;
		}
	}
	return $cleanInputArray;
}

//Initialize $errors array and $vars
$fn = $ln = $e = $e_raw = $m = NULL;
$errors = array();
$cleanFields = array();
//http://www.regular-expressions.info/email.html
$regex_email = '!^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$!i';

if(isset($_POST['submitted']) AND empty($_POST['e_tob'])) {	//Begin SUBMITTED 'if'
	//Clean any user input
	$cleanFields = cleanUserInput($_POST);
	//Assign cleaned input to $vars
	$fn = $cleanFields['fn'];
	$ln = $cleanFields['ln'];
	$e_raw = $cleanFields['e'];
	$m = $cleanFields['m'];

	//Check for first name
	if(empty($_POST['fn'])) {
		$errors['fn'] = '<span class="error">Please enter your first name</span>';
	}

	//Check for last name
	if(empty($_POST['ln'])) {
		$errors['ln'] = '<span class="error">Please enter your last name</span>';
	}
	
	//Check for e-mail address
	if(empty($_POST['e'])) {
		$errors['e'] = '<span class="error">Please enter your e-mail address</span>';
	} elseif(preg_match($regex_email, $e_raw)) { //Check for valid e-mail address
			$e = $e_raw;
			$e_raw = NULL;
	} else {
			$errors['e_regex'] = '<span class="error">Please enter a valid e-mail address</span>';
	}
	
	//Check for message
	if(empty($_POST['m'])) {
		$errors['m'] = '<span class="error">Please enter a message</span>';
	}
} elseif(isset($_POST['submitted']) AND !empty($_POST['e_tob'])) {	//!trela toB
	//Bot alert so send them to Batman on drugs via Youtube
        header('Location: http://www.youtube.com/watch?v=e1dvSlvZLG8');
	ob_end_clean();
	die();
}	//End SUBMITTED 'if'

//If the form was submitted AND no errors then send the e-mail
if(isset($_POST['submitted']) && !$errors) {
	//Set timezone
	date_default_timezone_set('America/Vancouver');
	//Prepare the e-mail
	$time = date('g:i a (T)');
	$date = date('l F j, Y');
	$fullname = $fn . ' ' . $ln;
	$body =	"
Dear So and so,

You have received an e-mail from Your Website contact form.
It was sent at $time on $date.

Name: $fullname
E-mail Address: $e
Message:
------
$m
------

This is an automated e-mail.
You may reply to the sender at $e.
			";
	$body = wordwrap($body, 70);
	//Send the e-mail
	mail('email@here.com', 'Your Website Contact Form Submission', $body, "From:no-reply@yourwebsite.com");
	echo '<p>You have successfully sent your message!</p>';
	//Reset $vars & arrays to clear the form data
	$fn = $ln = $e = $e_raw = $m = NULL;
	$_POST = array();
    $cleanFields = array();
//If the form was submitted, but with error(s)
} elseif(isset($_POST['submitted']) && $errors) {
	echo '<p>Oops! You made a small mistake in the form!</p>';
//If the form was not submitted yet
} else {
	echo '<p>Please fill out every field in the form below</p>';
}
?>

I compacted the cleanUserInput function and shuffled things around a bit so the flow is more logical. Well, it is to me anyway :slight_smile:


// cleanUserInput() -- Cleans user input dependent on whether MQ is enabled
function cleanUserInput($array) {
	// Run through each element in the array
	foreach ($array as $key => &$value) {
		if (get_magic_quotes_gpc ()) {
			$value = strip_tags(trim(stripslashes($value)));
		} else {
			$value = strip_tags(trim($value));
		}
	}
	return $array;
}

//Initialize $errors array and $vars
$fn = $ln = $e = $e_raw = $m = NULL;
$errors = array();
$cleanFields = array();
//http://www.regular-expressions.info/email.html
$regex_email = '!^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$!i';

if (isset($_POST['submitted']) && !empty($_POST['e_tob'])) { //!trela toB
	//Bot alert so send them to Batman on drugs via Youtube
	header('Location: http://www.youtube.com/watch?v=e1dvSlvZLG8');
	ob_end_clean();
	die();
} //End SUBMITTED 'if'
elseif (isset($_POST['submitted']) && empty($_POST['e_tob'])) { //Begin SUBMITTED 'if'
	//Clean any user input
	$cleanFields = cleanUserInput($_POST);
	//Assign cleaned input to $vars
	$fn = $cleanFields['fn'];
	$ln = $cleanFields['ln'];
	$e_raw = $cleanFields['e'];
	$m = $cleanFields['m'];

	//Check for first name
	if (empty($_POST['fn'])) {
		$errors['fn'] = '<span class="error">Please enter your first name</span>';
	}

	//Check for last name
	if (empty($_POST['ln'])) {
		$errors['ln'] = '<span class="error">Please enter your last name</span>';
	}

	//Check for e-mail address
	if (empty($_POST['e'])) {
		$errors['e'] = '<span class="error">Please enter your e-mail address</span>';
	} elseif (preg_match($regex_email, $e_raw)) { //Check for valid e-mail address
		$e = $e_raw;
		$e_raw = NULL;
	} else {
		$errors['e_regex'] = '<span class="error">Please enter a valid e-mail address</span>';
	}

	//Check for message
	if (empty($_POST['m'])) {
		$errors['m'] = '<span class="error">Please enter a message</span>';
	}

	if (count($errors) > 0) {
		// One or more errors occured
		echo '<p>Oops! You made a small mistake in the form!</p>';
	} else {
		//Set timezone
		date_default_timezone_set('America/Vancouver');
		//Prepare the e-mail
		$time = date('g:i a (T)');
		$date = date('l F j, Y');
		$fullname = $fn . ' ' . $ln;
		// don't wrap text around like you did, it introduces a bunch of unnessary spaces everywhere
		$body = wordwrap(
				"Dear So and so,\
\
You have received an e-mail from Your Website contact form.\
It was sent at $time on $date.\
\
Name: $fullname\
E-mail Address: $e\
Message:\
------\
$m\
------\
\
This is an automated e-mail.\
You may reply to the sender at $e.\
",
				70
		);
		//Send the e-mail
		mail('email@here.com', 'Your Website Contact Form Submission', $body, "From:no-reply@yourwebsite.com");
		echo '<p>You have successfully sent your message!</p>';
		//Reset $vars & arrays to clear the form data
		$fn = $ln = $e = $e_raw = $m = NULL;
		$_POST = array();
		$cleanFields = array();
	}
} else {
	echo '<p>Please fill out every field in the form below</p>';
}

If you have any questions, just ask :slight_smile:

EDIT: Nevermind, I figured it out :slight_smile:

Thanks for the help!

With the clean user input function, there is official code for disabling magic quotes over at http://php.net/manual/en/security.magicquotes.disabling.php
If you cannot (or do not want to) disable them via your php.ini, or your .htaccess, the script code at the above link takes care of that side of things for you.

As for the rest of the code, because there is a lot of effort being put in to it I would want to be able to reuse that code at a later date. We could create different functions to help with that, such as a validate function, and a send_mail function.

The main part of the code could be like this:


if (!empty($_POST['e_tob'])) {
    //Bot alert so send them to Batman on drugs via Youtube
    header('Location: http://www.youtube.com/watch?v=e1dvSlvZLG8');
}

if (!isset($_POST['submitted'])) {
    echo '<p>Please fill out every field in the form below</p>';
} else {
    $fieldTypes = array(
        'fn' => 'text',
        'ln' => 'text',
        'e' => 'email',
        'm' => 'text'
    );
    $errorMessages = array(
        'fn' => 'Please enter your first name',
        'ln' => 'Please enter your last name',
        'e' => 'Please enter your e-mail address',
        'e_regex' => 'Please enter a valid e-mail address',
        'm' => 'Please enter a message'
    );
    $errors = validate($_POST, $fieldTypes, $errorMessages);

    if (!empty($errors)) {
        // Add decoration to each error message
        foreach ($errors as $key => $value) {
            $errors[$key] = '<span class="error">' + $errors[$key] + '</span>';
        }
        echo '<p>Oops! You made a small mistake in the form!</p>';
    } else {
        sendMail($_POST);
    }
}

Where the validate and sendEmail parts are separate functions:


function validate(&$fields, $fieldTypes, $errorMessages) {
    $errors = array();
    foreach($fieldTypes as $field => $type) {
        // all fields currently are required
        if (empty($fields[$field])) {
            $errors[$field] = $errorMessages[$field];
        } else {
            $fields[$field] = strip_tags(trim($fields[$field]));
        }
        
        // validate the field
        switch($type) {
            case 'text':
                // any additional sanitization
                break;
            case 'email':
                $regex_email = '!^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}$!i';
                if (!preg_match($regex_email, $fields[$field])) {
                    $errors[$field] = $errorMessages[$field];
                }
                break;
            default:
                // do nothing
        }
    }
    return $errors;
}

It wouldn’t take much either to allow for optional and required fields. You would only need to change ‘fn’ => ‘text’ to be:
‘fn’ => array(‘type’: ‘text’, ‘required’: true)

with a small adjustment to the validate function to retrieve the appropriate data.

The sendMail function just helps to contain all of the email-handling processes in the one area.


function sendMail($fields) {
    date_default_timezone_set('America/Vancouver');
    $time = date('g:i a (T)');
    $date = date('l F j, Y');

    $fn = $fields['fn'];
    $ln = $fields['ln'];
    $e = $fields['e'];
    $m = $fields['m'];
    $fullname = $fn . ' ' . $ln;
    
    $to = 'email@here.com';
    $from = "From:no-reply@yourwebsite.com";
    $subject = 'Your Website Contact Form Submission';
    $message = "Dear So and so,\
\
" .
        "You have received an e-mail from Your Website contact form.\
" .
        "It was sent at $time on $date.\
\
" .
        "Name: $fullname\
" .
        "E-mail Address: $e\
" .
        "Message:\
" .
        "------\
" .
        "$m\
" .
        "------\
\
" .
        "This is an automated e-mail.\
" .
        "You may reply to the sender at $e.\
";
    $body = wordwrap($message, 70);

    //Send the e-mail
    mail($to, $subject, $body, $from);
    echo '<p>You have successfully sent your message!</p>';
}

Nice, I’ll have to save that for my next site.

Cheers! :smiley: