Log in
If you're a student, please log in, so you can get the most from this page.
Not graded. So why do it?
Not graded. So why do it?
Not graded. So why do it?
Matt's Ella. A dog, not a horse.
Log in
If you're a student, please log in, so you can get the most from this page.
Goal
Let's make a PHP page that tells you the sales tax rate of a state. It will only work for Michigan and Illinois, though we could extend it easily. You can download it.
Try it with Michigan:
With Illinois:
So this is on the end of the URL: ?state=something
.
What if the state is invalid? Let's see:
As we write the code, let's work out a programming pattern for validation, that we can reuse in other situations.
Top and bottom chunks
Here's a typical PHP page.
- <?php
- // Some code that runs before any HTML has been produced.
- ...
- ?><!DOCTYPE html>
- <html lang="en">
- ...
- <body>
- ...
- <?php
- // Code that runs while HTML is being processed.
- ...
- <body>
- </html>
It's common to make a page as two chunks.
We made a pattern out of this.
Computation at the top, simple output at the bottom
Put code for complex calculations in the top part of your program. That code puts values in variables. The bottom bit makes HTML using those variables.
Let's map our validation code into the two chunks.
- <?php
- // Code that runs before any HTML has been produced
- // Get the user's input
- // Check for errors
- // If there are no errors
- // Process the data
- ?><!DOCTYPE html>
- <html lang="en">
- ...
- <body>
- ...
- <?php
- // If there were errors
- // Show error messages
- // Else
- // Show processing results
- ...
- <body>
- </html>
So the top chunk does input and processing. The bottom chunk does output, either of errors, or the results.
The top chunk has to tell the bottom chunk at least two things:
- Validation errors, if any.
- Processing results.
Sales tax rate
OK, let's write a program that outputs a state's name and sales tax rate, when you give it a state name. For example, with the url something.php?state=mi
, you would get:
The top chunk
The top chunk does two things:
- Data validation
- Processing
We want to do the processing, only if data validation says the data is OK.
- If input data is OK:
- Process the input data
For the sales tax program:
- If input for state is MI or IL:
- Work out the sales tax
Let's bring in the the bottom chunk for a moment. It will need to know what errors there were, and the results of processing.
We'll use variables to tie the two pieces together.
Like this:
- Top:
- If input for state is MI or IL:
- Work out the sales tax
- Bottom:
- If there were errors:
- Output error messages
- Else
- Output state and tax rate
Adela
Wait, where did the error messages come from?
Oh, right. The top bit would work them out:
- Top:
- If not (input for state is MI or IL):
- Put something in an error message variable, like "State is bad"
- Else:
- Work out the sales tax
- Bottom:
- If there were errors:
- Output error messages
- Else
- Output state and tax rate
So an error message variable, and some other variables, tie the top and bottom of the program together. The top bit fills the variables, and the bottom bit uses them.
Several error types
Now, users can make different kinds of mistakes. The state could be missing from the input, if the URL was like this:
Or this:
http://webappexamples.skilling.us/validation/basics/state-sales-tax-rate/state-sales-tax-rate.php
So that's one error to check for: the state is missing.
Another possible error is that the data is there, but is an unknown value.
QLD isn't MI or IL, so there's an error.
Some programs can have many types of errors. Supposed we wanted users to input the number of doggos they have, like this: something.php?doggos=2
Some different types of errors:
- The doggos value is missing:
something.php
, orsomething.php?doggos=
- The doggos value is not a number:
something.php?doggos=some
- The doggos value is negative:
something.php?doggos=-1
- The doggos value is too large:
something.php?doggos=8192
We want to write validation code that can handle all these types of errors.
Notice the dependencies between the error messages. For example, if there is no doggos value, there is no point in checking whether the doggos value is not a number. There's nothing to check!
Another: if the doggos value is not a number, there is no point checking whether it is negative. The concept of "negative" doesn't even apply to a value that is not numeric.
Let's start with this code for the top part of a program like this:
- Get input value
- Do error check 1
- If data passed error check 1:
- Do error check 2
- If data passed error check 1 and 2:
- Do error check 3
- If data passed error check 1, 2, and 3:
- Do error check 4
- If data passed error check 1, 2, 3, and 4:
- Do processing
So the processing (line 10) only happens if all of the check pass. Makes sense. No point in processing data that's faulty.
So, how do we write the code?
Ethan
We'll need variables for all of the error checks, right?
Adela
Maybe... but maybe it could be simpler. Like, take this line...
- If data passed error check 1, 2, 3, and 4:
It doesn't matter which error happened, right? If anything went wrong, we don't want to process.
So maybe:
- Get input value
- Do error check 1
- If no problems so far:
- Do error check 2
- If no problems so far:
- Do error check 3
- If no problems so far:
- Do error check 4
- If no problems so far:
- Do processing
Hey, good idea! We could use a flag variable. Like, maybe:
- // No errors yet.
- $isDataOK = true;
- Get input.
- If error 1 found:
- // Something is wrong.
- $isDataOK = false;
- If $isDataOK then:
- // All OK so far, next error check.
- If error 2 found:
- $isDataOK = false;
- If $isDataOK then:
- // All OK so far, next error check.
- If error 3 found:
- $isDataOK = false;
- If $isDataOK then:
- // All OK so far, next error check.
- If error 4 found:
- $isDataOK = false;
- If $isDataOK then:
- // No errors!
- Process input.
Use a variable as a flag. Set the flag if any of a number of things happens. After, check the flag to see if any of those things happened.
Adela
We still need a variable for the error message, though.
Oh, OK. I forgot.
- $isDataOK = true;
- $errorMessage = '';
- Get input.
- If error 1 found:
- $isDataOK = false;
- $errorMessage = 'Sorry, ...';
- If $isDataOK then:
- If error 2 found:
- $isDataOK = false;
- $errorMessage = 'Sorry, ...';
- If $isDataOK then:
- If error 3 found:
- $isDataOK = false;
- $errorMessage = 'Sorry, ...';
- If $isDataOK then:
- If error 4 found:
- $isDataOK = false;
- $errorMessage = 'Sorry, ...';
- If $isDataOK then:
- Process input.
- Later...
- if (!$isDataOK) {
- print $errorMessage;
- else {
- print output from processing;
- }
Georgina
Wait! Idea! If there are no errors, $errorMessage
will be MT (empty), right?
We could use $errorMessage
as a flag, as well as holding an error message!
Oh, I get it!
- $errorMessage = '';
- Get input.
- If error 1 found:
- // Something is wrong, set error message.
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 2 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 3 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 4 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors! Processing.
- Process input.
- Later...
- if ($errorMessage != '') {
- print $errorMessage;
- else {
- print output from processing;
- }
Nice!
How would that work for the sales tax thing? Remember, the user gives a state, like something.php?state=mi
. If the state is OK, you get:
Here's some code for it.
- <?php
- // Set up vars to track results.
- // Error message is a flag, too.
- $errorMessage = '';
- $taxRate = 0;
- $stateShortName = '';
- $stateLongName = '';
- // Is there input?
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- if ($errorMessage == '') {
- // OK so far.
- $stateShortName = strtoupper(trim($_GET['state']));
- // Is the state known?
- if ($stateShortName != 'MI' && $stateShortName != 'IL') {
- $errorMessage = "Sorry, '$stateShortName' is not a recognized state.";
- }
- }
- if ($errorMessage == '') {
- // Input checks out.
- // Lookup.
- if ($stateShortName == 'MI') {
- $stateLongName = 'Michigan';
- $taxRate = 0.06;
- }
- else {
- $stateLongName = 'Illinois';
- $taxRate = 0.0625;
- }
- }
- ?><!doctype html>
- <html lang="en">
- ...
Let's break it down.
The first few lines set up variables:
- $errorMessage = '';
- $taxRate = 0;
- $stateShortName = '';
- $stateLongName = '';
You don't have to Dim
variables in PHP, but I like to set them all up anyway, to help remind myself what the code is going to do.
OK, there are two types of errors:
- The state is missing from the URL.
- The state is there, but is unknown (only MI and IL are valid).
Let's deal with the first one.
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
isset()
is a PHP function. It's true if its parameter exists. So !isset($_GET['state'])
will be true if something called state
is not at the end of the URL (remember, !
means "not"). If there's no state
, set $errorMessage
.
That's the first validation rule. The second is the state has to be known. Let's add a rule for it, but we only want to check the rule, if there are no errors so far.
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- if ($errorMessage == '') {
- // OK so far.
- $stateShortName = strtoupper(trim($_GET['state']));
- // Is the state known?
- if ($stateShortName != 'MI' && $stateShortName != 'IL') {
- $errorMessage = "Sorry, '$stateShortName' is not a recognized state.";
- }
- }
OK, the fourth line is:
- if ($errorMessage == '') {
We only check the second validation rule (a known state) if the first one (is there a state?) passed. This is important. Check out this line, that's part of the state check.
- $stateShortName = strtoupper(trim($_GET['state']));
$_GET['state']
will crash the program, if there is no state
param at the end of the URL. So, don't run $_GET['state']
, unless we know that the state
param is there. That's what the first validation rule is about. If it passes, $errorMessage
will be MT, so it's safe to $_GET['state']
.
This code pattern is:
- First validation test on variable $v.
- Is everything OK?
- Do the next test on $v.
- Is everything still OK?
- Do the next test on $v.
- How about now?
- Do the next test on $v.
- Still OK?
- Do the next test on $v.
Adela
Instead of...
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- if ($errorMessage == '') {
- // OK so far.
... could it be...
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- else {
- // OK so far.
Aye, it could be. Good thinking. There's no difference in this case. However, when validation gets more complex, with more rules and more fields, it's easier to keep track of things by testing whether $errorMessage
is MT (empty).
Check out this again:
- $stateShortName = strtoupper(trim($_GET['state']));
What's the deal with the strtoupper(trim(
?
Ethan
Same thing we did in VBA. trim()
gets rid of spaces at the start and end of the variable, strtoupper()
makes it uppercase, so we know what to test in the if
:
- if ($stateShortName == 'MI') {
Right! Nice work.
Adela
We could use lowercase, right?
- $stateShortName = strtolower(trim($_GET['state']));
- if ($stateShortName == 'mi') {
Aye, no problem.
After validation, there's processing.
- if ($errorMessage == '') {
- // Input checks out.
- // Lookup.
- if ($stateShortName == 'MI') {
- $stateLongName = 'Michigan';
- $taxRate = 0.06;
- }
- else {
- $stateLongName = 'Illinois';
- $taxRate = 0.0625;
- }
- }
We check the state only if there are no validation errors, that is, if $errorMessage
is MT. That's why we don't need an explicit test for IL. If the state isn't MI, it has to be IL, since the validation code already established that the state is either MI or IL.
The next chunk
Here's the big picture:
The top chunk fill some variables, so that bottom chunk knows what happened. Here's the code for the top chunk:
- <?php
- // Set up vars to track results.
- // Error message is a flag, too.
- $errorMessage = '';
- $taxRate = 0;
- $stateShortName = '';
- $stateLongName = '';
- // Is there input?
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- if ($errorMessage == '') {
- // OK so far.
- $stateShortName = strtoupper(trim($_GET['state']));
- // Is the state known?
- if ($stateShortName != 'MI' && $stateShortName != 'IL') {
- $errorMessage = "Sorry, '$stateShortName' is not a recognized state.";
- }
- }
- if ($errorMessage == '') {
- // Input checks out.
- // Lookup.
- if ($stateShortName == 'MI') {
- $stateLongName = 'Michigan';
- $taxRate = 0.06;
- }
- else {
- $stateLongName = 'Illinois';
- $taxRate = 0.0625;
- }
- }
- ?><!doctype html>
- <html lang="en">
- ...
At the end of this, if all is OK, $errorMessage
will be MT, and $stateLongName
and $taxRate
will have data, ready to output.
If there's a problem, $errorMessage
will have an error message, and the other variables will be meaningless.
The bottom chunk will use the variables filled by the top chunk.
- <h1>Sales tax rate</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>The sales tax rate for $stateLongName is $taxRate.</p>";
- }
- ?>
The bottom chunk handles the output. Either an error message, or the results.
The top chunk prepared the output variables, the bottom chunk showed the output variables, along with the HTML around them.
Another example
The last program took string input, interpreting it as a state code, and did some validation. Let's write a program that takes a number, and does the same sort of thing.
This program simulates a microwave. You tell it how many seconds to run, and it will tell you how hot the food is. OK, I know it's silly, but it's easy to follow.
Try these links, with different values for heating time.
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php?seconds=some
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php?seconds=-33
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php?seconds=10000
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php?seconds=33
http://webappexamples.skilling.us/validation/basics/microwave-time/microwave-time.php?seconds=700
Let's use the same validation approach as before.
- First validation test on variable $v.
- Is everything OK?
- Do the next test on $v.
- Is everything still OK?
- Do the next test on $v.
- How about now?
- Do the next test on $v.
- Still OK?
- Do the next test on $v.
We'll use the variable $errorMessage
as a flag. So the code will be more like:
- $errorMessage = '';
- Get input.
- If error 1 found:
- // Something is wrong, set error message.
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 2 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 3 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors so far. Next check.
- If error 4 found:
- $errorMessage = 'Sorry, ...';
- If $errorMessage == '' then:
- // No errors! Processing.
- Process input.
- Later...
- if ($errorMessage != '') {
- print $errorMessage;
- else {
- print output from processing;
- }
Last time, the tests were about a state code, like MI. This time, there'll be a number.
Here's the code:
- <?php
- // Set up vars to track results.
- // Error message is a flag, and a message container.
- $errorMessage = '';
- // User's input.
- $seconds = 0;
- // Output message. Only worked out if input is OK.
- $output = '';
- // Is there input?
- if (!isset($_GET['seconds'])) {
- $errorMessage = 'Sorry, you must give the number of seconds.';
- }
- if ($errorMessage == '') {
- // OK so far.
- // Get the user's input.
- $seconds = $_GET['seconds'];
- // Is input a number?
- if (! is_numeric($seconds)) {
- $errorMessage = "Sorry, '$seconds' is not a number.";
- }
- }
- if ($errorMessage == '') {
- // Input checks out so far. Time for another test.
- // Is the input negative?
- if ($seconds < 0) {
- $errorMessage = "Sorry, time cannot be a negative number.";
- }
- }
- if ($errorMessage == '') {
- // OK so far. Time for another test.
- // Is the input too big?
- if ($seconds > 1000) {
- $errorMessage = "Sorry, time cannot be that large.";
- }
- }
- // All input checks done.
- if ($errorMessage == '') {
- // Input OK.
- // Work out the output message.
- if ($seconds < 60) {
- $output = 'Warmed it up.';
- }
- else if ($seconds < 300) {
- $output = 'Some like it hot.';
- }
- else if ($seconds < 600) {
- $output = 'Warning! Hot lava!';
- }
- else {
- $output = 'ARGH! Hull breach!';
- }
- }
- ?><!doctype html>
- <html lang="en">
- <head>
- <title>Microwaving</title>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <h1>Microwaving</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>Starting microwave...</p>\n";
- print "<p>PING</p>\n";
- print "<p>$output</p>\n";
- }
- ?>
- </body>
- </html>
Let's compare the code between the sales tax and microwave programs. They started like this:
- <?php
- // Set up vars to track results.
- // Error message is a flag, too.
- $errorMessage = '';
- $taxRate = 0;
- $stateShortName = '';
- $stateLongName = '';
- <?php
- // Set up vars to track results.
- // Error message is a flag, and a message container.
- $errorMessage = '';
- // User's input.
- $seconds = 0;
- // Output message. Only worked out if input is OK.
- $output = '';
They both set up some variables at the start. Both had $errorMessage
, then different variables, depending on the purpose.
Next, they both checked for input:
- // Is there input?
- if (!isset($_GET['state'])) {
- $errorMessage = 'Sorry, you must give a state.';
- }
- // Is there input?
- if (!isset($_GET['seconds'])) {
- $errorMessage = 'Sorry, you must give the number of seconds.';
- }
Then, they both grabbed the input into a variable, and ran another check - but only if there were no errors so far.
- if ($errorMessage == '') {
- // OK so far.
- $stateShortName = strtoupper(trim($_GET['state']));
- // Is the state known?
- if ($stateShortName != 'MI' && $stateShortName != 'IL') {
- $errorMessage = "Sorry, '$stateShortName' is not a recognized state.";
- }
- }
- if ($errorMessage == '') {
- // OK so far.
- // Get the user's input.
- $seconds = $_GET['seconds'];
- // Is input a number?
- if (! is_numeric($seconds)) {
- $errorMessage = "Sorry, '$seconds' is not a number.";
- }
- }
The two programs ran different checks, depending on what they needed to test.
The programs kept running checks, until they had checked everything they needed to.
Then, they did the processing, but only if there were no errors.
- if ($errorMessage == '') {
- // Input checks out.
- // Lookup.
- if ($stateShortName == 'MI') {
- $stateLongName = 'Michigan';
- $taxRate = 0.06;
- }
- else {
- $stateLongName = 'Illinois';
- $taxRate = 0.0625;
- }
- }
- // All input checks done.
- if ($errorMessage == '') {
- // Input OK.
- // Work out the output message.
- if ($seconds < 60) {
- $output = 'Warmed it up.';
- }
- else if ($seconds < 300) {
- $output = 'Some like it hot.';
- }
- else if ($seconds < 600) {
- $output = 'Warning! Hot lava!';
- }
- else {
- $output = 'ARGH! Hull breach!';
- }
- }
Both programs filled variables for use in the output part of the program.
Here's what the output code looked like:
- <h1>Sales tax rate</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>The sales tax rate for $stateLongName is $taxRate.</p>";
- }
- ?>
- <h1>Microwaving</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>Starting microwave...</p>\n";
- print "<p>PING</p>\n";
- print "<p>$output</p>\n";
- }
- ?>
The programs output the results they worked out earlier, in the processing section.
What similarites and differences are there between the two programs?
Ray
Well, they're not the same. The validation tests are different, the variables are different.
But... in a way, they are the same. They do things in the same way. When you know what to look for, you can see they're the same.
Right! We'd say they have the same structure. If you want to do validation, which comes up a lot in business programming, you could copy the structure, and change the variables.
I do this all the time in my own programming. Every experienced programmer does.
Note that I also screw up now and then. I'll copy code, and forget to change some of the variable names. Still, the structure works, so I'm ahead.
Adela
Something else I noticed. The output code is, like, really, really simple.
Good eye, Adela! Here's the output code again:
- <h1>Sales tax rate</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>The sales tax rate for $stateLongName is $taxRate.</p>";
- }
- ?>
- <h1>Microwaving</h1>
- <?php
- if ($errorMessage != '') {
- print "<p class='error-message'>$errorMessage</p>";
- }
- else {
- print "<p>Starting microwave...</p>\n";
- print "<p>PING</p>\n";
- print "<p>$output</p>\n";
- }
- ?>
The logic is simple:
- If there's an error message:
- Show it
- Else:
- Show the output variables
The complexity is in the first part of the program, working out $errorMessage
and the output variables.
Taking different pieces of logic, and working on them separately, is common. It makes your life easier.
- When you're writing the validation and processing code, you don't need to think about the output code. Easier on your brain.
- When you're writing the output code, you don't need to think about the validation and processing code. Easier on your brain.
Georgina
Oo! Wouldn't that make it easier for two people to work on the program at the same time? You know, Adela could write the top bit, and Ethan the bottom.
They'd have to decide what variables link the two chunks of code. But then they could work at the same time, and the program would get written faster.
Wow! Good brain work! Aye, that's true. That's how teams work on software. They break the job into chunks, and decide how the chunks are going to talk to each other (with variables, and other things). Then they can work in parallel, that is, at the same time.
Let's make this validation approach into a pattern. We need a name for it. What should we call this pattern?
- First validation test on variable $v.
- Is everything OK?
- Do the next test on $v.
- Is everything still OK?
- Do the next test on $v.
- How about now?
- Do the next test on $v.
- Still OK?
- Do the next test on $v.
Ethan
How about a validation chain? You know, the different tests are all chained together.
OK, that'll work.
Test some input, making an error message if needed. If that's OK, run another test. If it's still OK, do another.
Up next
Let's validate several fields at once.