Control structures change the flow of a program's execution based on the current state of things. while and for are examples of looping constructs, if/elseif/else and switch are examples of conditional execution. The ?: operator can often be used to compactly get the if/else effect. In general, the fewer control structures, the less complex the code is. Object oriented programming is one technique that can be used to replace conditionals. A lot of looping can be replaced with built in PHP functions; we'll see some of those later.
Conditional execution
We've encountered if. It executes its body of code when the expression in parentheses is "non false". PHP has a few values that are considered false when used in an expression – the obvious false, null, array(), "", 0 and the not-so-obvious "0" (while "false" is not false!). This is a comprehensive reference showing how different values are treated. By adding elseif/else we can test multiple conditions and provide alternative logic. Let's look at an example that we can build on. Edit the /public/index.php file to look like:
<?php require
'../includes/proj.php'; require
'code.php';
$name = "John Smith";
if ($link = getNavLink (array ('name' => $name, 'session' =>
USER_SESSION))) {
$navHtml = sprintf('<a href="%s">%s</a>', $link ['url'],
$link ['label']); } else
$navHtml = '<p style="color: red;">Unauthorized access</p>'; ?>
<!DOCTYPE html> <html> <head> <meta
http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<title>phpinfo output</title> </head>
<body>
<?php echo $navHtml; ?>
</body> </html>
Expressions being tested can be simple or complex. We can compare values, call a function or even set and test at the same time. The if statement above calls a function, assigns its result to $link and then evaluates the result as true/false. The flexibility of not needing to declare variables before use is shown with $link; it's only used after we verified it has a value. I also wanted to show how flexibly the getNavLink argument could be passed. A string, $name, is created on its own and then used to initialize the value of an array entry. The array can be created “on the fly” right in the function call.
If there is more than one statement to be executed, they must be delimited by {}. Some teams will insist on always using, even around one statement {}, because it can help prevent the following situation. Starting with this
if ($a == true)
print ("a is true”);
If we wanted to add another line that was only executed if $a is true and forget to add {} around both statements (normal code would be harder to read so this would be less obvious)
if ($a == true)
print ("a is true”);
$b = OnlyIfA ();
The problem here is that the new statement will execute no matter what the result of the if is. This rule applies to loop bodies also -- {} are necessary to treat multiple statements like one.
It's important to know that PHP "short circuits" its testing. That means when logical operators join several expressions, evaluation stops as soon as it's known that the result will be true or false.
if ($user && foobar ($user ['foo'], $bar) || $user = $defaultUser) {
/*
* We know we have a predictable sort of value in $user.
*/
}
Because of short-circuiting, we know we aren't calling foobar with nothing, and we know that $defaultUser will only be assigned if $user wasn't a reasonable value or foobar returns a false value.
The uppercase USER_SESSION is a define symbol, sometimes referred to in PHP as constants. Technically they're not constants, as PHP has a const type (used in classes) and there are better ways to designate special values – although it's still very appropriate for "environmental" settings like paths. define() is very common and you will probably see a lot of it.
if can optionally have multiple elseif statements and one else. Create a file named code.php in the includes directory (inside the same directory that public is inside). Add this
<?php
/**
* Returns nav link.
*
* Returns the correct navigation link for our page based
* based on the user session information.
*
* @param array $user or null
* @return array link or null
* ( 'name' => string user-name, 'session' => integer session type )
*/
function getNavLink ($user = null) {
if ($user && isset ($user ['session'])) {
if ($user ['session'] == USER_SESSION) {
$link = array (
'label' => "Profile for {$user ['name']}",
'url' => "edit_profile.php"
);
}
elseif ($user ['session'] == GUEST_SESSION) {
$link = array (
'label' => "Sign up",
'url' => "create_profile.php"
);
}
else {
$link = array (
'label' => "Home",
'url' => "index.php"
);
}
}
else
$link = null;
return $link;
}
This function expects an array argument with a default value that clearly shows when it hasn't been passed. Now create /includes/proj.php and add
<?php
set_include_path(dirname(__FILE__) .PATH_SEPARATOR .get_include_path());
define ('USER_SESSION', 1);
define ('GUEST_SESSION', 2);
This is more for structure; we don't want this kind of code in the application. Setting the include path to this includes directory allows other includes to use a bit of shorthand. (See the 2nd require statement in index.php). Now if you go to your Web url (for me: http://vaio.web.gwp/public/index.php) you should see

We passed John Smith who is a user. Try changing the values for name and session, or even passing no argument at all. We'll come back to this example later.
switch is another important way to control execution flow. It's a generalization of the if/elseif/else.
switch ($myVar) {
case 1:
print ("myVar is 1");
break;
case 2:
print ("myVar is 2");
default:
print ("myVar is something else");
}
However, there's [probably] a bug in this switch: notice the absence of a break statement in case 2. If you don't want any other cases to be executed, you need to use the break statement, which resumes execution at the 1st statement following the switch. In our example switch, if $myVar == 2, the output would be:
myVar is 2
myVar is something else
It can be useful to “stack” the cases when you want to share code between cases:
switch ($myVar) {
case ?????????
Looping or Iterating
The basic looping constructs are for, while and do-while.
While loops as long as the expression is true.
$i = 0;
while (++$i < 5) {
print $i;
}
1234
Note the increment operator's precedence relative to when the value of $i is used for comparison. We're incrementing before we use the value of $i. If it were $i++, we would see
12345
(Even though it's 0 the first time, $i gets incremented to 1 before printing.)
Where while checks its condition before executing any code, do will always execute its code at least once.
$i = 0;
do {
print $i;
} while ($i-- > 0);
0
Using a simple index variable in while doesn't make it very useful, that's just for illustration. However, imagine the expression is a function that returns something to process. If getNextAccount() returned an array containing account information or null when there's no more accounts:
$total = 0;
while ($account = getNextAccount()) {
$total += $account ['balance'];
}
printf ("Total balances = %d", $total);
We both assign the returned array and test it for not being a false/null/empty result.
for loops combine the separate pieces of the while loop – an initializing expression, a condition to test at the beginning of each loop and an “incrementing” expression to execute at the end of each loop
for ($i = 0; $i < 5; $i++) {
print $i;
}
01234
Each of the three expressions can consist of multiple, comma-separated statements. We can even turn our previous while into a for that doesn't require any code in its body:
for ($total = 0; $account = getNextAccount(); $total+= $account['balance']) {}
printf ("Total balances = %d", $total);
break can be used inside loops to completely jump out. continue can be used to immediately skip to the next loop iteration.
while ($i) {
if ($i == $stop)
break;
// the loop will stop immediately
}
while ($i) {
if ($i == $skip)
continue;
// this will be skipped when $i = $skip
}
Iterating with Arrays
We can use for, while and do-while to process arrays.
$array = array ('eat', 'apple', 'pie');
for ($i = 0; $i < count ($array); $i++) {
print $array[$i];
}
But unless the array is indexed in a predictable way, like a simple list starting at index 0, it's hard to know how to increment the index.
foreach is a loop that's built for arrays. This would print the same values as the previous for loop.
foreach ($array as $str) {
print $str;
}
You can use it to iterate through each value of the array, irrespective of the keys, or get both the key and value for each entry.
$customer = array (
'name' => "Acme Inc",
'type' => 1,
'invoice' => 1300
);
foreach ($customer as $p => $v) {
print "{$p} is {$v}<br/>";
}
name is Acme Inc
type is 1
invoice is 1300
The key variable, in our case $p, is a string. You should note that even though $customer is an array, $v is passed by value; if we want to change it, add &.
$customer = array (
'name' => "Acme Inc",
'type' => 1,
'invoice' => 1300
);
foreach ($customer as $p => &$v) {
$v = 'apple';
}
print_r ($customer);
Array ( [name] => apple [type] => apple [invoice] => apple )
Only use the as $key => $value syntax when you need the key. Code maintenance is harder (and probably less efficient) with the presence of unused variables.
foreach lets you write code quickly to do complex array manipulation, but PHP supplies many advanced array functions that eliminate looping. Honestly, whenever I code a foreach loop for an array, I stop and ask myself if there's a way to do it with built in functions, almost a code smell.
We'll get to some array functions and forms next.