π (Variable Scope, References, Closures & use)
These concepts separate "framework users" from real PHP engineers. Mastering them is key to writing efficient, bug-free, and complex code.
1. Variable Scope in PHP (Where variables live and die)
Scope defines the context in which a variable is defined and accessible.
β Local Scope: The Function's Private Space
A variable declared inside a function (or method) is locally scoped. It is created when the function starts and completely destroyed when the function finishes execution.
function calculateArea($width, $height) {
// $area, $width, and $height are all local variables.
$area = $width * $height;
echo "Area is: " . $area;
}
// echo $area; // Fatal Error: Undefined variable $area
Analogy: Like a private notebook inside a secured meeting room β the contents are relevant only for that meeting, and no one outside can read or modify it.
β Global Scope: The Public Declaration
A variable declared outside any function or class belongs to the global scope. Functions cannot access global variables directly by default.
$site_name = "MyApp"; // Global variable
function showSite() {
// Explicitly importing the global variable into the function's local scope
global $site_name;
echo "Welcome to " . $site_name;
}
showSite(); // Output: Welcome to MyApp
β οΈ Important: A better practice than using global is to pass the variable as a function argument.
Analogy: A notice board in the office foyer β everyone can see it, but you need to tell PHP explicitly to look outside the function's room to read it.
β Static Scope: Function Memory with Persistence
Variables declared as static inside a function are local to that function but not destroyed when the function finishes. They retain their value between subsequent function calls.
function counter() {
static $count = 0; // Initialized once
$count++;
return $count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
π‘ Practical Example: Simple ID Generator
This ensures a unique, sequential ID is generated every time the function is called, without needing a global counter.
function generate_id() {
static $current_id = 0; // Initialized ONLY once
$current_id++;
return $current_id;
}
echo "User ID: " . generate_id() . "\n"; // Output: User ID: 1
echo "Order ID: " . generate_id() . "\n"; // Output: Order ID: 2
Practical Example: The Singleton Pattern (Database Connection)
The Singleton pattern ensures only one instance of a class (like a database connection) is created, often relying on a static property.
class Database
{
private static $instance = null;
// Use Static Scope to store and reuse the connection instance
public static function getInstance(): Database
{
if (self::$instance === null) {
// Only create the connection ONCE
self::$instance = new self();
}
return self::$instance;
}
// Private constructor prevents direct object creation
private function __construct() {}
}
$db1 = Database::getInstance();
$db2 = Database::getInstance();
// $db1 and $db2 are the same object, thanks to static scope.
Why it matters: Essential for tracking state (like a call count or ID generator) without relying on global variables or class properties.
Analogy: Sticky notes that stay on your desk after you leave and come back.
2. References (&) β Working on the Original Data
In PHP, most variables are passed by value (a copy is made). References allow you to create a second alias or name for the same variable content, enabling pass-by-reference.
β Normal Copy (Pass-by-Value)
$a = 10;
$b = $a; // $b gets a copy of 10
$b = 20; // Only $b is changed
echo $a; // Output: 10
β Reference Version (Shared Memory)
The & operator means "share the memory address." Both $a and $b point to the same data.
$a = 10;
$b =& $a; // $b is now an alias for $a
$b = 20; // Changing $b also changes $a
echo $a; // Output: 20
Analogy: Two remote controls for the same TV. Pressing a button on either control affects the single TV set.
Reference in Functions (Explicit Pass-by-Reference)
By putting & in the function signature, any changes made to the argument inside the function will affect the original variable passed in.
function addOne(&$num) { // & here is key
$num++;
}
$x = 5;
addOne($x);
echo $x; // Output: 6
Reference in foreach (The Danger Zone)
Using foreach ($arr as &$v) makes $v an alias for the array element. You must break this reference after the loop.
$arr = [1, 2, 3];
foreach ($arr as &$v) {
$v *= 2;
}
unset($v); // ALWAYS do this to break the reference tie
3. Closures β Functions That Remember
A Closure is an anonymous function (a function without a name) that can be stored in a variable, passed as an argument, and, most importantly, access variables from the scope in which it was created.
// Storing an anonymous function in a variable
$greet = function ($name) {
return "Hello " . $name;
};
echo $greet("Mamu");
Analogy: A portable microchip containing a specific, pre-programmed action that you can carry around and activate anywhere.
4. use β Capturing Variables Inside Closures
An anonymous function cannot access variables from the outer scope by default. The use keyword allows you to explicitly import these external variables into the closure's definition.
β Capturing by Value
The closure captures the value of $name at the moment the closure is defined.
$name = "Mamu";
$fn = function () use ($name) { // Capturing $name
return $name;
};
$name = "Ali"; // This change happens after the capture
echo $fn(); // Output: Mamu (It captured the original "Mamu")
Capture by Reference with use
Adding & to the captured variable makes the closure hold a reference to the external variable. Any change affects the original variable.
$count = 0;
$fn = function () use (&$count) { // Capturing $count by reference
$count++;
};
$fn();
$fn();
echo $count; // Output: 2
Analogy: The closure is holding a live wire connected to the original variable, not just a photograph of it.
5. Lexical Scoping (The Birthplace Rule)
Lexical Scoping means the structure of the code (where it is written) dictates which variables a closure can access.
$message = "Outside"; // Scope A
$fn = function () use ($message) {
// $message is captured from Scope A: "Outside"
echo $message;
};
function run($cb) {
$message = "Inside"; // Scope B
$cb();
}
run($fn); // Prints "Outside"
Rule to Remember:
Closures use variables from where they were created (lexical scope), not where they are executed (dynamic scope).
6. π» Real-World Example: Creating a Dynamic Array Filter
Closures and use are crucial for functions like array_filter(), which require a dynamic callback.
Filtering Products by a Price Threshold
We define the products and the dynamic price threshold in the outer scope.
$products = [
['name' => 'Laptop', 'price' => 1200],
['name' => 'Mouse', 'price' => 25],
['name' => 'Monitor', 'price' => 350],
['name' => 'Keyboard', 'price' => 75],
];
// The variable we want to capture and use as a dynamic filter condition
$maxPrice = 100;
The Closure Implementation
The closure uses use ($maxPrice) to bring the external price threshold into the filtering logic.
$affordableProducts = array_filter($products, function ($product) use ($maxPrice) {
// The closure compares the product's price against the captured $maxPrice
return $product['price'] <= $maxPrice;
});
Output
The filter uses the captured value of $maxPrice (100) successfully:
print_r($affordableProducts);
/*
Output:
Array
(
[1] => Array ( [name] => Mouse [price] => 25 )
[3] => Array ( [name] => Keyboard [price] => 75 )
)
*/
7. π‘ Summary of PHP Concepts
| Concept | Explanation | Key Syntax |
|---|---|---|
| Local Scope | Variables created inside a function; destroyed upon exit. | $variable |
| Global Scope | Variables created outside any function; must be explicitly imported. | global $variable; |
| Static Scope | Local variables that retain their value between function calls. | static $count = 0; |
| Reference | Creates an alias for a variable, pointing to the same memory location. | &$variable |
| Closure | An anonymous function that can be stored as a value. | function () {} |
use Keyword |
Imports external variables into a Closure's scope (lexical capture). | function () use ($var) |
| Lexical Scoping | Closures use variables from their creation scope. | N/A |
Top comments (1)
Using static variables in a function that output the variable are problematic. A function call deeply hidden in the code could make expected output somewhere else unreliable.
A better option is to add the initial/previous value, and the logic creates a new value based on that input.
The reference variable in a loop is not more dangerous that the by value variable in a loop. Both live as long as the function it is in runs.
That is why it is a best practice to make the loop variables as specific to the context of the loop as possible. This makes it less likely a variable further in the function receives an unexpected value.
For the
array_filteruse an arrow function, that saves you from needinguse.