include vs require vs *_once
debt(d5/e5/b3/t5)
Closest to 'specialist tool catches it' (d5). The detection_hints list semgrep, phpstan, and rector as the tools that catch misuse patterns (variable paths, wrong variant choice, missing _once). These are not default linters but specialist static analysis tools — a developer must opt into them. The wrong choice (include vs require) won't produce any compiler or syntax error, and a standard linter won't flag it by default.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix prescribes migrating to Composer autoloading rather than a simple one-line swap. Replacing manual include/require statements with autoloading requires touching composer.json, ensuring PSR-4 namespace mapping, and removing include/require calls across potentially many files. This is a meaningful refactor, though contained within the dependency/loading layer rather than a full architectural overhaul.
Closest to 'localised tax' (b3). The applies_to covers web and cli contexts broadly, so misuse can appear in many files. However, each misuse is relatively self-contained — a wrong include vs require choice or a missing _once doesn't infect unrelated parts of the codebase in a deeply structural way. The burden is a persistent but localised tax: developers must remember the correct variant each time they add a file inclusion, but it doesn't reshape the entire system's architecture.
Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field explicitly states that include and require are treated as interchangeable by many developers, when in fact they differ critically on failure behavior (warning + continue vs fatal error + halt). The common_mistakes also highlight the _once redeclaration trap. These are well-documented gotchas that experienced PHP developers learn, but competent developers new to PHP will commonly guess wrong initially.
Also Known As
TL;DR
Explanation
PHP has four file-loading constructs. require emits a fatal error and halts if the file is missing — use for files the application cannot function without. include only emits a warning and continues — use for optional components. require_once and include_once track loaded files and skip re-loading, preventing function/class redeclaration errors. Best practice: use require_once for class and library files, require for critical config, include for optional template partials. Never use any of them with user-supplied paths.
Common Misconception
Why It Matters
Common Mistakes
- Using include() for core application files — a missing controller or model should be a fatal error, not a silent skip.
- Using require() for optional features or plugins that may legitimately not exist.
- Not using _once variants for files that define classes or functions — double inclusion causes fatal redeclaration errors.
- Using user-controlled paths in include/require — enables local and remote file inclusion attacks.
Code Examples
// Dynamic include with user input — LFI
\$page = \$_GET['page'];
include "pages/\$page.php"; // ?page=../../etc/passwd
// require = fatal error if missing (critical files)
// include = warning only (optional parts)
// _once variants prevent re-inclusion
require_once 'bootstrap/app.php'; // must exist
include_once 'partials/sidebar.php'; // optional
// NEVER include user-supplied paths — use an allowlist:
\$allowed = ['home', 'about', 'contact'];
\$page = in_array(\$_GET['page'] ?? '', \$allowed, true) ? \$_GET['page'] : 'home';
include "pages/{\$page}.php";
// php.ini: allow_url_include = Off (always off)