Application Structure
An error commonly made with applications and websites is made right at the very start - when laying out the structure of your application. It can make the difference between coding being a joy, and a chore. Not only this, but it can also help to make your app/website more secure, by preventing you having to remember important tasks like authentication. So onward...
First of all. Let me tell you this. Not everyone thinks like you. Not everyone will understand your directory names. I tell you, it's infuriating seeing directories with three letter names which contain absolutely nothing related to what you were expecting. For example, a "lib" directory which doesn't contain "lib"raries, or a "bin" directory which doesn't contain "bin"aries. Be verbose! It doesn't hurt. Trust me.
So with supersized directory names firmly in mind , I present to you, the directory structure that I have, over the past three or four years, evolved to be using. Go ahead, click the pluses - they work...
Overview
The structure is laid out so that all major directories are at the same level, in the main application directory (in this case "www.example.com"). This structure suits both applications and websites equally well, since as we'll see paths and DocumentRoots can easily be customised. Normally though, the DocumentRoot would be the htdocs directory (so called as that's the name that the Apache webserver uses by default). So here's a break down of each directory...
cron
Holds all scripts that are run via cron, for example daily/hourly maintenance. Shouldn't be any reason why you have your maintenance scripts in another language either. Code reuse. Hoo-ya.
htdocs
As noted, the DocumentRoot. Holds all the "front end" scripts - the files whose names you tap daintily into your browsers address bar. Anything to be accessible by the user. Things like CSS files, images and Jabbascript files should of course be put away neatly in their own directories. We'll get to the config.php file later.
includes
The real meat of the site. All your application/site specific libraries go here. Classes, functions, you name it. Again, feel at liberty to further organise into subdirectories, in particular foreign code (eg. the jpgraph library). Again, the files shown here (prepend.php and template.php), we'll come to later.
logs
I don't always employ this one, but it's handy at times. Quite obviously, it's for your log files.
pear
Now this will probably be quite contentious. Simply put, the whole idea of a central PEAR installation gives me the willies. One upgrade can break multiple apps/websites? No thanks. Having to check every app/website when you do get around to upgrading? Double plus bad. So in each of my apps/websites there is a pear directory, in which lives a replica of the PEAR directory structure and all the PEAR code that I use. Heck if you really wanted to, you could even change the options in the installer and use the installer to manage this particular directory. That's far too much work for me though, so I use good old fashioned cut 'n' paste code reuse. Not only this, but if you're using source control, then all the PEAR code can more easily be incorporated into your repository.
scripts
This one is for shell scripts that support the app/site. Things like upload/release scripts, database import/export etc.
templates
And finally, the templates directory. This is where you can put your templates. Surprise that. It can either be "real" templates, or pseudo templates (which are essentially just PHP includes).
So there you have the directory structure. Very simple, very easy to understand, in particular if you're picking up maintenance of an app/site. This isn't all there is to it though. There's also how you structure your files. This is where you make your app easier and quicker to write, and more secure.
index.php | +- config.php | | | +- Path & environment setup | +- Common libraries | +- prepend.php | ... Logic goes here | | +- template.php
This here thing on the right, is how a files require()s are laid out. Starting at the top, working your way down:
-
First thing in every single script, is to include the config.php file, which lives
in the htdocs directory. This sets up various paths, all based on the current directory.
These paths are then used to setup the include_path [1]. Also, things
like error_reporting are set. This file, using the newly set include_path then includes
all files which are commonly required. Examples of this are database, authentication and
session libraries, things you don't want to be thinking about when putting together a
new page.
-
Then the prepend.php is dragged in. This is code which is common to every request. Things
that you don't want to rewrite on every page, and things you don't want to have to remember.
Examples are starting the session, authentication if you app/site requires it, opening a
database connection etc. This is where the improved security comes in: the less you have to
remember, the less there is to forget. Forget to check authentication on a page, and you're
in deep doo-doo.
-
Your logic. Simply put, this is your code that is specific to this page. Could be anything.
- The last thing to do is to show the page, and for that you need template.php. This isn't the template library itself, rather a file where you can put code that is common to showing a page. For example showing a header and/or footer, setting up variables that pertain to the header/footer etc. Again, showing headers/footers is not something you want to have to rewrite on every new page.
So that's it. A simple yet elegant way to layout your application/website which can save you time and mullah.
Notes
-
You might think that munging the include_path on every page request is sub-optimal, and you'd
be right. However, certainly whilst in development, it can be very handy. Consider this:
<?php
$_PATHS["base"] = dirname(dirname(__FILE__)) . "/";
$_PATHS["includes"] = $_PATHS["base"] . "includes/";
$_PATHS["templates"] = $_PATHS["base"] . "templates/";
$_PATHS["pear"] = $_PATHS["base"] . "pear/";
$_PATHS["logs"] = $_PATHS["base"] . "logs/";
/**
* Set include path
*/
ini_set("include_path",
"$_PATHS[includes]:$_PATHS[pear]:$_PATHS[templates]");
?>
By doing this, you can move your application around your filesystem at will, onto another server, and include/require calls won't break. How wonderful.
As a side note, if your default include_path contains unnecessary directories, reducing it in this way can actually improve subsequent include/require performance, since fewer stat() calls to find files will be required.