PHP Engineering: Deep Dive into Core Concepts

Published: (December 8, 2025 at 08:11 AM EST)
5 min read
Source: Dev.to

Source: Dev.to

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 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.

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 import the global variable into the function's local scope
    global $site_name; 
    echo "Welcome to " . $site_name;
}
showSite(); // Output: Welcome to MyApp

Important: Passing the variable as a function argument is usually preferable to using global.

Analogy: A notice board in the office foyer — everyone can see it, but you must tell PHP explicitly to look outside the function’s room.

Static Scope: Function Memory with Persistence

Variables declared as static inside a function are local to that function but persist between 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

A static variable can generate unique, sequential IDs without a global counter.

function generate_id() {
    static $current_id = 0; // Initialized only once
    $current_id++;
    return $current_id;
}

echo "User ID: " . generate_id() . "\n";   // User ID: 1
echo "Order ID: " . generate_id() . "\n";  // Order ID: 2

Practical Example: The Singleton Pattern (Database Connection)

The Singleton pattern relies on a static property to ensure only one instance of a class exists.

class Database
{
    private static $instance = null;

    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

Why it matters: Static scope is 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.

References (&) — Working on the Original Data

In PHP, most variables are passed by value (a copy is made). References allow you to create an alias 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)

$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)

function addOne(&$num) {
    $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 each array element. Always break the reference after the loop.

$arr = [1, 2, 3];

foreach ($arr as &$v) {
    $v *= 2;
}
unset($v); // Break the reference

Closures — Functions That Remember

A closure is an anonymous function that can be stored in a variable, passed as an argument, and access variables from the scope in which it was created.

$greet = function ($name) {
    return "Hello " . $name;
};

echo $greet("Mamu"); // Hello Mamu

Analogy: A portable microchip containing a pre‑programmed action that you can carry around and activate anywhere.

use — Capturing Variables Inside Closures

An anonymous function cannot access outer‑scope variables by default. The use keyword imports those variables into the closure.

Capturing by Value

$name = "Mamu";

$fn = function () use ($name) {
    return $name;
};

$name = "Ali"; // Change after capture

echo $fn(); // Output: Mamu

Capturing by Reference

$count = 0;

$fn = function () use (&$count) {
    $count++;
};

$fn();
$fn();

echo $count; // Output: 2

Analogy: The closure holds a live wire connected to the original variable, not just a snapshot.

Lexical Scoping (The Birthplace Rule)

Lexical scoping means a closure accesses variables from where it was written, not where it is executed.

$message = "Outside"; // Scope A

$fn = function () use ($message) {
    echo $message; // Captured from Scope A
};

function run($cb) {
    $message = "Inside"; // Scope B
    $cb();
}

run($fn); // Prints "Outside"

Rule to Remember: Closures use variables from their creation scope (lexical), not from the call site (dynamic).

Real‑World Example: Creating a Dynamic Array Filter

function makeFilter($min, $max) {
    return function ($value) use ($min, $max) {
        return $value >= $min && $value  12 [2] => 17 )

In this example, the closure captures $min and $max by value, allowing array_filter to apply a custom range check without exposing those variables globally.

Back to Blog

Related posts

Read more »