Jump to content


PHP Tutorial 5: Program control


  • Please log in to reply
12 replies to this topic

#1 Guest_ImmoMan_*

Guest_ImmoMan_*
  • Guests

Posted 01 November 2005 - 07:12 PM

PHP Tutorial 5: Program control

So far, all the code we've worked with was still pretty static and deterministic. We stored values, added them, compared them and printed them, but in each and every single situation we knew exactly what the result would be. The same code was executed every time we ran the script. This is very limiting as you can understand, because there are often situations where you want to do something either once or many times depending on the situation. This is what the next part of the tutorial is about.

if
To do something only when a certain expression (the value that a combination of operators, functions and variables has) is true, you use the if construct. Its form is like this:
if (condition_expression)
    statement;
where condition_expression is the expression of which the value is tested, and statement is the statement that should be executed only if the condition is met (that is, has the value true).

An example:
$number = 30;
if ($number == 30)
    print "number is 30<br>";

if ($number < 20)
    print "number is less than 20<br>";

if ($number / 5 >= 4)
    print "number divided by 5 is more than or equals 4<br>";

if (true)
    print "if this text doesn't show there's probably something veeerry wrong";

number is 30
number divided by 5 is more than or equals 4
if this text doesn't show there's probably something veeerry wrong

The conditions in the first and third if statement are met, so the statements that accompany them are executed. But the condition in the second if is false, so the statement following it is not executed. It is simply skipped over. The fourth if block shows a rather elementary assumption. Since the value of true is of course true, the statement following it is always executed. We could have easily left out the if and its condition altogether and the result would have been the same.


if-else
Often you have a situation where you want to execute a statement if something is true, but do something else if it is false. Like in this example, if $var is greater than 6:
if ($var > 6)
    print "var is greater than 6";

if ($var <= 6)
    print "var is not greater than 6";
It seems silly that you would have to test the same condition twice, when the second condition is always the opposite of the first one. For that reason, there is a useful extension to if that you can use in cases like this:
if (condition_expression)
    statement_if_true;
else
    statement_if_false;
So using else, we could have written the code above as:
if ($var > 6)
    print "var is greater than 6";
else
    print "var is not greater than 6";
A nice improvement, isn't it?


Block statements
What if you wanted to execute more than one statement when a condition was met? You could of course make one if statement for each statement you wanted to execute conditionally:
if ($myvar != 0)
    print "myvar isn't 0";
else
    print "myvar is 0";

if ($myvar != 0)
    $number = $number / $myvar;
else
    $number = 0;

if ($myvar != 0)
    print "number divided by myvar is $number";
else
    print "can't divide by zero!";
But that's a lot of work. And what if you decided you wanted to change the condition? You'd have to edit ALL the if statements by hand. Obviously that isn't going to work.

Fortunately, there is a solution. Whenever a single statement is needed, that statement can be replaced by a so-called block statement. A block statement is really nothing more than a bunch of statements, grouped together to act as if they were one single statement. Still, they're one of the most useful things out there so be sure to use them! A block statement is made by enclosing one or more statements with curly braces { and }. So, using block statements, we could have written the above code as:
if ($myvar != 0)
{
    print "myvar isn't 0";
    $number = $number / $myvar;
    print "number divided by myvar is $number";
}
else
{
    print "myvar is 0";
    $number = 0;
    print "can't divide by zero!";
}
Certainly a lot more pleasing to the eye, and it's a lot easier to edit, too. Notice how I indented the lines inside the block statement. This makes it easy to see which part of the code belongs to what. The matching braces are also placed above each other, so that it is immediately clear which braces are paired. It's not necessary to do it this way, but it can certainly save you a headache afterwards, so be sure to do it like this to make your code as readable as possible.


while loop
Sometimes you want to execute a statement (or a block) many times depending on a condition, instead of just once. For example, you want to print the value of a variable, but only as long as that variable's value is still greater than a certain value. Copy-pasting your if-statements as many times as you want the code to be executed is an option of course. But if you want to do it lots of times, for example more than 100, your code suddenly becomes a black hole of maintenance. Any work you put in never comes out. And that can't be right...

And again, there is a solution for this: a while loop. A while loop is similar to an if statement, except that the statement keeps executing over and over as long as the condition is still true. The code for a while loop is the same as an if statement, except that the keyword if gets replaced with while:
while (condition_expression)
    statement;
There is no else for a loop, since it brings up the question 'should the else statement be looped too or just execute once?'. If it loops over the else statement, you'd have a loop that never ends (because it would always loop over either one of the statements). But if it only executed once, it would technically not be a loop anymore. Allright enough programming philosophy, back to the real stuff. :p

So, say we wanted to print the value of $counter and then decrement it. And we'd want to do that as many times as the value of $counter was at the start. Here's how that would be done with a while loop (using a block statement):
$counter = 10;
while ($counter > 0)
{
    print "counter is $counter<br>";
    $counter--;
}
print "end of the loop";

counter is 10
counter is 9
counter is 8
counter is 7
counter is 6
counter is 5
counter is 4
counter is 3
counter is 2
counter is 1
end of the loop

$counter starts off with the value 10. 10 is more than 0, so the block statement executes. The first statement in the block prints the value of $counter. Then the second decrements $counter. The block then ends, but the program jumps back to the top of the loop and tests the condition again. $counter is now 9, which is still greater than 0. So the block statement executes a second time. $counter then becomes 8, which is still greater than 0 so the loop is executed again, and so on. Eventually $counter is decremented to 0. When control jumps back to the condition, the test evaluates as false and the loop is stopped. The program jumps past the loop and continues executing statements after the loop, one-by-one like it always did.

When using loops, make sure that at some point the condition expression becomes false. If you forget to increment/decrement/adjust the variable that controls the loop, you could get the dreaded infinite loop. An infinite loop is a loop that never stops because its condition cannot become false, ever (with while (true) being a fine example). Occasionally you'll want an infinite loop, and instead use a break statement (see below) to end the loop. But more often than not, infinite loop equals bad news. So watch out for them. If you're wondering what PHP does if it happens to get stuck in an infinite loop anyway... PHP has a maximum script execution time, 30 seconds by default. If executing a script ever takes longer than that time, PHP cuts your script short and prints an error message. So you're reasonably protected from infinite loops, but in general try to avoid them.


for loop
In our previous example about the while loop, you saw how we used a counter to control how often the following statement was executed. Situations like that often occur in a program. But we had to adjust the counter inside the block statement, and we had to give the counter a proper value beforehand.

Because it happens so often (about 99% of the loops I use are like that!), there is a special shortcut variety of this: the for loop. The syntax for a for loop is a bit odd, but once you use it a few times you'll remember it:
for (initialisation_statement; condition_expression; loop_statement)
    statement;
initialisation_statement is the statement that should be executed when the loop starts for the first time. Typically, this is where you set you counting variable to a proper value. After that there is the condition_expression. That is exactly the same as it is for the while loop: it tests the condition after each iteration (each 'lap') of the loop and stops the loop if it is false. Finally, the loop_statement gets executed every time the loop 'loops'. So usually you want to adjust your counter variable there. Be warned though that although both the initialisation_statement and the loop_statement are statements, they can NOT be block statements. Only one statement allowed, although it is possible to separate certain statements with commas (,) instead inside a for loop header.

Now our first example with a for loop. This is code does the same as the code in the example of the while loop above, but it has been rewritten into a for loop instead:
for ($counter = 10; $counter > 0; $counter--)
    print "counter is $counter<br>";

print "end of the loop";
The output is the same as in the previous example, try it for yourself if you don't believe it. ;) The difference of course is that the for syntax is shorter, and (to those familiar with them) easier to understand quickly.

But, what if we have nothing to initialise? Or what if we don't need a loop statement? You can leave one of the statements blank if you want. The following for loop does exactly the same as a while loop would do with the same condition statement:
$another_counter = 0;
for (; $another_counter < 20; )
{
    print "counting... $another_counter";
    $another_counter++;
}
Of course it's pretty useless this way, a full-fledged for loop would have been a lot nicer. It was just to prove a point...


Loop control
Loops are pretty easy to understand, but they are rather limited in the amount of control you have over them. They keep going and once they're going there's no way to shut them down except by doing something to make the condition statement evaluate to false. But sometimes you can't change the condition's value directly or indirectly, because it is out of your control (for example because it is a value that is read from a file). But you'd still really really like to stop a loop even if the condition is still true...

For that, there is the break statement. When used as a statement, break; (with a semicolon, since it is a statement by itself) causes the loop to end immediately, skipping all statements after the break statement. Here's an example (and realise that an if statement inside a for loop is prefectly valid, since ALL control structures mentioned in this part of the tutorial are statements themselves, and so can be nested):
for ($i = 1; $i <= 10; $i++)
{
    if ($i == 5)
        break;
    
    print "counting to 10... $i";
}

counting to 10... 1
counting to 10... 2
counting to 10... 3
counting to 10... 4

$i starts off being 1, but it is incremented after each iteration of the loop. However, when it reaches the value 5, the if condition evaluates to true and causes the break statement to execute, ending the loop prematurely.

A variety of break is continue, which causes the rest of the block within the loop to be skipped, but does not stop the loop itself. It merely skips one iteration of the loop. Here is the example used for break, but with 'break' replaced by 'continue':
for ($i = 1; $i <= 10; $i++)
{
    if ($i == 5)
        continue;
    
    print "counting to 10... $i";
}

counting to 10... 1
counting to 10... 2
counting to 10... 3
counting to 10... 4
counting to 10... 6
counting to 10... 7
counting to 10... 8
counting to 10... 9
counting to 10... 10

Now, the loop does not end when $i equals 5, it just skips the rest of the block and so the print statement does not execute when $i is 5.

If you have more than one loop or switch statement (see below) nested inside each other, you might want to break or continue more than one of them at a time. To do this, simply put a number after the break or continue, separated by a space:
break 2;
continue 3;
The number indicates how many nested loops should be break-ed or continue-ed. When the number is 1, the effect is the same as a normal break or continue statement.


switch
Switches are useful only for making easy-to-read and maintain code. They're reasonably limited in what they can do, and your code certainly doesn't get shorter. But because making simple, readable code is half the work, you'd do well to know of the switch statement and use it whenever possible.

A switch statement is useful when you have a list of if-else statements, each comparing the same variable to a different, but constant value. Like this:
if ($unity == 1)
    print "unity is one<br>";
else if ($unity == 2)
    print "unity is two<br>";
else if ($unity == 3)
    print "unity is three<br>";
else if ($unity == 4)
    print "unity is four<br>";
else if ($unity == 5)
    print "unity is five<br>";
else
    print "unity is unknown";
This could have been written, using a switch statement, as:
switch ($unity)
{
case 1:
    print "unity is one<br>";
    break;

case 2:
    print "unity is two<br>";
    break;

case 3:
    print "unity is three<br>";
    break;

case 4:
    print "unity is four<br>";
    break;

case 5:
    print "unity is five<br>";
    break;

default:
    print "unity is unknown";
}
As you see, the code is hardly shorter but it is definitely more organised and easier to maintain (because the variable being tested is only mentioned once, at the top). Every if test is replaced by a case clause, with the number after it, and ended by a colon (:). When the value in the brackets following switch matches the value of one of the case clauses, program control jumps to that clause and starts executing from there. The code following the default clause is executed when none of the previous case clauses match the value of the expression after switch. default must always be the last clause in a switch.

The reason for the break statements at the end of each case is that otherwise, control would just 'fall through' to the next case clause and execute the code there, too. In that way, a switch is like a loop that is only executed once. If we left all the break statements out in the example, whenever $unity equalled 3, the following would be printed:

unity is three
unity is four
unity is five
unity is unknown

Sometimes behaviour like this is useful, usually it isn't. So remember to put in break statements at the end of each case clause.

One final note about switches: The values for each of the cases can only be a predefined constant integer or string value. You can't use floats or booleans, and the value for each case has to be fixed in the code, it cannot be an expression. Keep this in mind.

That's all for now, see you next time when we discuss $next_topic. :)

Edited by ImmoMan, 01 November 2005 - 07:14 PM.


#2 DCoder

DCoder

    One of the Few

  • Members
  • 331 posts
  • Location:Lithuania, Central Europe
  •  Old Grumpy Bastard

Posted 02 November 2005 - 08:19 AM

if ($var > 6)
   print "var is greater than 6";
else
   print "var is not greater than 6";


Or just use a ternary operator,
echo 'var is ' .  ($var < 6 ? 'not ' : ' ') . 'greater than 6';

Ares - How do you want to improve YR today?
What's stopping you from migrating to Ares? We can't implement it unless you tell us!

ModEnc - C&C Modding Encyclopedia | Modders' Quote Database | Yet Another C&C Modding Forum

#3 Guest_ImmoMan_*

Guest_ImmoMan_*
  • Guests

Posted 02 November 2005 - 02:01 PM

My focus was on learning the basics, not going into code efficiency. :p

#4 Mastermind

Mastermind

    Server Technician

  • Undead
  • 7,014 posts
  • Location:Cambridge, MA
  • Projects:MasterNews 3
  •  The Man Behind the Curtain

Posted 02 November 2005 - 02:37 PM

I want to brutally murder whoever thought of the idea of the ternary operator. It makes for the most annoying, unreadable code ever.
Posted Image

Well, when it comes to writing an expository essay about counter-insurgent tactics, I'm of the old school. First you tell them how you're going to kill them. Then you kill them. Then you tell them how you just killed them.

Too cute! | Server Status: If you can read this, it's up |

#5 Guest_ImmoMan_*

Guest_ImmoMan_*
  • Guests

Posted 02 November 2005 - 05:02 PM

I happen to like it, for the reason that it is an expression and not a statement.

#6 Blodo

Blodo

    The one who disagrees

  • Project Team
  • 3,002 posts
  • Location:Eastern Europe
  • Projects:siteMeister, Mental Omega
  •  The wise guy

Posted 02 November 2005 - 05:26 PM

Ternary operator rules :p Much cleaner than a if else statement.

ARGUMENT FROM CREATION, a.k.a. ARGUMENT FROM PERSONAL INCREDULITY (I)
(1) If evolution is false, then creationism is true, and therefore God exists.
(2) Evolution can't be true, since I lack the mental capacity to understand it; moreover, to accept its truth would cause me to be uncomfortable.
(3) Therefore, God exists.


#7 DCoder

DCoder

    One of the Few

  • Members
  • 331 posts
  • Location:Lithuania, Central Europe
  •  Old Grumpy Bastard

Posted 02 November 2005 - 05:47 PM

I want to brutally murder whoever thought of the idea of the ternary operator.  It makes for the most annoying, unreadable code ever.

<{POST_SNAPBACK}>


Don't diss ternary, it is much more readable than some stuff I work with in Perl :p
# Normalize space around quotes
$$text =~ s!\n*((?:(?:^|\n)>[^\n]*)+)\n*!\n$1\n\n!g;

Ares - How do you want to improve YR today?
What's stopping you from migrating to Ares? We can't implement it unless you tell us!

ModEnc - C&C Modding Encyclopedia | Modders' Quote Database | Yet Another C&C Modding Forum

#8 Guest_ImmoMan_*

Guest_ImmoMan_*
  • Guests

Posted 02 November 2005 - 06:04 PM

That has got to be the most unreadable code I've ever seen...

#9 DCoder

DCoder

    One of the Few

  • Members
  • 331 posts
  • Location:Lithuania, Central Europe
  •  Old Grumpy Bastard

Posted 02 November 2005 - 06:23 PM

The magic of regular expressions :p
Ares - How do you want to improve YR today?
What's stopping you from migrating to Ares? We can't implement it unless you tell us!

ModEnc - C&C Modding Encyclopedia | Modders' Quote Database | Yet Another C&C Modding Forum

#10 Guest_ImmoMan_*

Guest_ImmoMan_*
  • Guests

Posted 02 November 2005 - 06:27 PM

I figured that, but wouldn't that string be quoted or something?

#11 Mastermind

Mastermind

    Server Technician

  • Undead
  • 7,014 posts
  • Location:Cambridge, MA
  • Projects:MasterNews 3
  •  The Man Behind the Curtain

Posted 02 November 2005 - 06:30 PM

The magic of regular expressions :p

<{POST_SNAPBACK}>

No, that's just Perl in general. If you can read Perl code it's obviously broken.
Posted Image

Well, when it comes to writing an expository essay about counter-insurgent tactics, I'm of the old school. First you tell them how you're going to kill them. Then you kill them. Then you tell them how you just killed them.

Too cute! | Server Status: If you can read this, it's up |

#12 Athena

Athena

    Embody the Truth

  • Undead
  • 6,946 posts
  •  Former Community Leader

Posted 02 November 2005 - 06:35 PM

lol, what are the pros of Perl above other languages then?

#13 DCoder

DCoder

    One of the Few

  • Members
  • 331 posts
  • Location:Lithuania, Central Europe
  •  Old Grumpy Bastard

Posted 02 November 2005 - 06:49 PM

What is Perl?
Perl is a general-purpose programming language originally developed for text manipulation and now used for a wide range of tasks including system administration, web development, network programming, GUI development, and more.
The language is intended to be practical (easy to use, efficient, complete) rather than beautiful (tiny, elegant, minimal). Its major features are that it's easy to use, supports both procedural and object-oriented (OO) programming, has powerful built-in support for text processing, and has one of the world's most impressive collections of third-party modules.
Different definitions of Perl are given in perl, perlfaq1 and no doubt other places. From this we can determine that Perl is different things to different people, but that lots of people think it's at least worth writing about.


Coding in Perl is the easiest way to encrypt your source code :p I personally use it because it's ... fun.
Ares - How do you want to improve YR today?
What's stopping you from migrating to Ares? We can't implement it unless you tell us!

ModEnc - C&C Modding Encyclopedia | Modders' Quote Database | Yet Another C&C Modding Forum




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users