File structure - The PHP Project Guide (2014)

The PHP Project Guide (2014)

3. File structure

3.1 Why does file structure matter?

A well thought out, well executed file structure will not only make it easier for you to manage your project in the short run, but will also mean that as your website grows and requires more files and directories, you and your team will be able to quickly and easily locate files, whether or not they’ve been working with your website for a long time.

Let’s say you’re working with a framework. This will almost force you to keep a consistent file structure due to the nature of most frameworks implementing the MVC (Model View Controller) structure. However, working without a framework means you have to take extra care to ensure everything is where you need it. Lumping all of your project files into a root directory will not help. Carefully designing a file structure might also make it harder for attackers to locate files, particularly if you have error reporting off (which you should have turned off in a live environment).

3.2 Planning your file structure

This isn’t easy, because just as every other planning phase, you need to look to the future. This means it’s extremely frustrating to attempt to plan something to cater for future scale. The best thing to do is start small and only plan for separating main groups of files. This could be having a directory that contains several files all containing functions that relate to different areas of your application, or broken up pieces of template that you need to include as part of your overall design. If you’re frustrated with planning out your directory structure, try and plan around what makes sense. For example:

1 require 'template/header/logo.php';

Looks and reads much better than:

1 require 'designs/logo.php';

3.3 Naming files

Files can be named anything as long as they can be read by your server. Usually, files are just given a short, lowercase name describing their contents. For example, it’s safe to assume that init.php is an initialisation file, or logout.php terminates a user session.

An often preferred way of naming files is with an extra piece of readable information. Let’s say you had three files:

1 require 'user.php';

2 require 'user.php';

3 require 'User.php';

These files could be located within different directories, and could also contain entirely different code. But, within the grand scheme of things, this can make it difficult to distinguish between them. We could have a user profile page, an include displaying a snippet of information about a user and a user class. It would make perfect sense to name all these files user.php providing they were within carefully named directories:

1 require 'user.php';

2 require 'assets/display/user.php';

3 require 'classes/User.php';

There! Now we’re more aware of the difference between each file.

3.4 Watch your extension

As you may already know, PHP files can be named with any extension to identify them, you don’t really need a .php extension. There are extremely important security considerations when renaming file extensions which we’ll discuss in a moment. For now, let’s look at an example:

1 require 'template/sidebar/latest.inc';

If we have PHP code within latest.inc, PHP will interpret it and everything should work as expected. However, you’re now eliminating the webserver’s ability to recognise this file as a PHP file, and therefore will be served as plain text. Even if you don’t have sensitive information within this file, it can still give clues about the way your code works and is therefore a security risk. You can register any file extension to be interpreted as PHP file by adding the following to an .htaccess file:

1 AddType application/x-httpd-php .inc

You can specify multiple extensions like so:

1 AddType application/x-httpd-php .inc .class

However, it’s always better to just stick with naming a file with a .php extension. This keeps consistency, eliminates confusion and means you’re protected even if you switch your code to another server.

3.5 Organising classes

If you’re working in an object oriented style, it’s highly recommended when working with classes to place all class definition files within a separate directory. If you’ve not worked with object oriented PHP before, a class looks like the following and can contain properties (variables) and methods (functions):

1 class User {

2 public $auth = false;

3

4 public function login($username, $password, $remember) {

5 // some delightful code here

6 }

7 }

You may have a method to include a class as it’s instantiated (when a usable object is created from a class). This means that by placing a file, for example, User.php into your classes directory, you know exactly what the class name is, and you can then instantiate it.

You may even have an autoloader, which would include any class found within a particular directory when you instantiate it. This means that to make use of a class, all you’d need to do is create a new file with a valid class defined and then place it within this directory. You’re then free to instantiate it when required. Let’s take a look at how we can do this in practice with some code:

1 spl_autoload_register(function($class) {

2 require 'core/classes/' . $class . '.php';

3 });

How does this work? spl_autoload_register will automatically detect any classes that you instantiate. So when you do:

1 $user = new User();

This will find the file named User.php and include it ready for use. This is an extremely useful way of including classes, meaning you don’t have to include a long list of class includes like so:

1 require 'core/classes/Database.php';

2 require 'core/classes/User.php';

3 require 'core/classes/Topic.php';