Dan Q's PHP Cookery Class

An intro to PHP for Web Revivalists

Lesson 3: Lists and Loops

Objectives

  • Create a variable and assign it a value
  • Use a for loop to repeat a block of code a certain number of times
  • Use a foreach loop to iterate through each item in an array
  • Understand that arrays can be indexed by numbers (the default) or by strings
  • Use a glob() function to turn a list of files into an array

Looping

PHP has several ways of looping through the same code several times. A for loop lets you specify the number of times the loop should run. The syntax is a little gnarly if you've not come across for loops in any language before, but you'll get to grips with it with practice.

Let's create a new file called 7-times-table.php to show off how well we know our 7-times table:

<h1>The 7-times table</h1>
<p>
  <?php
  // $i is our variable (it's a bit like algebra)
  // It'll go from 1 to 10, adding one (++) each time
  for( $i = 1; $i <= 10; $i++ ) {
    // Print out the question...
    echo "7 × $i = ";
    // ...and the answer...
    echo ( 7 * $i );
    // ...and a HTML line break
    echo "<br>";
  }
  ?>
</p>

Here's what's happening:

  1. Our for loop creates a variable called $i and assign it the value 1
  2. While the value of $i is less than or equal to 10, the code inside the for loop is executed, and then the value of $i is increased by 1 (++)
  3. We print out the question "7 × $i = "
    • See how PHP lets us use the variable right in the middle of the string: this is called string interpolation, and it works for strings in double quotes (") but not for strings in single quotes (')
  4. Then we calculate the answer ( 7 * $i ) and echo it out
  5. Finally, we echo a HTML line break (<br>)

Doing your 7-times table up to ten sevens isn't so impressive, but what if you made it go to a hundred sevens. Or a thousand?

It's possible to put loops inside other loops. Can you work out how to print out all the 1-times tables, 2-times tables, 3-times tables, etc., up to 12-times-12? You'll need a loop inside another loop, with the "outer" loop using a different variable name than the "inner" loop. (🏁 view solution)

A basic gallery

Another kind of loop is the foreach loop. This iterates through each item in an array. (An array is a kind of list of values.) Arrays in PHP can be defined either using square brackets ([]) or the array() function: either way, the items in the array are separated by commas (,).

Here I've made an array listing different fruits. For each fruit in the array, I exit my PHP code (?>) to start writing HTML code. I write an <img> tag for each fruit, using the fruit's name as part of the filename and to help write the alt-text:

<?php
$images = [
  'apple', 'bananas', 'blackberries', 'cherry', 'kiwi',
  'lemon', 'orange', 'pear', 'pineapple', 'watermelon'
];

foreach( $images as $image ) {
  ?>

  <img src="//php.danq.dev/img/fruit/<?php echo $image; ?>.jpg"
       alt="<?php echo $image; ?>"
     width="200"
    height="200"
     style="border: 1px solid black;">

  <?php
}

Loops are great when you want to do a very similar thing multiple times, like when you're outputting a list of links or the images in a gallery.

Instead of showing each image, could you make an ordered list (<ol> and <li>) of hyperlinks (<a href="...">...</a>) to each fruit's image? (🏁 view solution)

By default, an array in PHP is indexed by numbers (starting from 0). If you wanted to print the name of third fruit in the array, you could write <?php echo $images[2]; ?>. But it's also possible to index an array by some other identifier, such as a string. This means it's possible to make an array like this:

<?php
$person = [
  'name'    => 'Dan Q',
  'website' => 'https://danq.me/'
];

echo '<a href="';
echo $person['website'];
echo '">';
echo $person['name'];
echo "'s website!";
echo '</a>';

Let's use what we know to write some PHP that prints out a navigation menu for our site. A navigation menu is a perfect example of something that may require the same repetitive structure several times over:

<?php
/**
 * This example uses the Daily Life Sepia Icons by Asam Munir.
 * Were this a real site, you'd want to credit Asam in the text
 * of the page somewhere, not just in a comment!
 *
 * https://www.svgrepo.com/collection/daily-life-sepia-icons/
 */

// Set up an array of links - each link is itself an array with
// a title, icon, and href:
$links = [
  [
    'title' => 'Home', 'icon' => 'home.svg',
    'href' => '/'
  ],
  [
    'title' => 'Blog', 'icon' => 'megaphone.svg',
    'href' => '/blog'
  ],
  [
    'title' => 'My Itch.io games', 'icon' => 'controller.svg',
    'href' => 'https://dan-q.itch.io/'
  ],
  [
    'title' => 'Ideas bin', 'icon' => 'light-bulb.svg',
    'href' => '/ideas'
  ],
  [
    'title' => 'Code snippets', 'icon' => 'code.svg',
    'href' => '/my-code-snippets'
  ],
  [
    'title' => "Awards I've won!", 'icon' => 'award.svg',
    'href' => '/awards.html'
  ],
];
?>

<nav>
  <ul>
    <?php foreach( $links as $link ) { ?>
      <li>
        <a href="<?php echo $link['href']; ?>">
          <img
            src="/img/icons/<?php echo $link['icon']; ?>"
            alt="">
          <?php echo $link['title']; ?>
        </a>
      </li>
    <?php } ?>
  </ul>
</nav>

This site uses a navigation menu like this, but with a few enhancements. If you're curious, you can view the source code to see how it's done (look in the add_navbar() function).

Because we're working in PHP code, we can edit our array dynamically. For example, we could add a link to the page we made that's closed on Mondays to our navigation menu... but only if it's not a Monday.

To do this, we use an if statement to check if today is Monday. If it isn't, we use PHP's []= operator to add a new link to our array (another option would be the array_push() function):

<?php
/**
 * This example uses the Daily Life Sepia Icons by Asam Munir.
 * Were this a real site, you'd want to credit Asam in the text
 * of the page somewhere, not just in a comment!
 *
 * https://www.svgrepo.com/collection/daily-life-sepia-icons/
 */

 // Set up an array of links - each link is itself an array with
// a title, icon, and href:
$links = [
  [
    'title' => 'Home', 'icon' => 'home.svg',
    'href'  => '/'
  ],
  [
    'title' => 'Blog', 'icon' => 'megaphone.svg',
    'href'  => '/blog'
  ],
  [
    'title' => 'My Itch.io games', 'icon' => 'controller.svg',
    'href'  => 'https://dan-q.itch.io/'
  ],
  [
    'title' => 'Ideas bin', 'icon' => 'light-bulb.svg',
    'href'  => '/ideas'
  ],
  [
    'title' => 'Code snippets', 'icon' => 'code.svg',
    'href'  => '/my-code-snippets'
  ],
  [
    'title' => "Awards I've won!", 'icon' => 'award.svg',
    'href'  => '/awards.html'
  ],
];

// If it's NOT Monday, add a link to the Not-Monday page:
if( date('w') !== '1' ) {
  $links[] = [
    'title' => 'The Special Page!', 'icon' => 'star.svg',
    'href'  => '/closed-on-mondays.php', 'class' => 'secret'
  ];
}
?>

<nav>
  <ul>
    <?php foreach( $links as $link ) { ?>
      <li class="<?php echo $link['class']; ?>">
        <a href="<?php echo $link['href']; ?>">
          <img
            src="/img/icons/<?php echo $link['icon']; ?>"
            alt="">
          <?php echo $link['title']; ?>
        </a>
      </li>
    <?php } ?>
  </ul>
</nav>

The same approach could be used to add menu items that are only visible to logged-in users, for example. You'd still want to check if they're logged-in when they visit the page behind the link, of course, to prevent them from simply typing its URL into their browser's address bar!

Looping through files

Let's revisit our fruit gallery from before, but instead of manually specifying the list of fruits, we'll use PHP's glob() function. It'll produce an array of all the files in the img/fruit/ directory, which we can then loop through (using foreach) to display each image:

<?php
// Get a list of all the .jpg files in the img/fruit directory:
$files = glob('../img/fruit/*.jpg');

// Loop through each file and display it as an image:
foreach( $files as $file ) {
  ?>

  <img src="<?php echo $file; ?>"
     width="200"
    height="200"
     style="border: 1px solid black;">

  <?php
}

Now if we add a new .jpg file into the img/fruit/ directory, it will automatically be added to the gallery.

What if there were both .jpg and .webp images in the directory? Can you work out how to loop through both types of image using a single loop? array_merge() might be useful. What about if you want to keep the fruit in alphabetical order, regardless of their file extension? Then you might want to use the sort() function. (🏁 view solution)

Showing a random quote

PHP provides the array_rand() function which selects a random key from an array. Let's use that to show a random quote to our visitors:

<?php
$quotes = [
  [
    'quote' =>
      "People don't verify quotes they read on the Internet.",
    'by' => 'Abraham Lincoln'
  ],
  [
    'quote' => 'Do or do not, there is no try.',
    'by' => 'Yoda'
  ],
  [
    'quote' => "Snakes. Why'd it have to be snakes?",
    'by' => 'Indiana Jones'
  ],
  [
    'quote' => "
      I've wrestled with reality for 35 years, Doctor, and I'm
      happy to state I finally won out over it.
    ",
    'by' => 'Elwood P. Dowd'
  ],
  [
    'quote' => 'At the beep, the time will be ' . date('g:i a'),
    'by' => 'The Speaking Clock'
  ],
];

$random_quote_id = array_rand($quotes);
$random_quote = $quotes[ $random_quote_id ];

?>
<blockquote>
  <p>
    <?php echo $random_quote['quote']; ?>
  </p>
  <cite>
    <?php echo $random_quote['by']; ?>
  </cite>
</blockquote>

<p>
  <a href="./random-quote.php">🔄 Get another quote</a>
</p>

Could you use this technique to show a random image, and apply a random CSS effect to it? 🏁 view solution