The ultimate guide to PHP Project Structure

Share This Post

Share on linkedin
Share on facebook
Share on twitter
Share on email

PHP gives you a lot of freedom, it almost has no rule. Thus, if you don’t impose yourself some constraints, you will end up with spaghetti code for sure. This is the case of many “quick” PHP project that then grew organically. In this article, we see how to ensure your beautiful PHP script won’t become a mess over time. In fact, the most important tool to sleep at night is having a good structure. We will see the best PHP Project Structure, and why it is important to stick with it.

The Ultimate PHP Project Structure

Namespaces and Composer

Before we start diving into our PHP project structure, we need to have some tools to create it. Luckily, those tools are widespread in modern PHP. The first tool you need is Namespaces. With a namespace, you can group a portion of your code together. Typically, you group them by functions or libraries. Each PHP file can belong to a single namespace, and you tell that by writing the following code at the beginning.

namespace MyNamespace;

You can also nest namespaces into one another, like namespace MyNamespace\SubNamespace. Then, you can use the same class names in different namespaces, they will still remain different classes with no conflict. Later, when you want to use something from another namespace, you need to write the use directive, like so:

use MyNamespace\SubNamespace\MyClass;

However, that class is likely to be in another file. Thus, we need to use the require or require_once functions, but those are ugly tools of the past. If you have composer, it takes care automatically of your dependencies, provided that your folder structure reflect the namespaces. In other words, you should have at the root of your project a folder named MyNamespace, which then contains another folder named SubNamespace which then contains a file that defines MyClass.

Today, we assume you are familiar with those concepts. If you aren’t, start from this composer tutorial first.

Rule #1: Don’t mix PHP with HTML

With PHP, you can have in the same file both PHP (backend processing code) and HTML (frontend rendering code). Doesn’t it sound a little bit weird? The first and most important rule of this PHP project structure guide is to keep things separated. You want to have your backend logic in one place, and your rendering code in another place. So, something like the following is a complete failure.

<?php
   function myFunc () {
      // Blah
   }
   // Some other code ...
?>

 <html>
   ...

Of course, eventually, your render code needs to touch your logic at some point. But you should limit these points of contacts the most. Don’t worry, in this PHP Project Structure tutorial we will see how to achieve that.

The overall PHP Project Structure

There is no general rule about a PHP project structure. However, there is a kind of structure that works well under virtually any circumstance. This is because this structure is flexible, modular, and scalable. In your project root, you want to have this structure.

PHP project structure: use one folder for public files, one for the modules, and one for the templates to render pages.

We can break it down as follows.

  • The public folder contains all the files that are directly accessible via HTTP/HTTPS. These may include stuff for the client, like CSS, Javascript and images, and the basic PHP pages
  • The Modules folder will contain all the logic of your application. Each component of your application will have its own folder here.
  • Instead, the Templates folder contains all the PHP code to render the pages. This is also modularized in sub-folders, just like Modules.

On top of that, we need to add the vendor folder, this is where composer will install the libraries you will require.

Modules

Modules are at the core of our PHP project structure. Here, each sub-folder is a module. You can think about a module like a piece of your application, a group of components that are closely related with each other, and that all work together to provide the same set of results. For example, you may have a module for the authentication which contains – in several files – the code for login, logout and even registration.

Each module will be in a namespace, for example, the authentication module could be in the folder (and namespace) Modules\Auth. This will make your code extremely flexible and modular. In fact, you are ready to take your module out and make it a standalone component in case it becomes too big.

You are free to write the code you’d like in the modules, but the only rule is zero HTML in the modules folder. Not even a bit. Literally zero. In the end, a module is a processor: something that takes some data as input and returns a result. This can be achieved with a class or function that you may want to expose to the outside of the module. That function should return an array. For example, you could have a function for authentication that looks something like this:

<?php
   function authenticate ($username, $password) {
    // Do some stuff ...
    return [
       'user_id' => 1,
       'last_login' => '2019-05-01 14:32',
       // Some other stuff ...
    ];
   }
?>

Templates

The second important part of our PHP project structure is the template folder. Here, grouped in sub-folders, you have the code that actually renders the HTML. Here, you should have files with almost no logic and much HTML. For example, a file may contain the following code.

<html>
  <!-- Some code ... -->
  <body>
     <!-- Some other code ... -->
     User name is <? echo $result['username']; ?>
  </body>
</html>

Here, you may also have if-else statements or loops that are related to rendering. For example, a loop may be ideal to render a list. Here, templates are grouped into folders based on their area of the applications. It would be great, for example, to have all the pages related to the, say, authentication, within the same sub-folder.

Joining Modules and Templates

Now we have our modules that returns the data with no style. In another place, we have the templates that apply a style to unexisting data. And yet, none of that is directly accessible to the user. We obviously need to do some other thing, and we can do that in public/index.php.

In that folder, we map URLs to modules and templates. We basically want to detect the URL, then load the proper module accordingly and return the result of the module elaboration, after template parsing. We can also process friendly URLs, but before we do that we need to configure our web server so that all the request of the user are directed to index.php. In Apache, we do that with .htaccess file. This is very useful, because you start to have your URL-to-code mapping inside in PHP, and not in the web server configuration. Later, you can move to a different web server and continue having everything in place.

A quick tip, send all the URLs to index.php except, say, all URLs starting with /assets/. Then, you can have an asset folder where you put images, javascript, CSS, and other static files. Otherwise, you will need to define the rules to load them in PHP as well. It would be a waste of precious time!

An example index.php

Below, an example index.php to load a module and return the associated page. Of course, this is an extreme oversimplification, just to make the point.

<?php
  use Auth\Login;
   
   if ($_SERVER['REQUEST_URI'] === '/authentication') {
      $result = Login($_REQUEST['username'], $_REQUEST['password']);
      include_once "../Templates/Auth/login_results.php"
   }
   
?>

One of the quick improvements we can apply here is the use of regular expressions. You could also apply some error-handling that applies to other pages and so on. Just note we populated the $result variable just because later in the template we will reference that variable inside HTML.

Stick with a naming convention

With this PHP project structure, your PHP application will be much more maintainable. One additional thing, not strictly related to the PHP project structure, is the naming convention. You can have snake casing, camel casing and so on. You can put curly brackets on the same line of the if, or on the next line. PHP gives you freedom on that. Use that freedom, but we consistent with it.

It does not matter which language style you choose, it matters if you stick with it. If all classes starts with uppercase character, ensure that actually all classes do that.

Wrapping it up

In this quick article we saw how to create a maintainable and durable PHP Project Structure. Feel free to tweak this structure to better reflect your needs, as long as you maintain it modularized. In fact, many popular PHP frameworks like Laravel work with a very similar approach. Of course, this structure will give you a little more work to do in the beginning, but will save you an incredible amount of time in the long run. What do you think of it? Let me know in the comments!

Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.
Alessandro Maggio

Alessandro Maggio

Project manager, critical-thinker, passionate about networking & coding. I believe that time is the most precious resource we have, and that technology can help us not to waste it. I founded ICTShore.com with the same principle: I share what I learn so that you get value from it faster than I did.

Join the Newsletter to Get Ahead

Revolutionary tips to get ahead with technology directly in your Inbox.

Alessandro Maggio

2019-06-27T16:30:47+00:00

Unspecified

PHP

Unspecified

Want Visibility from Tech Professionals?

If you feel like sharing your knowledge, we are open to guest posting - and it's free. Find out more now.