2.3Includes, organization & file handling
The essentials in 30 seconds
- Write once, include everywhere: header, footer, config and functions live in dedicated files assembled with require/include — zero duplication.
- require for the essential: require fails fatally if the file is missing (config, functions), include only warns — *_once avoids redefinitions.
- Uploads = hostile input: real type checked, size limited, file renamed, storage with no PHP execution — an uploaded executable PHP file hands the server to the attacker.
Running mini-case — Riad tidies up the association website
Riad's association website is growing: the same menu copied into ten pages, the database connection copied everywhere — each change becomes painful. He tidies up: config.php for settings, header.php and footer.php for layout, fonctions.php for utilities, each page assembled with require. Then he adds event photo uploads: the upload is treated as hostile input — real type checked, file renamed, stored with no PHP execution. In 2.4, he will timestamp these photos with DateTime.
Sequel to thread 2.2: Riad's site moves from duplicated tinkering to a clean structure — and continues in 2.4.
1. Includes: write once, reuse everywhere
A real site shares common elements (header, menu, footer, configuration): duplicating them in every file is tedious and unmaintainable. PHP offers inclusion statements: include and require insert the contents of one file into another. The difference: require triggers a FATAL ERROR if the file cannot be found — for ESSENTIAL files (configuration, core functions); include only emits a warning and continues — for optional files. Their require_once and include_once variants guarantee a UNIQUE inclusion (no redefinitions) — often preferable. Typical: header.php and footer.php included in each page, config.php required at the top of scripts. Riad now edits his menu in a single place. A crucial SECURITY point: NEVER include a path coming from user input (include $_GET['page']) — it is an arbitrary file inclusion flaw.
2. Organizing before frameworks
These inclusions underpin the organization of a hand-built project: splitting the code into logical files (configuration, functions, interface elements, pages) and assembling them — a first step towards a clean architecture, leading to MVC (section 4-1) and autoloading (section 3-3). The principles: SEPARATE by responsibility, REUSE through includes, SEPARATE logic (processing, queries) from display (the HTML afterwards), arrange into clear folders (includes/, pages, assets/, secured uploads/), and place sensitive files (config with credentials) OUTSIDE the web root. Riad's site adopts this structure: every page starts with requiring the config, does its logic, then includes the display.
3. Files and uploads: read, write, stay suspicious
PHP READS and WRITES server files: file_get_contents() (read all), file_put_contents() (write), fopen()/fread()/fwrite()/fclose() (step by step, large files), file_exists(), unlink() (delete). The sensitive case: uploads, files sent by the user in $_FILES — to treat as hostile input. The protections, all together: CHECK the REAL type (the content, not the declared extension, which can be forged) with an allowlist and the SIZE; RENAME the file (never the user name); store it OUTSIDE the web root or in a folder with NO PHP execution — otherwise a malicious uploaded PHP file called by URL runs and compromises the server; move it with move_uploaded_file() after checking the error code. Riad's event photos follow exactly this protocol.
🔎 Concrete example — 🔎 Concrete example — Riad's event photos, secured
All the protections, as for Riad's uploads:
// check error, size, REAL TYPE (allowlist)
$nom = bin2hex(random_bytes(8)) . '.jpg';
move_uploaded_file($_FILES['img']['tmp_name'],
__DIR__ . '/../uploads/' . $nom);
// folder with no PHP execution
Reading: content checked, random renaming, storage with no execution.
Lesson: never keep the user-supplied name.
Section vocabulary
- include / require
- Insert the contents of one file into another; require triggers a FATAL error if missing (essentials), include a warning and continues (optionals).
Simple example: Riad requires config.php at the top of each page.
What it is not: copy-pasting the same menu into ten pages.
Reflex: essential = require.
- require_once / include_once
- Guarantee that a file is included only ONCE, avoiding multiple inclusions and redefinition errors — often preferable.
Simple example: fonctions.php is required with _once on all of Riad's pages.
What it is not: a repeated inclusion that redefines the same functions.
Reflex: shared file = _once by default.
- Organization with includes
- Splitting code into logical files (config, functions, header/footer, pages) assembled with includes; a first step towards a clean architecture (MVC, autoloading).
Simple example: Riad's site: config, header/footer, assembled pages.
What it is not: one big file mixing logic, queries and duplicated HTML.
Reflex: duplicated code = file to extract and include.
- File handling
- Reading and writing server files: file_get_contents, file_put_contents, fopen/fread/fwrite, file_exists, unlink.
Simple example: Riad reads a template and writes an export with file_get_contents/put_contents.
What it is not: an upload processed with no checks at all.
Reflex: server file = check existence and rights.
- Uploads ($_FILES) — security
- User-sent files ($_FILES): check real type and size, RENAME, store outside the root or with no PHP execution, limit extensions.
Simple example: Each of Riad's photos is renamed and stored with no PHP execution.
What it is not: a file kept under its original name in an executable folder.
Reflex: upload = hostile input, full protocol.
In practice — In practice — Organizing code and handling files
- Create header.php and footer.php and include them in several pages with require; create a config.php required at the top of scripts.
- Understand the require (fatal) / include (warning) difference and the point of require_once (unique inclusion).
- Read and write a file (file_get_contents / file_put_contents); check its existence (file_exists).
- Process an upload ($_FILES) SECURELY: check real type and size, RENAME the file, store it in a folder with no PHP execution — and NEVER include a path coming from the user.
Ready-to-say script — explaining the tidy-up to the association office (30 s)
“I tidied up the site: the menu and footer exist in a single copy; each page includes them — a change is made in one place. And for photos, each file is checked, renamed and stored in a folder where no script can run: even a trapped file stays harmless.”
Say it while showing a menu change applied everywhere instantly.
Copy-paste template — assembled page skeleton
Small showcase site
Five pages share header and footer: Riad fixes the menu once, the whole site follows.
Protected config
Database credentials outside the web root, required at the top: never exposed by URL, never in the repository.
Data export
The member list is written with file_put_contents after file_exists: controlled reading and writing.
Event photos
Renamed uploads, real type checked, folder with no PHP execution: the gallery stays harmless.
✓ Best practices
- Split code into logical files and reuse through includes.
- Prefer require for essential files and *_once against redefinitions.
- Treat uploads with all the protections (real type, renaming, no execution).
✗ Common pitfalls
- Including a path coming from user input (arbitrary file inclusion).
- Keeping a user-supplied name for an uploaded file.
- Storing uploads in a folder where PHP runs (takeover).
Key takeaways
- INCLUDES (include/require): inserting one file into another to avoid duplicating (header, footer, config, functions). require = fatal if missing (essentials); include = warning (optionals); *_once = unique inclusion.
- Organizing code into logical files assembled with includes = a first step towards a clean architecture (before MVC/autoloading).
- FLAW TO AVOID: NEVER include a path coming from USER input (include $_GET['page']) — arbitrary file inclusion.
- FILE HANDLING (file_get_contents/put_contents, fopen…) and above all SECURE UPLOADS ($_FILES): check real type/size, RENAME, store with no PHP execution, limit extensions (a classic source of serious flaws).
48h challenge — apply it tomorrow
Factor a real project: extract header, footer and config into files included with require/require_once, logic before display. Then secure a real upload: real type, size, renaming, folder with no PHP execution.
- I can explain require vs include and the point of *_once.
- I can structure a project (config, header/footer, logic before display).
- I have secured a real upload with the full protocol.
Frequently asked questions
How do I cleanly organize a hand-built PHP project before using a framework?
Aim for SEPARATION and REUSE, against “spaghetti code” (everything mixed, duplicated, unstructured). (1) SEPARATE into logical files: configuration (config.php), reusable functions (or classes, level 3), shared interface (header.php, footer.php), pages (specific content). (2) REUSE through includes: written once, included everywhere — one change in a single place. (3) SEPARATE logic and display: logic first (processing, queries), HTML afterwards — the path towards MVC (section 4-1). (4) STRUCTURE the folders: includes/, pages, assets/, secured uploads/. (5) SECURE: sensitive files OUTSIDE the web root, ideally a single entry point, secrets out of the repository. This organization teaches the principles that FRAMEWORKS formalize (MVC, routing, autoloading, config, security).
Why are file uploads so security-sensitive, and how do I handle them safely?
An upload lets a user DROP a file onto YOUR server. The worst scenario: a malicious .php file uploaded then called by URL — the server RUNS it and the attacker takes control (files, database, everything). Other risks: huge files (denial of service), misleading types, overwriting. Protections, all together: (1) NEVER execute — folder with no PHP or OUTSIDE the web root: THE key protection. (2) RENAME (never the user name) with a safe name and a controlled extension. (3) CHECK the REAL TYPE (content, not the declared extension) — allowlist. (4) LIMIT the size. (5) Validate the extension (allowlist). (6) Check the $_FILES error code and move with move_uploaded_file(). In a real project, prefer the proven tools of frameworks for such a critical topic.
What is THE key protection for uploaded files?