Lesson 7 / الدرس 7

One file, one job / ملف واحد، عمل واحد

Everything so far fits in one file. Real applications do not, and the mechanism that splits them is require — which is also, when handed a value from a visitor, one of the most dangerous things in the language.

كل ما سبق يسع ملفًا واحدًا. والتطبيقات الحقيقية لا تسعه، والآلية التي تقسّمها هي require — وهي أيضًا، حين تُسلَّم قيمة من زائر، من أخطر ما في اللغة.

require 'file.php' runs that file right here, as if you had pasted it in. Its functions become available, and anything it prints appears at that point in the page. That is the entire mechanism — every framework's loading machinery is this with automation on top.

Three ways to load a file, and one to use

FormIf the file is missingUse it for
requireFatal error, execution stopsAnything the page cannot work without
includeWarning, execution continuesAlmost nothing — the page limps on broken
require_onceFatal, and never runs twiceFiles defining functions or classes

Use require, or require_once for definitions. include exists so that a missing file becomes a half-rendered page instead of an obvious failure, which is almost never what you want — a page that fails loudly gets fixed, and one that fails quietly ships.

The shape a small application takes

public/            <- the ONLY directory the web can reach
  index.php        <- the front controller; everything arrives here
  assets/

src/               <- your code. Above the web root, unreachable.
  bootstrap.php    <- loads everything, once
  Content.php

views/             <- the HTML-with-holes files
  layout.php
  home.php

content/           <- data
config.php         <- credentials. Never in git.
The important line is the first one. Only public/ is reachable over the web; everything else sits one level above it, where no URL can name it. If config.php lived inside public/, a misconfigured server that stopped executing PHP would serve your database password as plain text.
<?php
// src/bootstrap.php — loaded once, by the front controller.
declare(strict_types=1);

require __DIR__ . '/../config.php';
require __DIR__ . '/helpers.php';
require __DIR__ . '/Content.php';

// __DIR__ is the directory of THIS file, always. A bare
// require 'helpers.php' is resolved against the working
// directory instead, which is whatever the caller happened
// to be in — so it works until the day the file is required
// from somewhere else.
Every path in a require should start with __DIR__. It is the directory of the file doing the requiring, so the path means the same thing no matter who called it — a class of bug that is very hard to see and trivial to prevent.

Check yourself / اختبر نفسك

1. Why does only public/ sit inside the web root?

2. Why must a require path never be built from $_GET?

3. Why start every require path with __DIR__?