Inserting files with links and images

See more about:

Where are we?

You’ve seen how to use the require statement to insert one file into another. But do it the wrong way, and your site won’t work. Let’s look at the problem and a solution.

This lesson’s goals

By the end of this lesson, you should:

  • See how inserting files can break images, links, and other paths in your site.
  • Be able to fix the problem with a PHP variable.
  • Understand why this is a Big Win.

The dog site

Let’s look again at the dog site. Remember that it has two sections: articles and dog profiles. Here’s the directory tree:

Directory tree

Figure 1. Directory tree

The file header.inc is inserted in all of the other PHP files. For example, here’s how it is inserted into playing-with-dogs.php:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Playing with dogs | The Dog Site</title>
  </head>
  <body>
    <?php 
    require '../library/header.inc';
    ?>
    <h2>Article: Playing with dogs.</h2>
    <p>Pretend there is some content here.</p>
    <p><a href="index.php">Back to the article list</a></p>
  </body>
</html>

Figure 2. Code for playing-with-dogs.php

Line 9 inserts a header file that is the same across the entire site. The path says “go up to the parent directory, then down into the library directory, then insert the file header.inc.”

Here’s the header:

<!-- Header file, to be included into every page. -->
<h1>The Dog Site</h1>
<h2>Bringing you happiness since last week</h2>
<hr>

Figure 3. HTML for header.inc

You can try the site. You can also download a zip file with all of the PHP and other files.

The client wants to add the following logo to the header of every page:

Logo

Figure 4. Logo

“No problem,” we say. We put the image file (logo.png) in the library directory.

Logo in the library directory

Figure 5. Logo in the library directory

We change header.inc to:

<!-- Header file, to be included into every page. -->
<h1><img src="logo.png" alt="Logo">The Dog Site</h1>
<h2>Bringing you happiness since last week</h2>
<hr>

Figure 6. First try at the new header.inc

But this doesn’t work. Look at the home page. You’ll see something like:

Broken logo

Figure 7. Broken logo

Exactly what you see depends on how your browser handles broken image links.

So what’s the problem? Tell your browser to show you the HTML of the page (Control+U, or right click), and you’ll see:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Welcome | The Dog Site</title>
  </head>
  <body>
    <!-- Header file, to be included into every page. -->
<h1>The Dog Site</h1>
<h2><img alt="Logo" src="logo.png">Bringing you happiness since last week</h2>
<hr>    <p>Welcome to the dog site.</p>
    <p>See some <a href="dog-profiles/index.php">dogs</a>.</p>
    <p>Read an <a href="articles/index.php">article</a>.</p>
  </body>
</html>

Figure 8. HTML of index.php with broken logo

Again, the layout is a little strange around line 9, because the HTML here is inserted by PHP.

Line 10 contains the logo HTML. It’s exactly what we put into header.inc. So what’s the problem?

It has to do with the directory that logo.png is in. The file is in the library directory (See Figure 5). But the browser gets this code:

<img alt="Logo" src="logo.png">

There is no path, just logo.png. So the browser tries to load the image from the same directory that index.php is in.

index.php is in the root directory of the site (/). So the browser tries to load /logo.png.

Oops.

Every page on the site has the same problem. For example, /articles/why-dogs-are-great.php also has the line:

<img alt="Logo" src="logo.png">

inserted by the PHP require statement. The browser tries to load logo.php from the same directory as why-dogs-are-great.php. So it tries to load /articles/logo.png.

Argh!

How to fix it?

One choice is to copy logo.png to every directory, but that’s a really bad idea. If we wanted to change the logo for the site, we’d have to find and change every copy of logo.png.

Root relative URLs

A better choice (but not the best) is to change the inserted code to:

<!-- Header file, to be included into every page. -->
<h1><img src="/library/logo.png" alt="Logo">The Dog Site</h1>
<h2>Bringing you happiness since last week</h2>
<hr>

Figure 9. Root relative path in header.inc

The src attribute on line 2 has changed. It has the full path to the logo. This is a root relative URL. It says “Go to the root of the Web site, then go down into the library directory, then find logo.png.

This works! But…

Root relative URLs cause their own problems. They reduce the portability of a site, that is, your ability to move all of the files in the site around.

Remember that part of CoreDogs is not just learning the tech, but learning to think like a Weber. That is, to understand how people who create Web sites do their work.

On just about every project, you want to have a separate test version of the site. You tell your client “go look at the test version to see what I’m doing.” Then you can talk about changes.

Let’s say you put a test version of the site on http://mytestsite.com/dogsite/. You also have test versions of other sites, like http://mytestsite.com/catsite/ and http://mytestsite.com/hipposite/. This is common Weber practice; keep test versions on different domains from the product site.

But there’s a problem. You’ve moved the files into a directory called dogsite. The root relative path of the logo file is now /dogsite/library/logo.png. Your library file (header.inc) has /library/logo.png. So you have to change it to /dogsite/library/logo.png. Argh!!

But every time you copy the test version of the site to the production version, you have to remember to change back every root relative path. Double argh!!

And you have to do this for every root relative path! Not just the logo, but CSS files, JavaScript files, ...

Triple argh!!!

There are ways around this, too, but the fact is that root relative URLs are a pain.

But there’s a good solution. One that works well for template-based, dynamic Web sites.

Using a root path variable

What we’re going to do is change the HTML code that header.inc inserts. Instead of just src="logo.png", it will insert src="library/logo.png", or src="../library/logo.png", or src="../../../library/logo.png", or whatever each page needs.

We’ll do this by creating a variable on each page, and having header.inc use that variable.

If you remember back to JavaScript, you learned that a variable is a piece of computer memory that’s given a name. For example, here’s some JavaScript that takes whatever the user typed into a form field, puts it into the variable user_name, and tests whether the variable is empty.

user_name = $("#user").val();
if ( user_name == "" ) {
  alert("Sorry, you must enter a user name.");
}

Figure 10. JavaScript variable

PHP has variables, too. They act much like JavaScript variables. One difference is that their names all begin with $ (a dollar sign).

Our solution to the logo problem is in two steps:

  • On every page, create a variable containing the path from that page to the Web site’s root.
  • Use that variable in header.inc.

Let’s have a look. Here’s the directory tree again.

Directory tree

Figure 5 (again). Logo in the library directory

See the file playing-with-dogs.php in the articles directory? Here is the new version:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Playing with dogs | The Dog Site</title>
  </head>
  <body>
    <?php 
    $path_to_root = '..';
    require $path_to_root . '/library/header.inc';
    ?>
    <h2>Article: Playing with dogs.</h2>
    <p>Pretend there is some content here.</p>
    <p><a href="index.php">Back to the article list</a></p>
  </body>
</html>

Figure 11. Code for playing-with-dogs.php

Line 9 creates a variable called $path_to_root. Remember that variables begin with $ in PHP. The string “..” is put into the variable. You can put any characters you like into a variable. You can put numbers, too, and other things.

Notice that the statement ends with a semicolon (;). All statements must have a semicolon at the end. JavaScript is forgiving about this. PHP is not.

Line 10 is:

require $path_to_root . '/library/header.inc';

The period (.) is PHP’s string concatenation operator. It sticks two strings together. So 'Big' . 'ger' would yield 'Bigger'.

JavaScript uses plus (+) to mean the same thing, but if you remember, that causes problems, because + in JavaScript also means “add two numbers together.” PHP doesn’t have that problem. PHP has two operators (. and +) that mean two different things.

Let’s look again:

$path_to_root = '..';
require $path_to_root . '/library/header.inc';

Part of Figure 10 (again). Code for playing-with-dogs.php

.. is the path from playing-with-dogs.php to the Web root. /library/header.inc is the path from the Web root to the header file. Put them together, and you get ../library/header.inc, which is the path from playing-with-dogs.php to the header file.

So far, so w00f. As long as $path_to_root is set correctly for every page, the header file will be found correctly.

Note that this variable is declared in files that other files (like the header) are inserted into. These are the files with require statements, not the files that are inserted by the require statements.

What about the files that are inserted? They use the variable that was declared. Let’s look at the new header.inc.

<!-- Header file, to be included into every page. -->
<h1><img src="<?php print $path_to_root; ?>/library/logo.png" alt="Logo">The Dog Site</h1>
<h2>Bringing you happiness since last week</h2>
<hr>

Figure 12. Code for header.inc

Remember that the print statement outputs stuff into the HTML stream. It can output anything, anywhere. Including file paths!

If $path_to_root has ‘..’ in it, then line 2 will produce:

<h1><img src="../library/logo.png"

As before, $path_to_root has the path from the file that the code in Figure 12 is inserted into, to the Web root. /library/logo.png is the path from the Web root to the logo file. Put them together, and you have the path from the page the header code is inserted into, to the logo.

You can try this version of the site. Look at the HTML for a few pages, and check out the paths to the logo. By the way, you know that “..” means “go up one level to the parent.” “.” means “stay where you are.” You’ll see that on the home page.

You can download a zip file of the site if you want.

This approach works for all paths, whether they’re for images, links, CSS files, JavaScript files, or anything else. To see that, suppose we wanted to add a contact page to the site, with a link from the header of every page:

Link to contact page in header

Figure 13. Link to contact page in header

The contact page is at /contact.php, that is, in the Web root, along with the site’s home page.

Contact page in directory tree

Figure 14. Contact page in directory tree

We can add a contact link to every page on the site by only changing header.inc! W00f!

Here is what the new header.inc would look like.

<!-- Header file, to be included into every page. -->
<h1><img src="<?php print $path_to_root; ?>/library/logo.png" alt="Logo">The Dog Site</h1>
<h2>Bringing you happiness since last week</h2>
<p><a href="<?php print $path_to_root; ?>/contact.php">Contact us</a></p>
<hr>

Figure 15. New code for header.inc

Line 4 does the trick. $path_to_root contains the path from the page the code is inserted into to the Web root. The contact page is right there in the Web root; that’s just /contact.php.

Why this is a Big Win

You see how we could add the contact link by changing only one file? It wouldn’t matter how many pages were in the site. 10? 100? 10,000? Change just one file, and everything gets changed.

This is a Big Win for someone who works on Web sites, especially someone who does it professionally. Webers don’t think just about the end result, that is, the site that users will see. They also think about the work processes that create that site.

Webers think about productivity. The more productive they are, the better value they give employers and clients.

Exercise: Inserting files again

Modify your solution to the first Wombat exercise. Add a variable to wombat.php that has the path to the Web root. Use the variable in all the require statements.

Upload the file to your server. Put the URL below.

You can see my solution, but try it yourself first.

(Log in to enter your solution to this exercise.)

Summary

  • Inserting files with paths in them – to images, links to pages, whatever – can break your site.
  • You can fix it with root relative paths, but this makes your site less portable.
  • A better way it to create a variable on each page, giving the path from that page to the root of the site. Add that variable in the inserted files.
  • This gives you a big productivity win. You can change an entire site just by changing one inserted file.

What now?

Using a variable like $path_to_root works with all kinds of paths in the HTML. We’ve fixed paths to images (the logo) and links (the contact page) in this lesson.

But there are often paths in JavaScript code. To images, for example. We need to fix them as well. How?


How to...

Lessons

User login

Log in problems? Try here


Dogs