Scope — where a variable is visible (local, global, closure, class)
Beginner5 min read·eng-12-025
Concept
Scope — the region of code where a variable is visible and accessible. PHP has several distinct scoping rules that differ from most other languages.
PHP's scoping rules (different from JavaScript!):
- Local scope: Variables defined inside a function are only accessible inside that function. They don't "see" variables defined outside.
- Global scope: Variables defined outside functions. NOT accessible inside functions unless explicitly declared.
globalkeyword: Brings a global variable into the local function scope:global $config;.$GLOBALSsuperglobal: Array containing all global variables:$GLOBALS['config'].- Static scope:
static $counter = 0;inside a function — persists between calls, but still local to that function. - Closure scope: Closures do NOT automatically inherit outer scope. Use
use ($var)to capture specific variables.
PHP vs JavaScript scoping: In JavaScript, an inner function can access variables from outer functions (closure). In PHP, a regular function CANNOT see its outer scope — you need explicit use ($var) in a closure.
Class scope:
private: Only within the class.protected: Within the class and subclasses.public: From anywhere.- Static properties: Shared across all instances, accessed via
ClassName::$proporstatic::$prop.
Block scope (PHP 8): PHP does NOT have block scope for variables. A variable defined inside a for loop is accessible after the loop. This differs from JavaScript/Java.
Variable variables: $$name — variable whose name is stored in $name. Rare, but worth knowing.
Code Example
php
<?php
$message = 'Hello from global scope';
$count = 0;
// ❌ PHP function CANNOT see global variables
function greet(): void
{
echo $message; // Notice: Undefined variable $message
}
// ✅ Use global keyword to import
function greetWithGlobal(): void
{
global $message; // explicitly import
echo $message; // 'Hello from global scope'
$message = 'Modified!'; // modifies the actual global!
}
// ✅ Better: pass as parameter (pure function style)
function greetPure(string $message): void
{
echo $message;
}
// Closure scope — must explicit capture with 'use'
$prefix = 'Hello';
$greet = function(string $name) use ($prefix): string
{
return "{$prefix}, {$name}!";
};
echo $greet('Alice'); // 'Hello, Alice!'
// Without 'use ($prefix)': Notice: Undefined variable $prefix
// Arrow function — automatic capture (but by value at definition time)
$greeting = fn(string $name): string => "{$prefix}, {$name}!";
echo $greeting('Bob'); // 'Hello, Bob!'
// Static scope — persists between calls
function counter(): int
{
static $count = 0; // initialized ONCE, survives between calls
return ++$count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
// PHP has NO block scope — loop variable leaks
for ($i = 0; $i < 3; $i++) {
$loopVar = $i * 2;
}
echo $i; // 3 — still accessible!
echo $loopVar; // 4 — still accessible!
// Class scope levels
class BankAccount
{
public float $interestRate = 0.05; // accessible everywhere
protected float $balance = 0.0; // accessible in class + subclasses
private string $accountNumber; // only within BankAccount
public function getBalance(): float { return $this->balance; } // can access private
}
class SavingsAccount extends BankAccount
{
public function earn(): void { $this->balance += $this->balance * $this->interestRate; } // protected OK
// $this->accountNumber — Fatal error: private property of parent
}
// Static class property — shared across all instances
class RequestCounter
{
private static int $requests = 0;
public static function increment(): void { self::$requests++; }
public static function getCount(): int { return self::$requests; }
}
RequestCounter::increment();
RequestCounter::increment();
echo RequestCounter::getCount(); // 2 — shared state