In PHP, what does $config = require __DIR__ . '/config.php'; evaluate to, and how does an included file return a value?
answer
- include is an expression
- top-level return ends the included file
- no return statement means int 1
- failed include evaluates to false
- (include 'f.php') == true needs parentheses
basics
~20 sAn included file can end with a top-level return statement, and include or require then evaluates to that value, so config.php can return an array. Without a return the expression is int 1, and a failed include gives false.
solid answer
~40 s`include` and `require` are **expressions**. A top-level `return` inside the included file stops executing that file and hands its operand back to the caller, so `config.php` containing `return ['db' => ['dsn' => '...']];` makes `$config = require __DIR__ . '/config.php';` an array. If the file has no `return`, the expression evaluates to `1`; a failed `include` evaluates to `false`. This pattern keeps configuration free of global variables and makes the dependency explicit at the call site. Two gotchas: `include` is a construct, so `include('a.php') == true` parses as `include (('a.php') == true)`; and a repeated `require_once` evaluates to `true`, not to the file's value, so value-returning files use plain `require`.
code
php · 11 lines<?php
declare(strict_types=1);
// config/app.php contains: <?php return ['debug' => false, 'locale' => 'en'];
$app = require __DIR__ . '/config/app.php';
var_dump($app['locale']); // string(2) "en"
// partials/footer.php has no return statement
var_dump(include __DIR__ . '/partials/footer.php'); // prints the footer, then int(1)
var_dump(require_once __DIR__ . '/config/app.php'); // bool(true): already loadedgo deeper
Know that a config file can end with return [...], and that $config = require 'config.php'; then holds that array.
List the four possible values: the returned value, 1, false for a failed include, and true for a repeated _once, and explain the precedence gotcha.
Prefer value-returning config files over shared globals, load them with plain require, and let OPcache keep compiled arrays warm.
Standardise configuration as returned arrays or objects loaded in one bootstrap, so dependencies are explicit and environments differ only in data.
## include and require are expressions Although they are usually written as statements, `include`, `require` and their `_once` forms are **expressions** with a value. That value comes from one of four places: | Situation | Value of the expression | |---|---| | File runs a top-level `return $x;` | `$x` | | File runs to the end without `return` | `1` | | `include`/`include_once` cannot open the file | `false` (plus warnings) | | `include_once`/`require_once` finds the file already loaded | `true` | A failed `require` has no value at all, because it throws `Error`. ## Returning from an included file A `return` statement at the **top level** of an included file, outside any function, ends execution of that file immediately and passes its operand to the includer. Code after it in the file does not run, although functions declared further down at the top level are still defined, because PHP binds top-level function declarations when it compiles the file. This gives a clean configuration pattern: ```php <?php // config/database.php declare(strict_types=1); return [ 'dsn' => 'pgsql:host=db;dbname=shop', 'user' => 'shop', 'password' => getenv('DB_PASSWORD') ?: '', ]; ``` ```php <?php // bootstrap.php $db = require __DIR__ . '/config/database.php'; ``` Compared with the older habit of setting variables such as `$db_user` in a shared file: - **No hidden globals.** The value arrives through one assignment, so a reader can see where `$db` comes from. - **Composable.** Several files can each return a piece, and the bootstrap merges them. - **Testable.** A test can require the file and assert on the array without running the whole application. - **Cache-friendly.** A file that returns a constant array compiles to little more than that array, and OPcache keeps the compiled result between requests. ## The precedence gotcha Because `include` is a construct that takes the whole following expression as its operand, parentheses do not work like a function call: ```php <?php if (include('vars.php') == true) { } // include(('vars.php') == true): loads "1" if ((include 'vars.php') == true) { } // intended: compare the result ``` The first line compares the string `'vars.php'` with `true` first, which is `true`, then includes a file named `1`. Wrapping the whole `include` in parentheses fixes it. The same applies when concatenating: `require __DIR__ . '/x.php'` works because the entire concatenation is the operand. ## Pitfalls with return values 1. **`require_once` and values.** The first call returns the array; every later call returns `true`. Use plain `require` for files that return data, and keep the result. 2. **Returning from the main script.** A top-level `return` in the main script simply ends it; the value is discarded. 3. **Output before `return`.** Anything outside `<?php` tags in the included file is sent as output, including blank lines after a closing `?>` beyond the single newline PHP swallows. Omitting the closing tag in pure-PHP files avoids stray whitespace. 4. **Checking for failure.** With `include`, compare the result strictly: `false` means failure, but a file may legitimately `return false;` too, so a file that returns data should not use `false` as a value. ## Returning objects and closures The returned value can be anything, not only an array: - An **object**, for example a configured service or a value object built from environment variables. - A **closure**, which lets a file return a factory: `return static fn (array $env): Router => new Router($env['BASE_URL']);`. The caller invokes it with the data it needs. - A **scalar**, such as a version string generated at build time. Because the file runs every time it is included with plain `require`, a returned object is a fresh instance on each load; cache the result in the caller if one shared instance is wanted. ## Environment-specific files A common layout keeps one file per concern and optional overrides: - `config/app.php` returns defaults and is always loaded with `require`. - `config/app.local.php` is optional, loaded with `include` or behind `is_file()`, and merged over the defaults with `array_replace_recursive()`. - Secrets come from the environment through `getenv()` or `$_ENV`, not from files committed to the repository. ## Related tools - `get_included_files()` lists what has been loaded, useful to confirm which configuration file actually ran. - Output buffering with `ob_start()` and `ob_get_clean()` around an `include` captures a template's **output** as a string, which is different from its **return value**.
- In PHP, can an included file use return inside a function it defines to return from the include?No. A `return` inside a function returns from that function when it is called. Only a `return` at the top level of the included file, outside any function or method body, ends the file and supplies the value of the `include`/`require` expression.
- In PHP, why is include('vars.php') == true a bug?`include` is a language construct whose operand is the whole expression that follows. The parser reads `include (('vars.php') == true)`, compares the string with `true` first, which is true, and then tries to include a file named `1`. Write `(include 'vars.php') == true`, or better, assign the result and compare it with `===`.
saying these in an interview costs you the question
- A file without a return statement makes include evaluate to null
- include('a.php') == true compares the result of the include
- require_once returns the file's array on every call
- Code after a top-level return in an included file still runs
- Only require, not include, can return a value