In PHP, how does require_once decide a file was already loaded, and what does it return on the second call?
answer
- one table per request
- keyed by the resolved real path
- second call evaluates to true
- plain include ignores the table
- config via require_once returns true twice
basics
~20 srequire_once resolves the path to a real file path and checks a per-request table of files already compiled; if the file is there it skips it and evaluates to true, otherwise it loads the file like require.
solid answer
~40 sPHP keeps a per-request table of every file it has compiled, the same list `get_included_files()` returns. `include_once` and `require_once` first **resolve** the path (include path lookup plus `realpath`, so symlinks and `../` segments collapse to one canonical path) and look it up in that table. If the file is already there, nothing is executed and the expression evaluates to `true`; otherwise the file is loaded exactly like `include` or `require`. Files loaded by plain `include`/`require` are in the table too, so a later `_once` skips them, but a plain `include` never checks it and runs the file again, which fails with "Cannot redeclare function" if the file declares functions. The classic bug is `$config = require_once 'config.php';` in two places: the second call returns `true`, not the array.
code
php · 11 lines<?php
declare(strict_types=1);
// lib/money.php declares function format_price(); nothing else
require __DIR__ . '/lib/money.php';
var_dump(require_once __DIR__ . '/lib/../lib/money.php'); // bool(true): same real path
var_dump(count(get_included_files())); // int(2): this script and money.php
require __DIR__ . '/lib/money.php';
// Fatal error: Cannot redeclare function format_price()go deeper
Know that the _once forms load a file at most once per request and exist to avoid redeclaring functions and classes.
Explain the resolved-path table, the true return on a repeat call, and why mixing plain and _once calls depends on order.
Spot the config trap where a second require_once returns true, and move class loading to an autoloader rather than chains of require_once.
Use the remaining _once calls as an inventory of code not yet covered by autoloading or explicit bootstrapping, and retire them deliberately.
## The problem _once solves A file that declares functions, classes or constants can only run once per request. Loading it a second time fails with a fatal error such as `Cannot redeclare function format_price()`. In code without an autoloader, several files often depend on the same library file, and the easiest guard is `require_once`. ## How the check works PHP maintains a hash table of **included files** for the current request. Every file that gets compiled is added to it: the main script, and every file loaded through `include`, `require`, `include_once` or `require_once`. `get_included_files()` returns its keys; `get_required_files()` is an alias. For `include_once` and `require_once`, the engine then does this: 1. **Resolve** the given path the same way `include` would: absolute and `./`/`../` paths directly, other relative paths through the `include_path` and the including file's directory. 2. Turn the result into a canonical **real path**, so `lib/../lib/db.php`, a symlink and an absolute path to the same file all become one key. 3. If that key is already in the table, skip the file and evaluate to **`true`**. 4. Otherwise open, compile and run the file like `include`/`require`, and record it. If the file cannot be found, the failure behaviour is that of the base construct: `include_once` warns and evaluates to `false`, `require_once` warns and throws `Error`. | Call | File not yet loaded | File already loaded | File missing | |---|---|---|---| | `include 'f.php'` | runs, returns value or `1` | **runs again** | warnings, `false` | | `include_once 'f.php'` | runs, returns value or `1` | skipped, `true` | warnings, `false` | | `require_once 'f.php'` | runs, returns value or `1` | skipped, `true` | warning, throws `Error` | ## Mixing plain and _once calls Because every compiled file lands in the same table, the order matters: - `require 'lib.php';` followed by `require_once 'lib.php';` loads the file **once**: the second call finds it in the table. - `require_once 'lib.php';` followed by `require 'lib.php';` loads it **twice**: the plain form never checks the table, so a file declaring functions dies with "Cannot redeclare". ## The config-file trap A file that returns a value, such as `return ['db' => [...]];`, gives its value only when it actually runs: ```php <?php $a = require_once __DIR__ . '/config.php'; // array $b = require_once __DIR__ . '/config.php'; // true, not the array ``` The second caller receives `true`; reading `$b['dsn']` then raises a "Trying to access array offset" warning and yields `null`, and passing `$b` to an `array` parameter throws `TypeError`. Files that return values should be loaded with plain `require`, and the result cached in a variable or an object if it is needed in several places. ## Where _once does not help The table only prevents **the same file** from running twice. It does not help when: 1. Two **copies** of a library live at different paths, for example a vendored copy and a second copy in another directory. Both load, and the second one fails with "Cannot redeclare" or, for classes, with a class-already-declared error. 2. A file must run **again on purpose**, as a template does for each rendering; then plain `include` is the right tool. ## Cost and the modern alternative The `_once` check costs a path resolution per call even when the file is skipped. The engine keeps a cache of resolved real paths, and OPcache avoids recompiling, so the overhead is small, but it is not zero in code that calls `require_once` for dozens of files on every request. In current PHP codebases, classes are loaded by an **autoloader** registered with `spl_autoload_register()` (Composer generates one), which loads a class file only when the class is first used. Explicit `require_once` then survives mainly in three places: - the entry point requiring the autoloader file; - files of plain functions, which cannot be autoloaded; - legacy code that predates the autoloader. ## Summary of rules - `_once` compares **resolved real paths**, not the strings you wrote. - The table is **per request**; a new request starts empty. - An already-loaded file makes `_once` evaluate to `true`. - Plain `include`/`require` never skip anything.
- In PHP, why can two different relative paths to the same file still be caught by include_once?`include_once` resolves the string to a canonical real path before checking the included-files table, so `lib/../lib/db.php`, `./lib/db.php` from the right directory and a symlink to the file all map to the same key. The comparison is on the resolved file, not on the literal you wrote.
- In PHP, what does get_included_files() return?An array of the resolved paths of every file compiled in the current request, in load order, starting with the main script. It includes files loaded by plain `include`/`require` and by the `_once` forms; `get_required_files()` is an alias. It is handy for checking which files an entry point pulls in.
saying these in an interview costs you the question
- require_once compares the literal path strings you pass it
- require_once returns the file's return value every time
- A plain include after require_once is skipped automatically
- The included-files table persists across requests under PHP-FPM
- _once has zero cost when the file was already loaded