Q:

PHP contact form

I recently had my site hacked, with the main index file being overwritten (nothing else was touched). I'm assuming they hijacked my form input.
Are there any vulnerabilities in the code or does this look to be safe now?
<?php
  $myemail = "...@gmail.com";
  $subject = "Form Submitted";
  $name = $_POST['name'];
  $email = $_POST['email'];
  $number = $_POST['number'];
  $comments = $_POST['comments'];
  $feedback = "";
  $message  = <<<EMAIL

Name:   $name
Email:  $email

Message:

$comments

EMAIL;

/////////////////////////////////////////////////////////////////////////////
//                          Form input validation
/////////////////////////////////////////////////////////////////////////////
if ($_SERVER["REQUEST_METHOD"] == "POST") {
  if (empty($_POST["name"])) {
    $nameErr = "Name is required";
    $feedback = "";
  } else {
    $name = test_input($_POST["name"]);
    // check if name only contains letters and whitespace
    if (!preg_match("/^[a-zA-Z ]*$/",$name)) {
      $nameErr = "Only letters and white space allowed";
      $feedback = "";
    } else {
      $nameErr = "";
    }
  }

  if (empty($_POST["comments"])) {
    $commentsErr = "Message is required";
    $feedback = "";
  } else {
    $comments = test_input($_POST["comments"]);
    $commentsErr = "";
  }
} // End input validation

/////////////////////////////////////////////////////////////////////////////
//                       Successful form submission
// Neither (name and nameErr) nor (comments and commentsErr) could possibly
// be empty strings. This prevents the form from being submitted as soon as
// the page is loaded. It also clears all fields when the form is successful.
// All field contents will remain until form is successfully submitted.
/////////////////////////////////////////////////////////////////////////////
if($commentsErr == "" && $nameErr == "" && $name != "" && $comments != ""){
  mail($myemail,$subject,$message);
  $feedback = "Thanks for contacting us ! We'll be in touch soon.";
  $_POST['name'] = "";
  $_POST['email'] = "";
  $_POST['number'] = "";
  $_POST['comments'] = "";
}

// Function strips any dangerous characters from input
function test_input($data) {
   $data = trim($data);
   $data = stripslashes($data);
   $data = htmlspecialchars($data);
   return $data;
}

?>

A:

I don't see vulnerabilities here:

The script is not executing shell commands
The script is not executing database queries
The only "external" call is to mail(...), the arguments are reasonably sanitized, and in any case I would expect the mail(...) function to do its own sanitization, and not erase the hard disk if something really evil is embedded $message

But I see some dangerous practices.
You create $message from input that's not yet validated:

  $name = $_POST['name'];
  $email = $_POST['email'];
  $comments = $_POST['comments'];
  $feedback = "";
  $message  = <<<EMAIL

Name:   $name
Email:  $email

Message:

$comments

EMAIL;

At this point all the $_POST fields are not validated yet.
I don't know if this is intentional,
for example because you want to preserve the original inputs instead of using the sanitized ones.
This is a bit confusing.
The script uses $_POST["something"] repeatedly.
It would be better to create cleaned copies early on,
and work with the cleaned versions,
for example:
$cleaned_name = clean_input($_POST['name']);
$cleaned_email = clean_input($_POST['email']);
$cleaned_comments = clean_input($_POST['comments']);

function clean_input($data) {
   $data = trim($data);
   $data = stripslashes($data);
   $data = htmlspecialchars($data);
   return $data;
}

test_input was not a good name for this purpose, so I renamed it to clean_input.
I find it odd that you clear the values of the $_POST:

  mail($myemail,$subject,$message);
  $feedback = "Thanks for contacting us ! We'll be in touch soon.";
  $_POST['name'] = "";
  $_POST['email'] = "";
  $_POST['comments'] = "";

This seems to suggest that you don't trust the rest of the program might use these values by accident.
It would be better to reorganize your code to render such accidental uses impossible,
something you can trust, so you don't need to write paranoid code.
For example it would be better to reorganize your code to functions,
and have a better control of the flow of the program and the actions taken.
A common practice is to use a Form class,
so that you can have a main flow like this:
$form = new Form;
if ($form->is_valid()) {
    // send email, taking values from $form->cleaned_data
} else {
    // print errors, taking values from $form->errors
}

