Ray Patrick Soucy

Application Structure and Organization

One of the most common mistakes made by the new PHP developer is the failure to properly define the structure of an application’s code base.

Organizing your application code before you start the project will save time, yield more maintainable code, and if done correctly result in a more secure application structure.

When coding PHP for a large scale project it makes sense to follow the Zend PHP coding standards.  You will notice that the majority of the code I release makes an attempt to follow these for readability.

Excessive include statements are often the source of performance losses in PHP.  As a result the include_once statement was introduced to check if a file has previously been included.  While this would be fine as a compiler directive it is actually costly in terms of CPU time (especially if you have hundreds of them).  As a developer you should know exactly what has been included or not at any point in the source code.  If you don't take the time to design your application structure this may quickly become difficult.

Another common mistake that PHP developers make is to place Library PHP files in a web-accessible directory.  This can result in unexpected access to files that should never be accessed directly by the user.  That is a problem in itself, but even more disconcerting, the fact that if a web server is miss-configured and no processing PHP files, the source can be returned to the user instead of the processed data exposing potentially sensitive information such as passwords.

Thirdly, for include statements to be efficient; they should always use explicit full or relative paths.  You should never have to search the include path or run a function to determine where your file lives.  While it may seem trivial for smaller projects, once used in a larger project you can actually see a measurable compute-time difference (often close to or over a second).

Placing these files outside the web root solves these problems.

I commonly use the following directory structure as a starting point for new web applications:

project/
        htdocs/
        htdocs-ssl/
        ssl/
            cert.pem
            key.pem
        log/
            access.log
            error.log
            ssl-access.log
            ssl-error.log
        lib/
            init.php
            _config.php

This directory structure is similar to what you would create for an Apache virtual host.  You could also create an Alias for the directory to include it as a sub-directory of an existing website (useful if you do not run virtual hosting).  In this case you would leave out the log and ssl directories.

Assuming we place our project directory in the /srv root (yes, that's what it's there for) here is an example Apache configuration for a virtual host.

<VirtualHost 192.0.2.1:80>
    ServerName project.example.com
    DocumentRoot /srv/project/htdocs/
    ServerAdmin webmaster@example.com
    ErrorLog /srv/project/log/error.log
    CustomLog /srv/project/log/access.log combined
</VirtualHost>

<VirtualHost 192.0.2.1:443>
    ServerName project.example.com
    DocumentRoot /srv/project/htdocs-ssl/
    ServerAdmin webmaster@example.com
    ErrorLog /srv/project/ssl-error.log
    CustomLog /srv/project/ssl-access.log
    SSLEngine on
    SSLCertificateFile /srv/project/ssl/cert.pem
    SSLCertificateKeyFile /srv/project/ssl/key.pem
</VirtualHost>

And here would be an example using a directory Alias.

Alias /project "/srv/project/htdocs"
<Directory "/srv/project/htdocs">
    Options Indexes MultiViews FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
</Directory>

For more information on configuring Apache, see the Apache HTTP Server Documentation.

Going back to our directory structure, you'll notice that there were two files in the lib directory, init.php and _config.php.

lib/
    init.php
    _config.php

The lib directory is where we will store all PHP include files.  The init.php file will make all the PHP includes (functions, classes, global variables) required by every page, including the _config.php file which will store configuration variables for the project (such as Database connection information).  This creates a situation where for most PHP files in the web root, only one include statement is needed (and init.php includes the rest).  The exception to this are situations where an include may only be required for a small set of files.  While we could include everything we need using init.php, it's better to keep it as light as possible.

You will also notice that the _config.php file is prefixed with an underscore _.  This is a naming convention used to show that the file is included elsewhere; in this case, init.php.  As a general rule, we will never include a file that begins with and underscore directly outside of our library.

Let's assume that along with our configuration we have a database wrapper class to include.  Here would be a sample init.php.

<?php

require '_config.php';
require 
'_EasyMySql.php';

$db = new EasyMySql($db_hostname$db_username$db_password$db_database);

Notice that we use the require statement rather than the include statement.  Require is identical to include except that it will halt execution on failure (which is what we want).  Also notice that we omit the PHP close tag.  This prevents accidental whitespace from being included.  Here would be a sample _config.php.

<?php

$db_hostname 
'127.0.0.1';
$db_username 'project';
$db_password 'password';
$db_database 'project';

Now, in the web root, we would create our index.php file using the init.php include.

<?php

require '../lib/init.php';

?>

The index.php file now has direct access to the database object.  Note that the database object was initialized in the init.php; this was to demonstrate that init.php can also be used to initialize some global variables, however, if not every page requires database access it may be a better idea to only create the object as needed.

By following these simple guidelines, you can save a lot of development time, and create a project directory that has all your most commonly used code which can then be used as a base when you create new projects.