Age Calculation
Listed In PHP and MySQL » Systems and Features — Viewing Full TutorialYou'll need to consider three main aspects of your age calculation "module", which are:
- The form
- The processing
- The conditional output: for example, where should they be redirected if they're under or over the required age?
The best way to start off is with your front end. This will just be a simple form with selects for day, month and year. Since writing out endless amounts of <option> tags is depressingly tedious, the code is provided below. It's over 100 lines, so get your scoll wheel at the ready...
<form name="age" id="age" method="post" action="">
<label for="day">Please enter your age:</label>
<select id="day" name="day">
<option selected="selected" value="">Day</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
<option value="19">19</option>
<option value="20">20</option>
<option value="21">21</option>
<option value="22">22</option>
<option value="23">23</option>
<option value="24">24</option>
<option value="25">25</option>
<option value="26">26</option>
<option value="27">27</option>
<option value="28">28</option>
<option value="29">29</option>
<option value="30">30</option>
<option value="31">31</option>
</select>
<select id="month" name="month">
<option selected="selected" value="">Month</option>
<option value="1">January</option>
<option value="2">February</option>
<option value="3">March</option>
<option value="4">April</option>
<option value="5">May</option>
<option value="6">June</option>
<option value="7">July</option>
<option value="8">August</option>
<option value="9">September</option>
<option value="10">October</option>
<option value="11">November</option>
<option value="12">December</option>
</select>
<select id="year" name="year">
<option selected="selected" value="">Year</option>
<option value="1995">1995</option>
<option value="1994">1994</option>
<option value="1993">1993</option>
<option value="1992">1992</option>
<option value="1991">1991</option>
<option value="1990">1990</option>
<option value="1989">1989</option>
<option value="1988">1988</option>
<option value="1987">1987</option>
<option value="1986">1986</option>
<option value="1985">1985</option>
<option value="1984">1984</option>
<option value="1983">1983</option>
<option value="1982">1982</option>
<option value="1981">1981</option>
<option value="1980">1980</option>
<option value="1979">1979</option>
<option value="1978">1978</option>
<option value="1977">1977</option>
<option value="1976">1976</option>
<option value="1975">1975</option>
<option value="1974">1974</option>
<option value="1973">1973</option>
<option value="1972">1972</option>
<option value="1971">1971</option>
<option value="1970">1970</option>
<option value="1969">1969</option>
<option value="1968">1968</option>
<option value="1967">1967</option>
<option value="1966">1966</option>
<option value="1965">1965</option>
<option value="1964">1964</option>
<option value="1963">1963</option>
<option value="1962">1962</option>
<option value="1961">1961</option>
<option value="1960">1960</option>
<option value="1959">1959</option>
<option value="1958">1958</option>
<option value="1957">1957</option>
<option value="1956">1956</option>
<option value="1955">1955</option>
<option value="1954">1954</option>
<option value="1953">1953</option>
<option value="1952">1952</option>
<option value="1951">1951</option>
<option value="1950">1950</option>
<option value="1949">1949</option>
<option value="1948">1948</option>
</select>
<input type="submit" value="Enter" name="checkage" id="checkage" />
</form>
A few notes about this code:
- There are default selected options at the top to describe to the user what's what. Later on we'll come to realise their usefulness as they have empty values.
- The minimum age (as of writing in 2007) is 12 as the maximum year is 1995. The maximum age is old enough!
- The <label> element will autofocus the user on the day <select> box if clicked in a modern browser, as it includes a for attribute.
- The form's action attribute points nowhere, so by default it will submit to the same page it was on. Therefore, we're going to put the processing code on the same page as this one, but at the top of the page.
So we've got the age form on our page somewhere - hopefully in the right place. Assuming this is an entry page, sort of like Smirnoff's website (mmm, vodka), we're now going to start writing some code at the top of the page before any HTML output.
The reason we're putting the code here is because the user will be redirected using the header() PHP command. Headers must all be sent before any output is sent to the browser in the form of HTML, plain text or anything else. We'll also be setting a cookie for the user to verify his or her age for future visits, so they won't have to fill in that form again.
We're now going to do a check for our form submission. This is done by checking for the submit button's presence as a value in the $_POST array. If it's there, that means the form has been $_POSTed, or submitted.
In regard to handling this user data, the amount of stripping needed is low as we're dealing with numbers and empty values only. We're not using any of the data in conjunction with a database either, which takes a lot of pressure off you as a developer when securing the script.
<?php
if(isset($_POST['checkage'])) {
$day = ctype_digit($_POST['day']) ? $_POST['day'] : '';
$month = ctype_digit($_POST['month']) ? $_POST['month'] : '';
$year = ctype_digit($_POST['year']) ? $_POST['year'] : '';
# Wondering what all the ? and : is all about? Read below.
}
?>
Okay, so far we've got something that says in English:
- If the form was posted,
- Set these variables: day, month, year to their $_POSTed equivalents, if they're digits. If not, set them to blank variables.
ctype_digit is an in-built function for PHP which checks whether its input is a digit or not. You could do regular expression matching for [0-9]{1-4} if you liked, but this method is much faster and it's a purpose built function.
If you've never seen the syntax I used to set these variables, it's called "ternary operation". It isn't anything amazing although it does save lines. If you've seen this before and know what it does, skip the next paragraph or two.
The syntax is simple. Let's use the $day setting line as an example.
$day is the variable we'll be setting.
ctype_digit($_POST['day']) is the condition, which returns either true or false.
? $_POST['day'] denotes the value $day will be set to if the condition returns true.
: ''; denotes the value $day will be set to if the condition returns false.
In this context, ? means true and : means false.
In the next step we're going to make a useful date out of these variables.
We can use the function called mktime() to make a Unix timestamp out of the details we have already, which can then be compared to today's current timestamp by calling time().
mktime requires a few arguments which are very straightforward. For a more detailed explanation, there's no place better than PHP.net's page for the function: http://php.net/mktime.
To make our timestamp, here's what we do:
$birthstamp = mktime(0, 0, 0, $month, $day, $year);
Yup, it's painfully(?) simple. Now we just have to compare this with today's timestamp:
$diff = time() - $birthstamp;
We're now left with the difference, in seconds, between the user's birth date and today. However, conviniently, age isn't measured in seconds these days - it's in years. We have to divide $diff by the number of seconds in a year. For those of us, including myself, who don't know the number of seconds in a year, Google has a nice feature where you can convert anything into anything else: search link.
Cheers Google, that's 31556926 seconds.
Now we've got all the data we need to figre out the user's age, we can plug it in:
$age_years = $diff / 31556926;
Now, if you've tried this yourself, or if you have any knowledge of maths whatsoever, you'll know there's a pretty slim chance of you getting an answer that is generated to an appropriate degree of accuracy for use in this scenario, i.e. 2 significant figures.
What function would you use to get around this?
If you're thinking about using round(), you're wrong - sorry! The reason we don't use this is because it will round any number over .5 to the next integer, which isn't what we want, since a user who is 15.63 years old is still 15.
To circumvent this little annoyance, we use floor(), which rounds a floating point number down regardless of place value. So, 15.63 years old would become 15.
$age_years = floor($age_seconds);
Let's now consolidate all the small snippets into our main code block we wrote earlier:
<?php
if(isset($_POST['checkage'])) {
$day = ctype_digit($_POST['day']) ? $_POST['day'] : '';
$month = ctype_digit($_POST['month']) ? $_POST['month'] : '';
$year = ctype_digit($_POST['year']) ? $_POST['year'] : '';
$birthstamp = mktime(0, 0, 0, $month, $day, $year);
$diff = time() - $birthstamp;
$age_years = $diff / 31556926;
$age_years = floor($age_seconds);
}
?>
Now that we've figured out the user's age in years, it's time to do something with it. Let's say we're the webmasters of the local off license and we're obliged by the law to give a preventative method to (admittedly stupid) minors (under 18) from entering and looking at all the pretty pictures of all the beer and the spirits and the cigarrettes that they're not allowed to consume, even though they probably do every weekend anyway. Moving on, *ahem*, let's do something with this.
I mentioned a minor is anybody under 18 years old, at least in the UK, so now we have something to compare $age_years to and work with. The following code will set a cookie describing whether the user is old enough and set the variable $url to
either the "home.html" if the user is old enough, or it will redirect them to "minor.html if they're under 18.
"
if($age_years >= 18) {
setcookie('legal', 'yes', time() + 31556926, '/', '.'.$_SERVER['SERVER_NAME']);
# The above line sets a cookie called "legal" throughout the entire domain and its value is yes. The cookie will expire next year, if it is not manually deleted.
$url = '/home.html';
} else {
setcookie('legal', 'no', time() + 31556926, '/', '.'.$_SERVER['SERVER_NAME']);
# You're not old enough, come back next year!
$url = '/minor.html';
}
header('Location: '.$url.');
That's almost it! The next page gives the full code so far.
This is what we have so far:
<?php
if(isset($_POST['checkage'])) {
$day = ctype_digit($_POST['day']) ? $_POST['day'] : '';
$month = ctype_digit($_POST['month']) ? $_POST['month'] : '';
$year = ctype_digit($_POST['year']) ? $_POST['year'] : '';
$birthstamp = mktime(0, 0, 0, $month, $day, $year);
$diff = time() - $birthstamp;
$age_years = floor($diff / 31556926);
if($age_years >= 18) {
setcookie('legal', 'yes', time() + 31556926, '/', '.'.$_SERVER['SERVER_NAME']);
# The above line sets a cookie called "legal" throughout the entire domain and its value is yes. The cookie will expire next year, if it is not manually deleted.
$url = '/home.html';
} else {
setcookie('legal', 'no', time() + 31556926, '/', '.'.$_SERVER['SERVER_NAME']);
# You're not old enough, come back next year!
$url = '/minor.html';
}
header('Location: '.$url.');
}
?>
Place it at the top of your page and you're *almost* done. If you'd like to have a more detailed play with it, the variable with the age in seconds, which can then be used to generate a more specific output is in $diff.
A cookie was set on the user's computer to skip past any checks in the future. Here's the code that checks the cookie's value and redirects the user accordingly:
<?php
if(isset($_COOKIE['legal'])) { # If the cookie has been set by the script earlier...
$url = ($_COOKIE['legal'] == 'yes') ? '/home.html' : '/minor.html';
header("Location: $url");
}
?>
Again, put this at the top of the page. Whether it's above or below the aforementioned code doesn't matter :)
So, that's it! We've made a quick script that checks a user's age and then redirects them depending on whether they're older than 18 or not.
