Codeskill

Learn to code, step by step

PHP project layout that does not turn into spaghetti

PHP project layout – how to organise files so a small app does not turn into a single 2,000-line script named index.php with everything crammed inside.

The public folder pattern

The most important habit: only one folder should be web-accessible. Call it public/. Your web server document root points there. Everything else – config, classes, templates, vendor – lives outside it and cannot be fetched directly.

my-app/
├── public/
│   └── index.php          # front controller (entry point)
├── src/
│   ├── Controllers/
│   ├── Models/
│   └── Services/
├── templates/
├── config/
│   └── app.php
├── storage/
│   └── logs/
├── vendor/                # Composer (later)
└── composer.json

Apache or nginx serves only public/. A request to /../../config/app.php should fail – because that path is not under the document root.

Separate concerns by folder

You do not need a framework’s folder names, but the idea is the same:

  • Controllers – read the request, call services, pick a response
  • Models – data access (database queries, save/load records)
  • Services – business logic that does not belong in a controller
  • templates/ – HTML views (plain PHP or Twig later)
  • config/ – settings returned as PHP arrays
  • storage/ – logs, uploads, cache files (not in git if generated)

A minimal public/index.php

Every request hits one file first. That file bootstraps the app and hands off to a router (we build one in a later tutorial). For now:

<?php
declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

$config = require dirname(__DIR__) . '/config/app.php';

// Router and dispatch come later
echo 'App bootstrapped';

Naming and namespaces

Match folder names to PHP namespaces. A class AppControllersHomeController lives in src/Controllers/HomeController.php. Composer’s autoloader maps this for you – we set that up in the next tutorial.

What not to do

  • Mix HTML, SQL, and validation in one file with no functions
  • Store uploaded files next to PHP scripts in public/
  • Commit .env or database passwords to git
  • Create a includes/ folder and require twenty files by hand with no autoloader

Start simple. Two or three folders beat a perfect enterprise layout you never finish. You can refactor when the project grows – but keeping the document root tight is worth doing on day one.

PreviousGoing further with PHP