Escaping The Labyrinth of PHP Includes
In Brief: The include() feature of PHP bases paths on the location of the including script. This may leave you puzzling how to create a PHP page you can include anywhere on your website without modifying the include path for each folder. The solution is simple but may be somewhat confusing for those new to PHP. To include a file from any PHP page you must specify the actual location of the file relative to the file system instead of relative to the ‘web root’ or the root folder where web pages are served from. For example: include(’/usr/www/users/youruserid/this.site/syndicate_the_news.php’); would include news in your site. If syndicate_the_news.php needs to include any text or php files, a database or XML parser module generating content, then you must ensure all includes in that file are ‘absolute’ or relative to the web root instead of to the folder of the executing script.
One of the great things about PHP pages is the ability to include content into a page from another source (included or generated from another PHP page, text file or HTML fragment). Placing included content anywhere on any page on your website is useful in reducing the amount of maintenance required to keep a website current. However, implementing this idea of universal includes can be difficult and frustrating to newcomers.
Why? Because of how PHP handles path names
You might think that it would be easy to include a piece of information on any page of your website. Just whip up a PHP page to generate some tasty HTML and include that page on whatever page you want the info to appear. You’d be wrong.
Let’s look at why.
The first thing to study is the way PHP handles includes. (I know, study is boring, but often is revealing.)
The easiest way to include our information is to place the included page in same folder with the page doing the including.
<?php // include from working folder include('scores.php'); ?>
To better organize our website we may prefer to gather all our include pages into a folder of their own. This is where the include business starts to get tricky.
<?php // include from sub folder include('includes/scores.php'); ?>
The two kinds of includes we have looked at so far use what are called relative paths to the included files. The location of the include file is relative to the path of the location of the page doing the including. The path used to locate the include file is based on where the executing PHP page is located. In the first instance scores.php must always be in the same folder as baseball.php, which means we can place these pages anywhere on the site without changing the paths as long as we move both files together. This is a convenience because it does not tie the script to any one location.
Relative path. A relative path is one like this: folder/folder/php.php An absolute path is one like this: /root_web_folder/folder/folder/php.php The difference is subtle in syntax but important in meaning. The relative path is always relative to current folder the PHP page exists in. The absolute path is referenced from the web server root folder.
The second instance complicates our scenario a little. The path is still relative to the executing PHP page and we can also move the files about the site as long as they are kept in the same relation to one another (this means creating another ‘includes’ folder where they are moved to if one does not already exist). For many beginning PHP programmers it might seem possible that this piece of code could be included anywhere on the site. However, if you move this to a sub folder, the include will fail, unless there happens to be an include folder beneath that folder.
Suppose we have several sub folders each representing a different topic on our website. How do we include scores into the index page on all folders? If we try to use the previous include, it will fail when executed from any of the sub folders because the path is relative to where the PHP page is executing, therefore if ‘includes/scores.php’ is executed from the ‘golf’ folder the path will look like this: ‘/golf/includes/scores.php’ It will fail because as you can see, no ‘includes’ folder exists within the golf folder. We want the includes folder to be centrally located off the main site folder. If we must create an includes folder off each topic folder that defeats the purpose of creating a centralized location for all included pages.
/ /baseball /football /golf /includes /swimming
PHP allows us to use the Unix folder navigation symbols to locate a file relative to the current working folder. The two dots means move one folder level back toward the root folder. Now we can modify our index page in each folder to use the following code:
<?php // include from sub folder include('../includes/scores.php'); ?>
This code looks one level back towards the root of the website folder tree (not the system root folder) and then back down into the ‘includes’ folder for the ‘scores.php’ file. Suppose we want to place the scores anywhere on our site? We have pages in several folders and we have an index page in our main folder. To place the scores on our index page we need:
<?php // code for index.php // include from sub folder include('includes/scores.php'); ?>
To place scores on pages in sub folders we need:
<?php /**************************** * code for * /baseball/index.php * /golf/index.php * etc. ***************************/ // include from sub folder include('../includes/scores.php'); ?>
Of course, the problem with this is we now must maintain different include paths for different pages. It would be nice to have just one piece of code we could use for any page on the site. Why not just include a header file on all pages? We would still have to use different relative paths on pages in different folders, but could we just include the scores something we include on every page? Suppose we also want to include a header on each page. We would have to include it the same way we included scores.php for pages on different folder levels. It might be possible then to keep scores.php in the includes folder along with the header.php and use a relative include, something like this:
We create a universal header called header.php (I’m not very inventive here am I?), placing it in the includes folder along with scores.php
<?php // universal header // include folder include('scores.php'); ?>
We include it the same way we included scores.php on all our pages.
<?php // code for index.php // include from sub folder include('includes/header.php'); ?>
If you try this, which I encourage you to do, you will receive an error message. Why does this happen? We are making a relative include from header.php to another page in the same folder with it. What is wrong with including a page into an included page when they are in the same folder as one another?
Because the rule of paths being relative to the executing script applies to included pages also. Instead of the path to scores.php being relative to the location of header.php it is relative to the original including page, which may be in one level of the folder tree or another.
The relative path to an include file in PHP is always based on the currently executing PHP page. This means if page a.php includes pages b.php and b.php includes c.php all relative include paths in b.php and c.php are relative to the location of a.php without exception.
As you can see, for a site of any magnitude this task is starting to be frustrating. What are the alternatives to using include files for placing scores on every page?
We can use an absolute path to the include file. This means giving up the easy ability to drag our PHP pages and drop them anywhere on the site without modification. To accomplish this we return to our original code used to include our scores from the ‘includes’ folder. By adding a single slash to the front of the include path we now reference our include from the main website folder. No matter what page we place this code on it will include scores.php from the same place.
<?php // include from sub folder include('/includes/scores.php'); ?>
We can fetch our content from an external source, such as a database. Instead of a scores.php file we draw this information from a database. To make this easy to include on any page, we encapsulate this in a function. We can place this code anywhere on the website. By the way, the information does not have to come from a database, but can come from any external source not on the filesystem, such as fetching XML data from a website and translating it into formatted HTML, etc.
<?php // include scores here display_scores(); ?>
We have to be careful that the function does not include anything from the file system, i.e. do any relative PHP includes. Otherwise, we are back to square one with the same problems we had before with relative includes and the same tradeoffs using absolute includes. This sometimes occurs when a database function depends on database abstraction layer or database configuration files in order to connect to the database.
Generating content from a function is also a good idea because the page or application does not have to know anything about the internals of the function generating the content. And it can be placed anywhere on any web page on our site. Except for the case in which the function itself resides in an file that must be included (as when functions are organized into one include file) or when database configuration and connection code must be included. Then we lose the modularity.
We can solve this problem by requiring any database configuration files to be absolute includes. (By the way this frustrates me because I have a philosophy born in my experience from making plain old static web pages that relative paths always best because it allows the document to be physically portable. But this probably does not matter as much for the include paths we specify in web applications. It’s not like someone is going to save the application page itself on their hard drive and want to view it from there. I’m not talking about the paths used in links and image sources, but the includes. Paths for links and image sources created by your application may better serve users if they are relative (depending on your audience).