skip to content

In PHP, why can't a helper function read a config variable defined at the top of the script, and how should it get the value?

level: juniorimportance: must knowfreq 58%

answer

  1. function scope and global scope only
  2. no automatic access to outer variables
  3. no block scope in if or for
  4. undefined variable warning, value null
  5. pass it as a parameter

basics

~20 s

PHP has function scope and global scope, and a function sees only its own variables, so an outer $config is undefined inside it. Pass the value as a parameter; global and $GLOBALS work but hide the dependency.

solid answer

~40 s

PHP has only **function scope** and **global scope**. Unlike many languages, a function does **not** see variables from the surrounding file: inside `function unitLabel() { return $config['units']; }` the name `$config` is a new, undefined local, so PHP 8 raises an "Undefined variable" `E_WARNING` and uses `null`. The clean fix is to **pass the value in**: `unitLabel(array $config)` or, better, a typed value such as a small config object. `global $config;` or `$GLOBALS['config']` also work, but they hide the dependency, make the function hard to test and let anything change the value. PHP also has **no block scope**: a variable assigned inside an `if` or a `for` body still exists after the block. Arrow functions are the exception to the first rule: they capture outer variables by value automatically.

go deeper

for a junior

Recall that functions do not see outer variables, that PHP has no block scope, and that the fix is to pass values as parameters.

for a middle

Explain the undefined-variable warning and null value, how include inherits scope, and how closures differ from named functions.

for a senior

Remove global state from helpers: turn configuration into constructor-injected objects so functions are testable and dependencies are visible.

for a principal

Set the codebase rule that configuration flows through explicit dependencies, and enforce it with static analysis that flags global and $GLOBALS use.

## The two scopes The PHP manual puts it in one line: PHP has a **function scope** and a **global scope**. - **Global scope**: variables assigned outside any function, in the main script or in files it includes at top level. - **Function scope**: each call of a function or method gets its own local variables. They start empty, apart from the parameters, and disappear when the call returns. The rule that surprises people coming from other languages: **a function does not inherit variables from the code around it**. The manual contrasts this with C, where globals are visible inside functions unless shadowed; in PHP a global must be imported explicitly if a function is to use it. ## The dashboard example A weather-station dashboard loads its settings at the top of `index.php` and formats readings in a helper: ```php <?php $config = ['units' => 'metric', 'station' => 'north']; function formatTemp(float $celsius): string { // $config here is a new local variable, not the one above. return $config['units'] === 'imperial' // Warning: Undefined variable $config ? round($celsius * 9 / 5 + 32, 1) . ' F' : round($celsius, 1) . ' C'; } ``` On PHP 8.5 reading `$config` raises `E_WARNING` "Undefined variable $config" (a notice before 8.0), the value is `null`, and reading an offset of `null` adds a second warning and yields `null` again. The helper falls through to the metric branch, so the bug shows up as a wrong unit plus warnings in the log, not as a crash. ## Ways to give the function the value | Approach | Example | Trade-off | |---|---|---| | **Parameter** | `formatTemp(float $c, string $units)` | explicit, testable, the default choice | | **Object dependency** | a `TemperatureFormatter` constructed with its settings | scales to many settings and to dependency injection | | **`global` keyword** | `global $config;` inside the function | hidden dependency, any code can change it | | **`$GLOBALS` array** | `$GLOBALS['config']['units']` | same drawbacks, but explicit at each use | | **Constant** | `const UNITS = 'metric';` | fine for true constants, not for runtime settings | The manual's own contrast with C gives the reason globals are opt-in: code inside a function cannot change a global by accident. Parameters keep that property; `global` gives it up. ## No block scope The second half of the rule is that **blocks do not create scopes**. Braces around an `if`, `for`, `while` or `foreach` body only group statements: - a variable assigned inside an `if` body exists after the `if` ends; - a loop variable such as `$i` in `for ($i = 0; ...)` keeps its last value after the loop; - a variable assigned in one branch and read after the `if` is undefined only when that branch did not run. This is why static analysers warn about variables that "might not be defined": the variable exists in the function's scope, but not on every path. ## Includes and closures Two related rules complete the picture: 1. **`include` inherits the scope of the line where it happens.** A file included at top level sees the globals; a file included inside a function sees that function's locals. 2. **Closures do not see outer variables unless told to.** An anonymous `function () use ($config) { ... }` captures listed variables; an arrow function `fn ($c) => ...` captures used variables by value automatically. Capture rules are their own topic. ## What interviewers look for - Function scope plus global scope, and no automatic access to outer variables. - No block scope. - The PHP 8 warning and `null` value on an undefined variable. - Passing values as parameters in preference to `global`.

  • Does a variable assigned inside a PHP if block exist after the block?
    Yes. PHP has no block scope, so braces around an `if`, `for` or `foreach` body do not limit variables. A variable assigned inside the body exists in the enclosing function or global scope afterwards, but only if that code path ran; otherwise reading it raises an undefined-variable warning.
  • Why prefer a parameter over the global keyword for a config value?
    A parameter makes the dependency visible in the signature, lets tests pass any value, and cannot be changed by unrelated code. `global` hides the dependency inside the body, ties the function to one global name, and shares one mutable value across everything that imports it.

Each function call is a separate room with its own whiteboard; names written on the hallway whiteboard are not visible inside unless someone carries the value in through the door as a parameter.

saying these in an interview costs you the question

  • A PHP function can read any variable defined earlier in the same file.
  • Variables declared inside an if block are destroyed when the block ends.
  • Reading an undefined variable throws an exception in PHP 8.
  • The global keyword is the recommended way to share configuration.
  • An anonymous function sees outer variables without a use clause.