12/28/2015
Checking whether a form was posted, in PHP
In plain PHP, test the request method rather than the presence of a field:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// handle the submission
}
In Symfony, or anything else built on HttpFoundation, ask the request:
if ($request->isMethod('POST')) {
// handle the submission
}
Use the framework's version wherever you have one. It is not merely
tidier: $_SERVER is the raw value, and the request object accounts for
the things that change it — a trusted proxy, and the _method override
some clients use to send PUT or DELETE through an HTML form, which only
supports GET and POST.
Why not test for a field
The common version of this check is:
if (isset($_POST['submit'])) { // don't
It fails in two ordinary situations.
A form submitted by pressing Enter may not send the submit button at all. The button is only included when it is the thing that was activated, so a keyboard submission from a text field can arrive without it.
An unticked checkbox is not sent. This is the one that costs an
afternoon. HTML posts nothing for a checkbox that is not checked — no
empty value, no key — so a settings form where the user turned everything
off arrives looking like it was never submitted. Any code shaped like
if (isset($_POST['notify'])) cannot tell "unticked" from "not
submitted", which means it can never turn a setting off.
Testing the method separates the two questions properly: was this a
submission is REQUEST_METHOD, and what did they choose is read
afterwards, with a default:
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$notify = isset($_POST['notify']); // false is a real answer here
}
Use === rather than ==
== is loose comparison. It is unlikely to bite on this particular
string, but there is no reason to accept the risk when the strict operator
is the same length.
Questions this keeps raising
Why does my checkbox never turn off?
Because an unchecked checkbox is not submitted at all - HTML sends no key for it. Code that reads isset($_POST['field']) to decide whether the form was posted cannot distinguish "unticked" from "not submitted", so it never sees the off state. Test REQUEST_METHOD for the submission and read the checkbox separately.
Should I use $_SERVER or the framework's request object?
The framework's, wherever you have one. $_SERVER['REQUEST_METHOD'] is the raw value; Symfony's $request->isMethod() accounts for trusted proxies and for the _method override used to send PUT or DELETE from an HTML form, which natively supports only GET and POST.
Is $_SERVER['REQUEST_METHOD'] safe to trust?
For deciding whether to process a submission, yes - it comes from the request line rather than from a header a client can invent freely. It is not an authorization check, though. Knowing the request was a POST says nothing about who sent it, which is what a CSRF token is for.
What about checking the referer instead?
Do not. The Referer header is optional, routinely stripped by browsers, proxies and privacy settings, and trivially forged. It cannot tell you a form was submitted and it cannot tell you where from.