Tiger Heron
Tiger Heron
Tiger Heron

First steps with PHP - booting a script, Part 1

Average: 3 (2 votes)

Having covered downloading and installing PHP and setting up a development environment, I'm now actually going to talk about writing PHP code! Specifically, I will describe how I boot my PHP scripts and why I boot them the way I do. Part 1 shows how my requirements for code portability and maintainability influence the boot process. Part 2 will go into more detail about the specific steps executed by the boot code.

My environment

Here's some background: I develop sites for multiple clients. Initial coding occurs on my Windows XP system. When the code is running well, I upload it to the client's hosting system, which is typically Linux based.

I start by creating a new directory for my client's Web files (on my PC) and then creating an alias in my Apache configuration file to point to this location. For this article, let's assume I reach the Web site using http://localhost/sample.

A sample task

For our first PHP class, let's tackle something easy—let's use PHP code to generate our sample site's navigation menu. Here's a quick sketch of the navigation class:

<?php
class Navigation
{
    public function printMainNav()
    {
        echo '<ul><li><a href="index.php">Home</a></li>....';
    }
}
?>

and here is the index.php page using the class:

<?php require 'Navigation.inc'; ?>
<html>
  <head>
    ...
  </head>
  <body>
    <?php $navigation = new Navigation(); $navigation->printMainNav(); ?>
      ...
  </body>
</html>

At this point, the directory we created contains two files:

  • index.php
  • Navigation.inc

The code works, but we're already seeing some potential pitfalls. First, we don't want to place the PHP classes in the same directory as the Web pages. Secondly, our sample system will eventually grow to where we will use sub-directories to organize our Web pages. The problems we see are:

  • Each Web page will need to include the Navigation.inc file based on the Web file's location. If we ever change our mind about where to place Navigation.inc, a lot of files will need changes.
  • The link to the home page (in the navigation), as written, only works for Web pages at the top of the directory tree.

It may seem that there are some easy work-arounds for these two problems. For instance, setting the path to the Navigation.inc file in the php.ini file solves the first problem. And linking to "/sample/index.php", solves the second. The reason these solutions aren't ideal lies with the environment in which I work.

Goals and requirements

These are my goals:

  • I want to use a consistent approach for all my clients.
  • I want to allow for sub-directories in the Web site.
  • I want all Web files to have a consistent way of including PHP code, regardless of their location on the site. This lets me use the same template for all files and to move a file from one place to another without having to edit it.
  • I don't want the Web pages to use any complicated calculations in order to include PHP code. Complicated code should be stored in one place, not scattered in every Web page.
  • I want to be able to upload the code from my Windows system to the hosting system without any changes and have it work in both environments.
  • Occasionally, I also want the code to work without change on a testing site on the hosting system. This lets the client preview site changes without affecting the site the customers sees.

These are the restrictions I have:

  • I can't alter the php.ini file since shared hosting services usually don't provide access to this file.
  • I can't place php_flag or php_value statements in the .htaccess file. These only work if PHP is run as an Apache module and some hosting companies use Fast CGI. (Note: apparently, this limitation will disappear in PHP 5.3.)
  • I can't assume I can reference a Web page at the top of the site by starting the path with "/". This might work for the final site, but not for the site on the PC, where the top is at /sample.

My current solution

I have not found the perfect solution. What I use is pretty close:

  • Each Web page begins with <?php require 'include.inc'; ?>.
  • Each Web site directory contains an include.inc file with the following contents:
    <?php
      $fileTop = '.';
      require "$fileTop/res/boot/boot.inc";
    ?>
    

The advantages are:

  • We can move Web files from one directory to any other without editing.
  • The variable $fileTop identifies the file path to the top of the root of the Web site.
  • $fileTop is a relative path, so the Web site tree can be located anywhere in the site's document root.

It's not a perfect solution because I still have to create one include.inc file per directory. Of course, that's not very many files and the contents are not complicated. Any heavy lifting can be relegated to the boot.inc file.

It's also not perfect because it requires an extra file load for the include.inc file.

It assumes that '.' is in the php.ini search path. So far, this has proven to be a safe assumption.

Alternatives

Drupal and a number of other systems use a common dispatcher page for all Web site accesses. Every page loads index.php, which then includes the desired content. $fileTop is always '.'.

Using Apache's mod_rewrite module, the process could work like this:

  • The user enters http://www.sample.com/somepage.
  • Mod_rewrite converts this to http://www.sample.com/index.php?q=somepage.
  • The index.php file is loaded.
  • The index.php can directly contain the boot.inc code. This saves one include/require.
  • It uses the 'q' parameter to determine which page the user really wanted.
  • That Web page somepage.inc is included.

This eliminates two file includes. There is some hidden cost in that the mod_rewrite script probably has to do a file check to see if the requested Web file actually exists—this allows access to CSS, Javascript and image files that you may not want handled by PHP.

One problem with this approach is that my Web pages can set some options before loading the include.inc file and these options control aspects of the boot process (e.g. whether to use caching, whether to use sessions, etc.). I will discuss this more in Part 2 of this article; we might lose this flexibility in the single dispatcher approach or we might have to work around it in a way that voids the performance gain. Not having tried this alternative, I'm not sure of the answer.

Do you have an even better approach? Please let me know.

More to come

In Part 2, I will discuss what happens when the boot.inc file is executed. Stay tuned!


Better Portability

There are potentially better methods than $fileTop = ".";. For example, $fileTop = dirname(__FILE__); is a great way to add portability to your code (I tend to prefer to leave the somewhat ambiguous "." operator out of my path's). As you know __FILE__ is the literal location of the file the code is executing in (not the file that included the code). dirname() just extracts the directory

Also for code portability, consider using the DIRECTORY_SEPARATOR constant instead of "/". It guarantees operation in a windows environment (where directories are separated by ") and a linux-based environment.

Finally consider the __autoload() and spl_register_autoload() functions. If you are placing your class files in a common directory or have a common naming convention, you can use the __autoload() magic function to automatically include (at the time of need) the file for a class that is trying to be accessed. spl_register_autoload() is best as it allows you to add an __autoload() function without worrying about overwriting a previously existing one (say if you dropped your code in with some other code that used __autoload() as well)


Re: Better Portability

Daniel, thank you for your thoughtful suggestions. I have a few comments on your response:

  • So everyone is clear, if a Web page is nested one-level deep, then I would set $fileTop = '..'. $fileTop is the relative path to the top of the Web site. Using your approach, I could also set $fileTop to dirname(__FILE__) . '/..'. In PHP 5.2.3, I could even use $fileTop = __DIR__ . '/..'.
  • There are advantages and disadvantages to the dirname(__FILE__) trick. The advantage is the including a file with an absolute path is faster than including a file with a relative path. The disadvantage is that if you compile the code, the value of __FILE__ is the value at compile time, not execution time. While most of us don't pre-compile PHP code, it is an attractive option for PHP-GTK applications. It is also a problem in some other cases—see PECL Bug #11872. These cases are unusual enough that it is not unreasonable to ignore them; I'm keeping my options open right now.
  • Your comments on using DIRECTORY_SEPARATOR for code portability are reasonable, but only if you're concerned with working on esoteric platforms. Linux, Windows and even modern Macs (which are Unix-based) all support '/' as a file separator. Remember, I run all my Web sites on a Windows XP system and then move them to a Linux system.
  • As for the comment about __autoload and spl_register_autoload(), I'll be writing some articles about my use of __autoload in the future (refer to my introductory article on this series). For booting, though, using __autoload doesn't solve any of my problems. I still need to get the boot.inc file loaded before I start using features like __autoload.

Thanks again for taking the time to write. I appreciate all comments.

Tony Freixas


still not sure

may be I am dumb

But I am still not sure how to use dirname(__FILE__) . '/..' thing

For example I have this structure

lib/a.php

b.php

c.php

dir/e.php

Now a.php includes c.php further a.php is included every page like b.php or dir/e.php.

How to mane this, please help.


Hi, Abhi,

Let me try to answer your question. The first clarification is that, in my scheme, the include.inc file that I create is needed only for directories that hold PHP Web "pages"—these are the files that the server calls in response to an HTTP request.

Files that are included by other PHP files do not need to worry about booting, but they do need to use the $fileTop variable when including or requiring another PHP file.

So: a.php has:

require_once "$fileTop/c.php";

b.php and dir/e.php both have

require_once "$fileTop/lib/a.php";

Now I'm going to guess that b.php and dir/e.php are called directly by the server. Normally, I would keep an included file like c.php in a different folder and I would use the ".inc" extension, but that's not important. The top level and the "dir" directories both have an include.inc file. The top level include.inc contains:

$fileTop = ".";

The "dir" include file contains:

$fileTop = "..";

So both b.php and dir/e.php would start like this:

require 'include.inc';
require_once "$fileTop/lib/a.php";

With $fileTop set appropriately, both b.php and dir/e.php will be able to include lib/a.php and lib/a.php will also be able to find c.php.

I hope this answers most of your question. The dirname(__FILE__) technique is just an alternate way of calculating $fileTop's value.

Tony Freixas

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.

 

Privacy policy Tiger Heron LLCinfo@tigerheron.com • (503) 771-7724

Copyright © 2005-2007, Tiger Heron LLC