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.
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!