14

I have an HTML form:

<form action='process.php' method='post'>
  <input type='checkbox' name='check_box_1' /> Check me!<br>
</form>

Here is a section from the PHP script process.php:

echo (isset($_POST['check_box_1']))?'Set':'Not set';

The output of the script when the checkbox is set is

Set

But when the checkbox is not set, the output is:

Not set

Why is this? This seems like a very poor design because my PHP script checks a number of $_POST variables to make sure they were passed along to the script. When the $_POST['check_box_1'] value is not set, then how do I know whether the script failed to pass along the value or the checkbox was just not set?

Jeff Atwood
  • 63,320
  • 48
  • 150
  • 153
Nathan Osman
  • 71,149
  • 71
  • 256
  • 361

5 Answers5

49

If you want to overcome this design feature, try this:

<input type="hidden" name="check_box_1" value="0" />
<input type="checkbox" name="check_box_1" value="1" />

This way, you'll always have $_POST['check_box_1'] set in the callback page, and then you can just check its value to see whether they checked it or not. The two inputs have to be in that order though, since the later one will take precedence.

nickf
  • 537,072
  • 198
  • 649
  • 721
5

nickf provided the best answer, but it won't work in the special case when you have multiple checkboxes with the same name. Instead (if you're using jQuery), you can use a fake checkbox to toggle the value of a real, hidden input like so:

<input type="checkbox" onclick="$(this).next().val(this.checked?1:0)"/>
<input type="hidden" name="verified[]"/>

<input type="checkbox" onclick="$(this).next().val(this.checked?1:0)"/>
<input type="hidden" name="verified[]"/>

Note that only the hidden checkboxes are named.

EricP
  • 3,395
  • 3
  • 33
  • 46
5

Only the value of checked checkboxes are submitted to the server. This is an HTML/browser design thing and has nothing to do with PHP. How else do you reliably distinguish a checked checkbox from an unchecked one?

...then how do I know whether the script failed to pass along the value or the checkbox was just not set?

You can trust the browser and PHP to get it right. If it's not set, the box wasn't checked. If you're running some Javascript that submits the form and may get it wrong, it's your responsibility to debug your script.


Regarding "it's bad design":

It's efficient design. There are only two other possibilities, given the limitation that values are send as text only:

  1. Send both the value and the state separately, like checkbox:state=checked&checkbox:value=myvalue. Let's pretend for a second that this would not cause major headaches and ambiguities in what is allowed for input element names and what is not.
  2. Use a magic value to mean "unchecked": checkbox=false. That means I can't make a checkbox like this: <input type="checkbox" name="checkbox" value="false" />

Let's pretend that either solution above would be used and problems associated with them would be solved. That would mean that for every checkbox on the page a POST value would be send. Usually that's not a big deal, but let's imagine a page with 50 checkboxes. Not too unrealistic I'd say. What about one with 200? That's 200 * ~10+ characters extra in HTTP headers for each POST, probably most of them just telling you what the user did not do.

Since this technique was invented when bandwidth was very precious, it's the most efficient to simply not send values of unchecked checkboxes.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • 1
    Easy: `check_box_1=true` and `check_box_1=false` – Nathan Osman Mar 26 '10 at 03:17
  • The problem is that some nefarious bot roaming the web may request the page - and since it would request the page without the checkbox field set, I would have to treat it just like a legitimate user that submitted the form with the checkbox unset. – Nathan Osman Mar 26 '10 at 03:19
  • @George The HTTP protocol that this is sent over only sends text which has no type. How do you distinguish a checkbox with value `"false"` from an unset checkbox? – deceze Mar 26 '10 at 03:20
  • @George So...? How can you tell the difference between a bot that didn't check the box and a user that didn't check the box? It's the wrong information to rely on anyway. – deceze Mar 26 '10 at 03:21
  • @deceze: You misunderstand me. The actual POST text would be `check_box_1=true` or `check_box_1=false` – Nathan Osman Mar 26 '10 at 03:23
  • @George Ditto. :) A checkbox can have *any arbitrary text value*. POST data is only *send as text*. That means you'll have to use some specific **magic text value** which means "not checked", but that means you won't be able to use this value anymore to mean "checked". I.e. you couldn't give your checkbox the value `"false"`, because the text value `"false"` in the POST data means the box is not checked. That would cause a lot more headaches than not sending the value at all to mean "unchecked". – deceze Mar 26 '10 at 03:26
3

Unfortunately, that's how the spec for checkboxes in submitted forms works - if the checkbox isn't checked, the browser doesn't necessarily send that field at all.

Amber
  • 507,862
  • 82
  • 626
  • 550
0

Yes, it's bad design, it most certainly is not "efficient" design, here's why:

A checkbox is a control that allows the user to pick zero or more values from a set of one or more options.

By not sending unchecked values, the browser forces the form handler to "know" what it should expect from the form and assume that a missing value means "I do not want that option". This is disastrous when you are updating a record somewhere since you can't rely on the submitted information to be complete.

You essentially don't know if a missing checkbox means "the user does not want that option set" versus "no information was provided from the user". The former requires you to update the record while the latter does not.

This makes partial forms (which allow the user to update part of the record) absolutely impossible without lots of extra work, being particularly nightmarish when your forms have a large number of checkboxes.

You essentially have to explicitly look for information that should be there in the first place, and then infer the user wants it "not set" when it's missing.

If a partial form, one that does not contain the checkboxes, is submitted to the same handler, it will wrongly assume the user wants those options "not set", where in fact there is no information whatsoever on the matter.

This makes code reuse very hard in the backend.

Andre Tannus
  • 79
  • 1
  • 4