Ok, you to validate that the user actually enters data. If you don't, $_POST['whatever'] might have nothing (null) or an empty string "". To check this, you might do something like this:
Then just execute(). Do not pass another array in. The values are already set by this point via bindValue. If you do NOT call bindValue, then you WOULD need to pass in the array. So what's happened in your example is, you've bound all the params using bindvalue, then passed in some (but not all) params via execute. This rebinds things, and since the new array is incomplete, some placeholders have no value. Thus, it fails.
// set $firstname = the value in $_POST only if there's a value, otherwise, set a default
$firstname = is_null($_POST['firstname']) ? 'default name' : $_POST['firstname'];
// lines skipped...
// bind the value
// or bind the value with data type info
$statement->bindValue(":firstname", $firstname, PDO::PARAM_STR);
From the manual:
"You cannot bind more values than specified; if more keys exist in input_parameters than in the SQL specified in the PDO:repare(), then the statement will fail and an error is emitted. "