PHP is free-form and has no imposed structure.  It is just a scripting language. Those familiar with Rails will feel lost with this lack of structure.  It’s easy to just throw another file in the document root to handle a new page.  However, this quickly turns into the dreaded file pile nightmare that plagues the vast majority of PHP-based websites.

For the past several years, I’ve used the same basic structure for all my PHP websites, and I believe this format works extremely well to keep a growing site organized.

Basic file structure

I have a structure I’ve been using for years when creating a new PHP website. This structure has served me very well, so let me expand upon it a bit more.

  • lib/ - Here I store all my include files that provide utility functions and classes.  By keeping this outside the document root, we are assured that users can’t view our source through the web.
  • lib/classes/- This subdirectory contains all classes.
  • www/- This is the document root and contains all the .php files.  Each .php file follows the basic structure in the next section.  Files beginning with an underscore are POST files, and do not display anything.  They process data and forward to another page.  More on this later.
  • src/ - This contains source JavaScript and CSS files.  This directory is explained in detail in my post about concatenating and compressing.
  • database/ - This contains the schema and rollout scripts necessary to maintain changes to the database.
  • tests/- Unit tests using PHPUnit.

Rendering HTML content

Most files that display HTML use the following structure:

<?php
    include_once("validate.inc");
    include_once("header.inc");
?>
<!-- CONTENT -->
<?php
    include_once("footer.inc");
?>
  • lib/header.inc - This displays the page title, sidebar, navigation, and sets up any global variables necessary for the site.
  • lib/footer.inc - This displays the copyright notice, contact information, and anything else that should appear in the footer.  This can also deconstruct open connections if necessary.
  • lib/validate.inc - If the site requires authentication, the validate.inc include file makes sure the user is authenticated.  If they are not, they are forwarded to the sign-in page. This must be included before the header.

Handling POST data

Scripts that handle POST requests always begin with an underscore (e.g. _signin.php).  These do not display any HTML, and always redirect to another page using Location redirection.  I’ll cover this technique more in a future post.  For example:

<?php
    do_something($_POST['id'], $_POST['value']);
    header("Location: /index.php");
?>