More conditions

More comparisons

You saw this:

  • if ($dogs < 1)

< means "is less than." You also saw:

  • if ($dogs == 1) {

== means "is equal to." As you might expect there's also:

  • <= is less than or equal to
  • > is greater than
  • >= is greater than or equal to
  • != is not equal to

They work just as they do in VBA.

Logical operators

In VBA, there's And, Or, and Not. The same in PHP, but the symbols are different.

And is &&. For example, to test whether an animal is long and thin:

  • if ($length > 1.8 && $thickness < 0.3) {
  •   print "<p>Long and thin.</p>\n";
  • }

|| is or. To test whether a dessert has dark or milk chocolate:

  • if ($darkChocWeight > 0 || $milkChocWeight > 0) {
  •     print "<p>Chocolate detected!</p>\n";
  • }

Not is !. If an animal is not a tiger or lion:

  • if (! ($type == 'tiger' || $type == 'lion') ) {
  •   print '<p>Not a tiger or lion</p>';
  • }

Boolean variables

Check these out:

  • if ($darkChocWeight > 0 || $milkChocWeight > 0) {
  •     print "<p>Chocolate detected!</p>\n";
  • }
  • ...
  • $hasChocolate = ($darkChocWeight > 0 || $milkChocWeight > 0);
  • if ($hasChocolate) {
  •     print "<p>Chocolate detected!</p>\n";
  • }

I've taken the test out of the if, and made a variable out of it. The variable will be true or false. That's called a boolean variable.

Booleans can make your code easier to read. For example, suppose your company buys puppies for employees who have worked there for 10 years or more, work in the IT department, are over 50, or are an OU grad. You could put that in one if, like this:

  • if ($tenure >= 10 || $dept == 'IT' || $age > 50 || $almaMater == 'OU') {
  •   print "<p>Give them a puppy!</p>\n";
  • }

Another way:

  • $isLongTerm = ($tenure >= 10);
  • $isIT = ($dept == 'IT');
  • $isOld = ($age > 50);
  • $isGriz = ($almaMater == 'OU');
  • if ($isLongTerm || $isIT || $isOld || $isGriz) {
  •   print "<p>Give them a puppy!</p>\n";
  • }

I've extracted each test, and given it a name. This makes the business rule easier to understand.

Some business rules are complex. It's easier on your head meat to break them down. If you find yourself getting confused when writing an if, try breaking it down into a simpler test.

Pattern

Break down complex conditions

Businesses often have complex rules. That can lead to code that is complicated, and hard to read. Use boolean variables for individual conditions. Your code will be easier to think about.

Notice the variable names. Booleans tend to start with $is, or $has.

Nesting

Often you end up with ifs inside ifs. That's called nesting.

For example, suppose Shallow Sherri is reviewing a list of potential friends. If the person is young, then OK, they might be friend material. If they're between 30 and 50, they have to have a good income to be interesting, or be well connected. Above 50, they have to have a boatload of cash, and be well connected.

Here's some code for that:

  1. $isPotentialFriend = false;
  2. if ($age < 30) {
  3.   // Young.
  4.   $isPotentialFriend = true;
  5. }
  6. else {
  7.   // Older - check income and connections.
  8.   if ($age >= 30 && $age < 50) {
  9.     if ($income > 100000 || $isConnected) {
  10.       $isPotentialFriend = true;
  11.     }
  12.   }
  13.   else {
  14.     // Old! Ewww.
  15.     if ($income > 500000 && $isConnected) {
  16.       $isPotentialFriend = true;
  17.     }
  18.   }
  19. }
  20. if ($isPotentialFriend) {
  21.   $greeting = "Hi! I'm Sherri. What's your name?";
  22. }
  23. else {
  24.   $greeting = "(Sniff, sniff) Do I smell loser?";
  25. }
  26. print "<p>$greeting</p>\n";

Nesting can make rules easier to think about. When I'm writing the code the lines 15 to 17, I no longer have to think about age. I know this is the code for old people, like me (ewww!). I just focus on that.

Same for lines 9 to 11. I think about the conditions for middle-aged people.

When you get inside the body of an if, you reduce the cognitive complexity of your work. Look for opportunities to make your work easier to think about, when you're writing code.

Principle

Reduce cognitive complexity

Make your program easy to think about. You'll spend less time wrestling with bugs.

Notice the variable $isPotentialFriend. It's a flag. It summarizes the results of some processing. Initialize it (line 1), run some code that might change its value (lines 2-19), then check the flag's value (line 20).

Flags are useful things. You learned how to use them in validation, in the VBA course.

Pattern

Flag

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.

Example

Your company makes three products: K32, X64, and F128. You have a few people who field calls about the products. Different people specialize in different things, so you have to route calls, depending on the product and issue.

You can try the program:

http://webappexamples.skilling.us/html/more-conditions/more-conditions.php?product=k32&issue=returns

It takes the parameters product and issue from the URL, and shows output like this:

Output

If missing input, it shows error messages, like this...

Error

... and this...

Error

If the product is unknown, show:

Error

Ol' Man Joe tells you who gets called for what.

(Read in an old, slow cowboy voice.)

Well, I'll tell ya, youngun, but ya ain't gonna like it.

For the K32, well, call Paula Poundstone for a warranty issue. She's right loquacious, she is. Hey, that's be a dang good vocab word. Loquacious. Huh.

Anyways, if the issue is sales or returns, best be talkin' to young Adam the Felber. He done wrote a strange novel awhile back. Weirdest thing you ever read. Though the woman in the beginning was good. Reminds me o' cousin Lucinda. But that's a horse of a different color.

Orange.

You wanna talk about the K32's noise, you talk to Bonnie Burns. Darn woman is a walkin' foley, no lie.

So that's K32 warranty, sales, returns, and noise. Any other K32 issues, you talk to Toni Anita Hull. She's inta extreme cruisin', the biohazard kind.

Now, the X64, that's different. Paula Poundstone handles warranty and noise. Sales, that's Adam the Felber. He sure is around a lot. Always underfoot. Doug the Intern does returns and shippin' for the X64. Maybe the X64 ain't doin' so well. Ain't see young Doug in a while.

Oh, anythin' else about the X64, you ask Toni Anita Hull.

Last, the F128. Well, Paula Poundstone does warranty. Lots to do, there, the 128 is a piece of crap, pardon my French.

Adam the Felber handles noise on the 128. Doug the Intern, he does sales, returns, and shippin'. And, o' course, Toni Anita Hull does all the other F128 issues.

OK, let's write some code. We'll use that error-message-as-flag pattern. You'll see that a lot in this course.

Pattern

Error messages as a flag

Use a variable containing error messages as a flag. If it's MT, no errors.

We'll put most of the computation at the top of the PHP page, before doing any HTML output. You'll see that a lot, too.

Pattern

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 talk about input and error checking first. There are two GET parameters:

  • product
  • issue

Both are required. That means there'll be from zero to two error messages. If both are missing, we want two error messages. If one of them is missing, we want one error message. If both are present, no error messages.

  1. <?php
  2. // Init error message (also a flag).
  3. $errorMessage = '';
  4. // Get product.
  5. $product = '';
  6. if (!isset($_GET['product'])) {
  7.     $errorMessage .= 'Sorry, you must give a product.<br>';
  8. }
  9. else {
  10.     $product = strtolower(trim($_GET['product']));
  11. }
  12. // Get issue.
  13. $issue = '';
  14. if (!isset($_GET['issue'])) {
  15.     $errorMessage .= 'Sorry, you must give an issue type.<br>';
  16. }
  17. else {
  18.     $issue = strtolower(trim($_GET['issue']));
  19. }
  20. ...
  21.     <body>
  22.         <h1>Product contacts</h1>
  23.         <?php
  24.         if ($errorMessage != '') {
  25.             print "<p class='argh'>$errorMessage</p>\n";
  26.         }
  27.         else {
  28.             $product = strtoupper($product);
  29.             print "<p>Please call 1-800-666-6666, and ask $person about your $product.</p>\n";
  30.         }
  31.         ?>
  32. ...

First, initialize $errorMessage to MT. Then GET the product:

  • // Get product.
  • $product = '';
  • if (!isset($_GET['product'])) {
  •     $errorMessage .= 'Sorry, you must give a product.<br>';
  • }
  • else {
  •     $product = strtolower(trim($_GET['product']));
  • }

Recall that isset() is true, if the parameter exists. The if checks for that. If product isn't in the GET, the set $errorMessage. This will act as a flag that something has gone wrong, as well as explain what.

The message has a br tag at the end.

  • $errorMessage .= 'Sorry, you must give a product.<br>';

Hmm.

If the product is found, then:

  • $product = strtolower(trim($_GET['product']));

We're going to have a bunch of if soon, checking product to see who to call. Those statements will be easier to write, if we know whether product is upper- or lowercase. By using strtolower(), we know product will be lowercase.

OK, now do the same thing for the issue type.

  • // Get issue.
  • $issue = '';
  • if (!isset($_GET['issue'])) {
  •     $errorMessage .= 'Sorry, you must give an issue type.<br>';
  • }
  • else {
  •     $issue = strtolower(trim($_GET['issue']));
  • }

OK, back to the brs:

  • $errorMessage .= 'Sorry, you must give a product.<br>';
  • ...
  • $errorMessage .= 'Sorry, you must give an issue type.<br>';

Why? Here's what you get if you leave out both GET params:

Params missing

Both error messages are showing. We want them on separate lines like this.

Here's the HTML we want:

  • <body>
  •     <h1>Product contacts</h1>
  •     <p class='argh'>Sorry, you must give a product.<br>
  •     Sorry, you must give an issue type.<br>
  •     </p>
  • </body>

The brs make sure error messages are on separate lines.

Adela
Adela

Hey, maybe that explains something else I noticed.

  • $errorMessage .= 'Sorry, you must give a product.<br>';
  • ...
  • $errorMessage .= 'Sorry, you must give an issue type.<br>';

Does that mean "append to?"

Aye! Good on you! In VBA, that might be:

  • tErrorMessage = tErrorMessage & "Sorry, you must give a product.<br>"
  • ...
  • tErrorMessage = tErrorMessage & "Sorry, you must give an issue type.<br>"

The error messages accumulate in $errorMessage, with a br at the end of each one.

That's so useful, it deserves to be a pattern:

Pattern

Accumulate error messages

Make a variable for error messages. Append all messages to that variable.

Here's that error code again:

  1. <?php
  2. // Init error message (also a flag).
  3. $errorMessage = '';
  4. // Get product.
  5. $product = '';
  6. if (!isset($_GET['product'])) {
  7.     $errorMessage .= 'Sorry, you must give a product.<br>';
  8. }
  9. else {
  10.     $product = strtolower(trim($_GET['product']));
  11. }
  12. // Get issue.
  13. $issue = '';
  14. if (!isset($_GET['issue'])) {
  15.     $errorMessage .= 'Sorry, you must give an issue type.<br>';
  16. }
  17. else {
  18.     $issue = strtolower(trim($_GET['issue']));
  19. }
  20. ...
  21.     <body>
  22.         <h1>Product contacts</h1>
  23.         <?php
  24.         if ($errorMessage != '') {
  25.             print "<p class='argh'>$errorMessage</p>\n";
  26.         }
  27.         else {
  28.             $product = strtoupper($product);
  29.             print "<p>Please call 1-800-666-6666, and ask $person about your $product.</p>\n";
  30.         }
  31.         ?>
  32. ...

OK, so that's error handling. Now, what about the processing?

Processing

  1. if ($product == 'k32') {
  2.     // All the K32 options.
  3.     if ($issue == 'warranty') {
  4.         $person = 'Paula Poundstone';
  5.     }
  6.     else if ($issue == 'sales' || $issue == 'returns') {
  7.         $person = 'Adam the Felber';
  8.     }
  9.     else if ($issue == 'noise') {
  10.         $person = 'Bonnie Burns';
  11.     }
  12.     else {
  13.         $person = 'Toni Anita Hull';
  14.     }
  15. }
  16. else if ($product == 'x64') {
  17.     // All the X64 options.
  18.     if ($issue == 'warranty' || $issue == 'noise') {
  19.         $person = 'Paula Poundstone';
  20.     }
  21.     else if ($issue == 'sales') {
  22.         $person = 'Adam the Felber';
  23.     }
  24.     else if ($issue == 'returns' || $issue == 'shipping') {
  25.         $person = 'Doug the Intern';
  26.     }
  27.     else {
  28.         $person = 'Toni Anita Hull';
  29.     }
  30. }
  31. else if ($product == 'f128') {
  32.     // All the F128 options.
  33.     if ($issue == 'warranty') {
  34.         $person = 'Paula Poundstone';
  35.     }
  36.     else if ($issue == 'noise') {
  37.         $person = 'Adam the Felber';
  38.     }
  39.     else if ($issue == 'sales' || $issue == 'returns' || $issue == 'shipping') {
  40.         $person = 'Doug the Intern';
  41.     }
  42.     else {
  43.         $person = 'Toni Anita Hull';
  44.     }
  45. }
  46. else {
  47.     $product = strtoupper($product);
  48.     $errorMessage = "Sorry, $product is not one of ours.";
  49. }
  50. ...

These are nested ifs. The code shows something I mentioned earlier. When writing code, and you're inside an if block, forget about other conditions.

Lines 3-14 are about the K32. When writing that code, I can forget about validation and the other products, and just think about the K32.

Lines 18-29 are about the X64. When writing that code, I can forget about the validation and other products, and just think about the X64.

Lines 33-44 are about the F128. When writing that code, I can forget about the validation and other products, and just think about the F128.

You won't be able to program well, or at all, until you learn to decompose programs. That means breaking a problem into chunks, and work with one chunk at a time.

Plan then zoom

When I write code, I start by outlining the program at the high-level. I use comments. For example, with this program:

  • // Get and validate inputs.
  •  
  • // Compute person to call.
  •  
  • // Output.

It can also help to write down important variables, especially those that link parts of the code. For example:

  • // Get and validate inputs.
  • // Create $errorMessage.
  • // Create $product, and $issue.
  •  
  • // Compute person to call.
  • // Compute $person.
  •  
  • // Output.
  • // Print $errorMessage, $product, and $person.

$errorMessage, $product, and $person link the pieces of the program together.

Next, add in some high-level logic.

  • // Get and validate inputs.
  • // Create $errorMessage.
  • // Create $product, and $issue.
  •  
  • // Compute person to call.
  • // Compute $person.
  • if ($product == 'k32') {
  •   // Work out person
  • }
  • elseif ($product == 'x64') {
  •   // Work out person
  • }
  • elseif ($product == 'f128') {
  •   // Work out person
  • }
  • else {
  •   $errorMessage = 'Not our product';
  • }
  • // Output.
  • // Print $errorMessage, $product, and $person.

Eventually, I'll zoom in on this bit...

  • if ($product == 'k32') {
  •   // Work out person
  • }

... and just do that, pushing the rest of the program out of my head meat. The goal of the code chunk is to work out a value for $person. When I'm write that code, all I think about is working out $person for the K32.

Then I'll zoom out, and zoom in on the next bit...

  • if ($product == 'x64') {
  •   // Work out person
  • }

Learn to reduce the load on your brain is key to Happiness in Programming.

Principle

Reduce cognitive complexity

Make your program easy to think about. You'll spend less time wrestling with bugs.

You reduce brain load by zooming in on one part of the program at a time.

Let's make a principle out of plan then zoom.

Principle

Plan then zoom

Write a program as a broad plan first, leaving the deets for functions. Then work on the functions separately.

Learning how to do this makes programming - and many other jobs - easier.

Exercise

todo