Variables Scope
What Is Variable Scope?
In simple terms: Scope is the area of a program where a variable can be seen and used. A variable created in one place is not automatically available everywhere.
Technically: The scope of a variable is the context in which it is defined. PHP has a small number of scopes:
| Scope | Where Variables Are Created | Where They Are Visible |
|---|---|---|
| Global | Outside any function (the main script and the files it includes) | Only outside functions |
| Local (function) | Inside a function, including its parameters | Only inside that function call |
| Static | Inside a function, declared with static | Inside that function; value persists between calls |
| Superglobal | Built in ($_GET, $_POST, $_SERVER, …) | Everywhere |
Why Scope Exists
Imagine every variable were visible everywhere. A function using $total for its own calculation would overwrite a $total used by the main script, and a library function using $i would break your loop counter. Scope isolates code so that:
- Functions can use any variable names without colliding with the rest of the program
- A function's behavior depends only on its inputs (parameters), which makes it predictable and testable
- Bugs are easier to locate, because a variable can only be changed in a limited area
Global Scope
A variable created outside of any function has global scope. It is available anywhere in the main script — but not inside functions.
Example
<?php
$siteName = "GoLearn";
echo "Welcome to $siteName\n";
function showFooter()
{
echo "© 2026 $siteName\n";
}
showFooter();
Output
Welcome to GoLearn
PHP Warning: Undefined variable $siteName in /path/script.php on line 8
© 2026
Why This Happens
Inside showFooter(), PHP looks for $siteName in the function's local scope only. That scope is empty, so the variable is undefined.
This is a major difference from languages such as JavaScript, where inner functions can automatically read variables from outer scopes. In PHP, a regular function is completely isolated from global variables unless you explicitly bring them in.
Local Scope
A variable created inside a function has local scope. It exists only while that function runs and cannot be accessed from outside.
Example
<?php
function calculateDiscount()
{
$discountRate = 0.10;
$discount = 2000 * $discountRate;
echo "Discount inside function: $discount\n";
}
calculateDiscount();
echo "Discount outside function: $discount\n";
Output
Discount inside function: 200
PHP Warning: Undefined variable $discount in /path/script.php on line 12
Discount outside function:
How It Works
- When
calculateDiscount()is called, PHP creates a new, empty local scope. $discountRateand$discountare created in that scope.- When the function ends, the local scope and all its variables are destroyed.
- Outside the function,
$discountnever existed.
Each Call Gets a Fresh Scope
<?php
function countVisit()
{
$count = 0;
$count++;
echo "Count: $count\n";
}
countVisit();
countVisit();
countVisit();
Output
Count: 1
Count: 1
Count: 1
Every call starts with an empty scope, so $count is recreated as 0 each time. To keep a value between calls, use a static variable, covered in the next lesson.
Same Names, Different Variables
Because scopes are isolated, a local variable can share a name with a global one without any conflict:
<?php
$total = 999;
function sumItems()
{
$total = 10 + 20;
return $total;
}
echo sumItems() . "\n";
echo $total . "\n";
Output
30
999
The function's $total and the global $total are two different variables. The function cannot accidentally overwrite the global value.
Passing Data Into and Out of Functions
The correct way to give a function data is through parameters, and the correct way to get data back is with return.
Example
<?php
$siteName = "GoLearn";
$year = 2026;
function buildFooter(string $name, int $year): string
{
return "© $year $name. All rights reserved.";
}
echo buildFooter($siteName, $year);
Output
© 2026 GoLearn. All rights reserved.
Why This Is the Preferred Approach
- Explicit dependencies: The function signature shows exactly what it needs. You do not have to read the function body to discover that it secretly uses a global variable.
- Reusability: The same function can build footers for different sites or years.
- Testability: You can call
buildFooter("Test", 2000)and check the result without setting up any global state. - Safety: The function cannot modify the caller's variables. It only returns a new value.
Parameters are local variables of the function. $name and $year inside buildFooter() are copies of the values passed in. (The parameter $year and the global $year are separate variables that happen to share a name.)
Accessing Global Variables Inside a Function
Sometimes you need to read or modify a global variable from inside a function. PHP provides two mechanisms. Both work, but both should be used sparingly.
The global Keyword
<?php
$visitCount = 0;
function registerVisit()
{
global $visitCount;
$visitCount++;
}
registerVisit();
registerVisit();
registerVisit();
echo "Visits: $visitCount";
Output
Visits: 3
How It Works
global $visitCount; creates a local reference to the global variable of the same name. From that line onward, $visitCount inside the function refers to the global variable, so changes affect it directly.
Several variables can be imported at once:
global $dbHost, $dbUser, $dbName;
The $GLOBALS Array
$GLOBALS is a superglobal array that contains every variable in the global scope, with the variable name as the key.
<?php
$taxRate = 0.18;
$price = 1000;
function priceWithTax()
{
return $GLOBALS['price'] * (1 + $GLOBALS['taxRate']);
}
echo priceWithTax();
Output
1180
$GLOBALS['price'] reads the global $price without a global statement. You can also write to an element: $GLOBALS['price'] = 1200; changes the global variable.
Version note (PHP 8.1+):
$GLOBALSis now a read-only copy as a whole. Writing to individual elements still works, but replacing the entire array ($GLOBALS = [];) or passing it by reference is no longer allowed and causes a compile-time error.
global vs $GLOBALS
| Aspect | global $x; | $GLOBALS['x'] |
|---|---|---|
| Syntax | Declared once, then used as $x | Array access on every use |
| Makes clear it is global at the point of use | No — looks like a normal local variable later in the function | Yes — $GLOBALS is visible on every line |
| Can access a variable whose name is dynamic | No | Yes — $GLOBALS[$name] |
| Typical use | Older code, simple scripts | Occasional access, debugging |
Why Global Variables Should Be Avoided
Both mechanisms create hidden dependencies:
<?php
function applyDiscount()
{
global $cartTotal, $discountRate;
$cartTotal = $cartTotal * (1 - $discountRate);
}
Problems with this function:
- Its signature says nothing about needing
$cartTotalor$discountRate. - If any other code changes those globals,
applyDiscount()behaves differently. - It modifies a global variable, so calling it twice silently applies the discount twice.
- It cannot be reused for a second cart or tested in isolation.
The same function with parameters and a return value:
<?php
function applyDiscount(float $total, float $rate): float
{
return $total * (1 - $rate);
}
$cartTotal = applyDiscount($cartTotal, 0.10);
Now every input is visible, nothing is modified behind the caller's back, and the result is explicitly assigned. As applications grow, shared state is usually managed with objects and dependency injection, covered in the PHP OOP section.
PHP Has No Block Scope
In many languages, variables created inside if blocks or loops disappear after the block ends. PHP does not work that way. Braces { } in if, for, foreach, while, and switch do not create a new scope. Only functions (and classes) do.
Example
<?php
$score = 85;
if ($score >= 50) {
$result = "Pass";
}
echo $result . "\n";
for ($i = 1; $i <= 3; $i++) {
$lastValue = $i * 10;
}
echo "i = $i, lastValue = $lastValue\n";
Output
Pass
i = 4, lastValue = 30
Why This Matters
$result, created inside theifblock, is still available afterward.- The loop counter
$iand$lastValueremain after the loop.$iis4because the loop stops when$i <= 3becomes false.
This is convenient, but it can cause bugs. If the condition is false, the variable is never created:
<?php
$score = 30;
if ($score >= 50) {
$result = "Pass";
}
echo $result;
Output
PHP Warning: Undefined variable $result in /path/script.php on line 8
Fix: Initialize the variable before the block, so it exists on every path:
<?php
$score = 30;
$result = "Fail";
if ($score >= 50) {
$result = "Pass";
}
echo $result;
A related pitfall involves foreach with references, where the reference variable survives the loop and causes unexpected overwrites. It is explained in the Foreach Loop lesson.
Scope of Included Files
When you include or require a file, its code runs in the scope where the include statement appears. Included files do not have their own scope.
config.php
<?php
$appName = "Inventory Manager";
$itemsPerPage = 25;
index.php
<?php
require __DIR__ . '/config.php';
echo "$appName shows $itemsPerPage items per page.\n";
function loadConfigInsideFunction()
{
require __DIR__ . '/config.php';
echo "Inside function: $appName\n";
}
loadConfigInsideFunction();
Output
Inventory Manager shows 25 items per page.
Inside function: Inventory Manager
How It Works
- The first
requireruns in the global scope, so$appNameand$itemsPerPagebecome global variables. - The second
requireruns inside the function, so the same variables become local to that function call.
This explains why template files can use variables defined by the page that includes them: they run in the page's scope. A cleaner pattern for configuration is a file that returns an array:
<?php
// config.php
return [
'appName' => 'Inventory Manager',
'itemsPerPage' => 25,
];
<?php
$config = require __DIR__ . '/config.php';
echo $config['appName'];
Here the configuration arrives as a single, explicitly named value instead of a set of variables appearing silently in the current scope.
Superglobals: Available Everywhere
PHP provides a set of built-in arrays called superglobals that are accessible in every scope — inside functions, methods, and included files — without global.
<?php
function currentPage(): string
{
return $_SERVER['REQUEST_URI'] ?? '/';
}
function searchTerm(): string
{
return $_GET['q'] ?? '';
}
| Superglobal | Contains |
|---|---|
$GLOBALS | All global variables |
$_SERVER | Server and request information |
$_GET | URL query parameters |
$_POST | Submitted form data |
$_FILES | Uploaded files |
$_COOKIE | Cookies sent by the browser |
$_SESSION | Session data |
$_REQUEST | Combined GET, POST, and (by default) COOKIE data |
$_ENV | Environment variables |
Each superglobal has its own lesson in the PHP Superglobals section. Only these built-in variables are superglobal; you cannot create your own.
Scope in Anonymous and Arrow Functions
Anonymous functions (closures) and arrow functions are functions too, but they have special ways to use variables from the surrounding code.
Anonymous Function With use
<?php
$taxRate = 0.18;
$addTax = function (float $amount) use ($taxRate): float {
return $amount * (1 + $taxRate);
};
echo $addTax(500);
Output
590
use ($taxRate) copies the value of $taxRate from the surrounding scope into the closure at the moment the closure is created. Without use, $taxRate would be undefined inside the function, just like in a regular function.
Arrow Function (PHP 7.4+)
<?php
$taxRate = 0.18;
$addTax = fn(float $amount): float => $amount * (1 + $taxRate);
echo $addTax(500);
Output
590
Arrow functions automatically capture variables from the surrounding scope by value. No use clause is needed.
These capture mechanisms are explicit and limited to the specific function, which is why they are safer than global. They are covered fully in Anonymous Functions, Closures, and Arrow Functions.
Practical Example: Order Processing Without Globals
<?php
const TAX_RATE = 0.18;
function calculateSubtotal(array $items): float
{
$subtotal = 0;
foreach ($items as $item) {
$subtotal += $item['price'] * $item['qty'];
}
return $subtotal;
}
function calculateTax(float $amount): float
{
return round($amount * TAX_RATE, 2);
}
$cart = [
['name' => 'Notebook', 'price' => 60, 'qty' => 5],
['name' => 'Pen', 'price' => 15, 'qty' => 10],
];
$subtotal = calculateSubtotal($cart);
$tax = calculateTax($subtotal);
$total = $subtotal + $tax;
echo "Subtotal: $subtotal\n";
echo "Tax: $tax\n";
echo "Total: $total\n";
Output
Subtotal: 450
Tax: 81
Total: 531
Why This Design Works
TAX_RATEis a constant. Unlike variables, constants defined withconstordefine()are available in every scope, which makes them suitable for fixed configuration values. See PHP Constants.- Each function receives everything it needs as parameters and returns its result.
- The local
$subtotalinsidecalculateSubtotal()and the global$subtotalare separate variables; the global one receives the value throughreturn. - No function reads or changes global variables, so each one can be tested and reused independently.
Common Mistakes
Expecting Functions to See Outer Variables
What happens: Warning: Undefined variable inside a function.
Why: Regular PHP functions only see their own local scope.
Fix: Pass the value as a parameter. Use use for closures; arrow functions capture automatically.
Expecting Local Variables to Exist After the Function
What happens: Warning: Undefined variable after calling the function.
Fix: return the value and assign it: $discount = calculateDiscount();.
Overusing global
What happens: Functions depend on and modify hidden state, causing bugs that appear far from their cause.
Fix: Use parameters and return values. Group related shared data into objects.
Using Variables Created Only Inside a Conditional Block
What happens: The variable is undefined when the condition is false.
Fix: Initialize the variable with a default value before the block.
Assuming Loop Variables Disappear
What happens: A later loop or calculation accidentally reuses $i, $item, or $value from an earlier loop.
Fix: Use distinct, descriptive names, and keep longer logic inside functions, which do provide scope isolation.
Best Practices
- Pass data into functions with parameters, and get data out with
return. - Avoid
globaland$GLOBALSin application code; reserve them for small scripts or legacy code. - Use constants for fixed configuration values that many functions need.
- Initialize variables before conditional blocks.
- Keep functions small, so their local scope stays small and easy to understand.
- Load configuration from a file that returns an array instead of creating loose global variables.
Official reference: https://www.php.net/manual/en/language.variables.scope.php