Wanting to make attaching file with a contact form OPTIONAL

Hello & happiest of Fridays!

I was seeking some help in figuring out what is going awry with my current contact form. My PHP understanding is rudimentary at best, however, I’m eager to learn more - every day!

The form I’m using is rather basic for potential employment applications, however, the attachment feature is giving me issues.

~ I've been able to send the form through with an attachment but attaching a file is REQUIRED. It does not complete the 'send' animation nor does it actually come through via email.

~ I recreated the form to send the info through without an attachment and it works, however, when you do try to attach it, whatever file was attached does not appear in the email with the other information.

I’m just wanting a form that has the attachment option as just that: an option. Whether someone attaches their resume or cover letter or not, I want the info/attachments to be forwarded over correctly.

Here is a pasting of the action.php page for the form:

<?php

require dirname(__FILE__)."/validation.php";
require_once ("../../../inc/function.php");

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require 'src/Exception.php';
require 'src/PHPMailer.php';
require 'src/SMTP.php';

class GoogleRecaptcha 
	{
		/* Google recaptcha API url */
		private $google_url = "https://www.google.com/recaptcha/api/siteverify";
		private $secret = 'XXX';

		public function VerifyCaptcha($response)
		{
			$url = $this->google_url."?secret=".$this->secret."&response=".$response;

			$curl = curl_init();
			curl_setopt($curl, CURLOPT_URL, $url);
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
			curl_setopt($curl, CURLOPT_TIMEOUT, 15);
			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
			curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); 
			$curlData = curl_exec($curl);

			curl_close($curl);

			$res = json_decode($curlData, TRUE);
			if($res['success'] == 'true') 
				return TRUE;
			else
				return FALSE;
		}
	}

/************************************************/
/* Your data */
/************************************************/

	/* Your email goes here */
	$your_email = "$site_email";

	/* Your name or your company name goes here */
	$your_name = "$site_company_name";

	/* Message subject */
	$your_subject = "New Job Applicant For $site_company_name";

/************************************************/
/* Settings */
/************************************************/

	/* Select validation for fields */
	/* If you want to validate field - true, if you don't - false */
	$validate_firstname		= true;
	$validate_middlename	= true;
	$validate_lastname		= true;
	$validate_address		= true;
	$validate_city			= true;
	$validate_state			= true;
	$validate_zipcode		= true;
	$validate_email			= true;
	$validate_phone			= true;
	$validate_startdate		= true;
	#$validate_message		 = true;

	/* Select the action */
	/* If you want to do the action - true, if you don't - false */
	$send_letter = true;

/************************************************/
/* Variables */
/************************************************/

	/* Error variables */
	$error_text		= array();
	$error_message	= '';

	/* POST data */
	$firstname	= (isset($_POST["firstname"]))		? strip_tags(trim($_POST["firstname"]))		: false;
	$middlename	= (isset($_POST["middlename"]))		? strip_tags(trim($_POST["middlename"]))	: false;
	$lastname	= (isset($_POST["lastname"]))		? strip_tags(trim($_POST["lastname"]))		: false;
	$address	= (isset($_POST["address"]))		? strip_tags(trim($_POST["address"]))		: false;
	$city	 	= (isset($_POST["city"]))			? strip_tags(trim($_POST["city"]))			: false;
	$state	 	= (isset($_POST["state"]))			? strip_tags(trim($_POST["state"]))			: false;
	$zipcode	= (isset($_POST["zipcode"]))		? strip_tags(trim($_POST["zipcode"]))		: false;
	$email	 	= (isset($_POST["email"]))			? strip_tags(trim($_POST["email"]))		 	: false;
	$phone	 	= (isset($_POST["phone"]))			? strip_tags(trim($_POST["phone"]))		 	: false;
	$startdate	= (isset($_POST["startdate"]))		? strip_tags(trim($_POST["startdate"]))		: false;
	#$message 	 = (isset($_POST["message"]))		 ? strip_tags(trim($_POST["message"]))		 : false;

	$firstname	= htmlspecialchars($firstname, ENT_QUOTES, 'UTF-8');
	$middlename	= htmlspecialchars($middlename, ENT_QUOTES, 'UTF-8');
	$lastname	= htmlspecialchars($lastname, ENT_QUOTES, 'UTF-8');
	$address	= htmlspecialchars($address, ENT_QUOTES, 'UTF-8');
	$city	 	= htmlspecialchars($city, ENT_QUOTES, 'UTF-8');
	$state	 	= htmlspecialchars($state, ENT_QUOTES, 'UTF-8');
	$zipcode	= htmlspecialchars($zipcode, ENT_QUOTES, 'UTF-8');
	$email	 	= htmlspecialchars($email, ENT_QUOTES, 'UTF-8');
	$phone	 	= htmlspecialchars($phone, ENT_QUOTES, 'UTF-8');
	$startdate	= htmlspecialchars($startdate, ENT_QUOTES, 'UTF-8');
	#$message 	 = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');

	$firstname	= substr($firstname, 0, 50);
	$middlename	= substr($middlename, 0, 50);
	$lastname	= substr($lastname, 0, 50);
	$address	= substr($address, 0, 100);
	$city	 	= substr($city, 0, 50);
	$state	 	= substr($state, 0, 25);
	$zipcode	= substr($zipcode, 0, 50);
	$email	 	= substr($email, 0, 40);
	$phone	 	= substr($phone, 0, 25);
	$startdate	= substr($startdate, 0, 50);
	#$message 	 = substr($message, 0, 2000);

	if(isset($_POST['g-recaptcha-response']))
	$captcha=$_POST['g-recaptcha-response'];
	if(!$captcha){
		echo '<b>Please check the the captcha form.</b>';
	}

/************************************************/
/* Validation */
/************************************************/

	/* First Name */
	if ($validate_firstname){
		$result = validateFirstName($firstname, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Middle Name */
	if ($validate_middlename){
		$result = validateMiddleName($middlename, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Last Name */
	if ($validate_lastname){
		$result = validateLastName($lastname, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Address */
	if ($validate_address){
		$result = validateAddress($address, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* City */
	if ($validate_city){
		$result = validateCity($city, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* State */
	if ($validate_state){
		$result = validateState($state, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Zip Code */
	if ($validate_zipcode){
		$result = validateZipCode($zipcode, 1);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Email */
	if ($validate_email){
		$result = validateEmail($email);
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Phone */
	if ($validate_phone){
		$result = validatePhone($phone);
		if ($result !== "valid") {
				$error_text[] = $result;
			}
	}

	/* Start Date */
	if ($validate_startdate){
		$result = validateStartDate($startdate, "/");
		if ($result !== "valid") {
			$error_text[] = $result;
		}
	}

	/* Message */
	//if ($validate_message){
	//	$result = validateMessage($message, 20);
	//	if ($result !== "valid") {
	//		$error_text[] = $result;
	//	}
	//}

	/* If validation error occurs */
	if ($error_text) {
		foreach ($error_text as $val) {
			$error_message .= '<li>' . $val . '</li>';
		}
		echo '<div class="error-message unit"><i class="fa fa-close"></i>Oops! The following errors occurred:<ul>' . $error_message . '</ul></div>';
		exit;
	}

$mail = new PHPMailer(true);

$response = $_POST['g-recaptcha-response'];

if( !empty($response) ){
		$cap = new GoogleRecaptcha();
		$send_letter = $cap->VerifyCaptcha($response);

		if ($send_letter) {
			#if (array_key_exists('userfile', $_FILES)) {}
				//First handle the upload
				//Don't trust provided filename - same goes for MIME types
				//See http://php.net/manual/en/features.file-upload.php#114004 for more thorough upload validation
				//Extract an extension from the provided filename
				#$ext = PHPMailer::mb_pathinfo($_FILES['userfile']['name'], PATHINFO_EXTENSION);
				//Define a safe location to move the uploaded file to, preserving the extension
				#$uploadfile = tempnam(sys_get_temp_dir(), hash('sha256', $_FILES['userfile']['name'])) . '.' . $ext;

				#if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {}
					try {
						//Recipients
						$mail->setFrom('[email protected]', $name);
						$mail->addAddress($site_email);               //Name is optional
						$mail->addReplyTo($email, 'Customer');
						#$mail->addCC('[email protected]');
						$mail->addBCC($company_email, 'VMS Data, LLC');

						//Upload File
						#if (!$mail->addAttachment($uploadfile, 'My uploaded file')) {
						#	$msg .= 'Failed to attach file ' . $_FILES['userfile']['name'];
						#}

						//Content
						require dirname(__FILE__)."/message.php";
						$mail->isHTML(true);                                  //Set email format to HTML
						$mail->Subject = $your_subject;
						$mail->Body    = $letter;
						$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

						$mail->send();
						echo '<div class="success-message unit"><i class="fa fa-check"></i>Your message has been sent</div>';
					} catch (Exception $e) {
						echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
					}
				// } Uncomment when upload file if statement is in use
			}
		}
	// } Uncomment when upload file if statement is in use
?>

Here is the validation.php page:

<?php

/******************************************************/
/* Validation methods */
/******************************************************/
	/* First Name */
	function validateFirstName($firstname, $min_length) {
		$error_text = "Enter your first name";
		$len = mb_strlen($firstname, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* Middle Name */
	function validateMiddleName($middlename, $min_length) {
		$error_text = "Enter your middle name";
		$len = mb_strlen($middlename, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* Last Name */
	function validateLastName($lastname, $min_length) {
		$error_text = "Enter your last name";
		$len = mb_strlen($lastname, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* Address */
	function validateAddress($address, $min_length) {
		$error_text = "Enter your address";
		$len = mb_strlen($address, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* City */
	function validateCity($city, $min_length) {
		$error_text = "Enter your city";
		$len = mb_strlen($city, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* State */
	function validateState($state, $min_length) {
		$error_text = "Enter your state";
		$len = mb_strlen($state, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}
	
	/* Zip Code */
	function validateZipCode($zipcode, $min_length) {
		$error_text = "Enter your zip code";
		$len = mb_strlen($zipcode, 'UTF-8');
		return ($len < $min_length) ? $error_text : "valid";
	}

	/* Email */
	function validateEmail($email){
		$error_text = "No capitalization in the email field to reduce robot traffic";
		$email_template = "/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/";
		return (preg_match($email_template, $email) !== 1) ? $error_text : "valid";
	}

	/* Phone */
	function validatePhone($phone) {
		$error_text = "Phone format: (xxx) xxx-xxxx";
		$phone_template = "/^\([0-9]{3}\) [0-9]{3}-[0-9]{4}$/";
		return (preg_match($phone_template, $phone) !== 1) ? $error_text : "valid";
	}
	
	/* Date */
	function validateStartDate($startdate, $symbol) {
		$error_text = "Start Date Format: mm" . $symbol . "dd" . $symbol . "yyyy";
		$reg_symbol = '\\'.$symbol;
		$date_template = "/^[0-3]{1}[0-9]{1}" . $reg_symbol . "[0-3]{1}[0-9]{1}" . $reg_symbol . "[0-9]{4}$/";
		return (preg_match($date_template, $startdate) !== 1) ? $error_text : "valid";
	}

	/* Message */
	//function validateMessage($message, $min_length) {
	//	$error_text = "The message is too short - min " . $min_length . " characters";
	//	$len = mb_strlen($message, 'UTF-8');
	//	return ($len < $min_length) ? $error_text : "valid";
	//}

	/* File */
	function validateFile($valid_types) {
		$attach_file_size	= 1*1024*1024;
		$error_exist		= false;
		$error_text			= "File: incorrect extension and/or too big file size";
		if (!empty($_FILES["file"])) {
			if (!in_array($_FILES["file"]["type"], $valid_types)) {
				$error_exist = true;
			}
			if (!is_uploaded_file($_FILES["file"]["tmp_name"])) {
				$error_exist = true;
			}
			if ($_FILES["file"]["size"] > $attach_file_size) {
				$error_exist = true;
			}
			return ($error_exist) ? $error_text : "valid";
		} else {
			return "Upload some file";
		}
	}

	/* Generate uniq name for file */
	function generateFileName(){
		return uniqid().'-'.strtolower($_FILES["file"]["name"]);
	}

	/* Upload file */
	function uploadFile(){
		$new_file = 'No file to upload.';
		if (!empty($_FILES["file"])) {
			$new_file = generateFileName();
			move_uploaded_file($_FILES["file"]["tmp_name"], '../upload_file/'.$new_file);
		}
		return $new_file;
	}
?>

If there is anything else I can provide, please let me know!

Again, I certainly appreciate any help on this - I’ve been at it for nearly a week and a half with minimal breakthroughs. Hence me being here!

Hope to see some feedback soon - thanks!

All the best,

The post method form processing code needs to detect if a post method form was submitted, at all, before referencing any of the form data. Use -

if($_SERVER['REQUEST_METHOD'] === 'POST')
{
    // all the form processing code goes here...
}

The form processing code and the form should be on the same page. This will allow you to display any validation errors with the form and repopulate the form fields with any existing values so that the user doesn’t need to keep entering data over and over. The code for any page should be laid out in this general order -

  1. initialization
  2. post method form processing
  3. get method business logic - get/produce data needed to display the page
  4. html document

If the total size of the form data exceeds the post_max_size server setting, both the $_POST and $_FILES arrays will be empty. You must detect this condition in your code and setup a message for the user letting them know that the form data could not be processed, and log information about the request so that you (the site owner/developer) will know that this is occurring.

After you have determined that there is form data in $_POST and $_FILES, the point of a captcha is to prevent unnecessary processing. You need to validate the captcha value first. If it’s not valid, setup a message for the user letting them know what is wrong, and skip over the remaining form processing code. In your current code, just echoing a message, you continue to run the rest of the code even when there is a captcha error.

When you get to the point of validating the uploaded file data, you need to test the [‘error’] element, e.g. $_FILES[‘userfile’][‘error’]. If no file was selected, the [‘error’] element will be - UPLOAD_ERR_NO_FILE Value: 4. There’s a list of the error values in the php documentation. If no file was selected, you would skip over the uploaded file validation and processing. The [‘error’] element will be - UPLOAD_ERR_OK Value: 0, for a successfully uploaded file. For the errors that the user can correct, you need to setup an error message letting the user know what went wrong. For the errors that the user cannot correct, you need to setup a general failure message for the user, and log all the information about the request, so that you will know that this is occurring. When you get to the point in the phpmailer code of attaching the file, you would only run the code to attach the file if a file was successfully uploaded, validated, and processed.

For the $_POST data -

  1. Except for unchecked checkbox/radio fields, all fields will be set. There’s no point in using isset() statements for these always-set fields.
  2. You should keep the form data as a set in a php array variable, then operate on elements in this array variable throughout the rest of the code. Do not create discrete variables for every field value. This is just a waste of typing. Keeping the form data as a set will also allow you to dynamically validate and process the data, so that you are are not writing out block after block of code for every field.
  3. You should only trim input data before using it, mainly so that you can detect if a value is all white-space characters. Do not use strip_tags on input data. There are valid email addresses that contain things that look like a html tag, that using strip_tags on will break.
  4. You need to use the form field name as array index in the $error_text[] assignment statements, so that you can perform any dependent validation on a field or if you want to display validation errors adjacent to the field they correspond to.
  5. htmlspecialchars() is an output function. Do not use it on data before validating it or limiting its length, since it changes the meaning of the data. Apply htmlspecialchars() to data right before it gets used in a html context.
  6. Don’t roll your own email validation. Use php’s filter_var() with the FILTER_VALIDATE_EMAIL flag.

One additional point. Email can be unreliable. You should also store the submitted data on the server, either in a database table or log it to a file, so that you have a record of it in case the email doesn’t work.

Sponsor our Newsletter | Privacy Policy | Terms of Service