Variables #

Variables are the most fundamental concept in PHP, but also where many of the language’s quirks hide. PHP is a dynamically typed language — a variable’s type is determined by the value you store in it, not by an explicit declaration. This makes code quick to write, but it also opens the door to subtle bugs: a variable suddenly changing type mid-program, a scope that’s different from what you assumed, or a value changing unintentionally because of an unnoticed reference. This article covers all aspects of PHP variables thoroughly — from the basic naming rules, how scope works, the critical difference between copy-by-value and pass-by-reference, to the destructuring feature that makes code far more expressive.

PHP Variable Basics #

Every PHP variable starts with a dollar sign $ followed by the variable name. PHP has no declaration keywords like var, let, or const for regular variables — you simply write the name and assign its value.

<?php
$name     = "Budi Santoso";   // string
$age      = 28;               // integer
$height   = 172.5;            // float
$active   = true;             // boolean
$address  = null;             // null — no value yet

PHP creates a variable the moment it’s first assigned. No separate declaration is needed, and a variable’s type changes automatically to follow the value given:

<?php
$data = 42;          // integer
$data = "text";      // now a string — valid in PHP
$data = [1, 2, 3];   // now an array — still valid
$data = null;        // now null

This flexibility is handy, but it can also be a source of bugs. In real projects, one variable should only hold one kind of data for its entire lifetime.

Variable Naming Rules #

PHP variable names follow these rules:

<?php
// ✓ Valid names
$name         = "Budi";
$fullName     = "Budi Santoso";   // camelCase — common PHP convention
$full_name    = "Budi Santoso";   // snake_case — also common
$_internal    = "value";          // may start with an underscore
$score2024    = 100;              // may contain digits (but not at the start)
$HTMLParser   = "parser";         // capitals allowed anywhere

// ✗ Invalid names — will cause a parse error
// $2024score  = 100;  // must not start with a digit
// $name-me    = "x";  // hyphens are not valid characters
// $final score = 0;   // spaces are not allowed
// $class@id   = 1;    // special characters are not allowed

One important rule that often surprises beginners: PHP variable names are case-sensitive. $name, $Name, and $NAME are three different, independent variables.

<?php
$city = "Jakarta";
$City = "Bandung";
$CITY = "Surabaya";

echo $city;  // Jakarta
echo $City;  // Bandung
echo $CITY;  // Surabaya — three different variables!

Naming Conventions #

PHP doesn’t enforce a single convention, but the community and the PSR-12 standard provide clear guidance:

ContextConventionExamples
Local variablescamelCase$fullName, $totalPrice
Function parameterscamelCase$userId, $isActive
Class propertiescamelCase$this->firstName
Global variables (avoid)snake_case or UPPER_SNAKE$_config, $MAX_RETRY
Short loop variablesSingle letter$i, $j, $k (loops only)

Variable Scope #

Scope determines where a variable can be accessed. PHP has three scope levels, and how they work differs from many other languages — especially how functions isolate their own scope.

flowchart TD
    A[Global Scope\nVariables outside functions/classes] --> B{Access from inside a function?}
    B -- "Directly\n❌ Not possible" --> C[Undefined variable]
    B -- "Use the global keyword\n⚠️ Avoid if possible" --> D[Accessible]
    B -- "Use parameters\n✓ The right way" --> E[Accessible via arguments]
    F[Function Scope\nFunction-local variables] --> G{Access from outside the function?}
    G -- "Not possible" --> H[Undefined variable]
    G -- "Via return value\n✓ The right way" --> I[Accessible]

Global Scope and Function Scope #

Unlike JavaScript or Python, PHP global variables are not automatically available inside functions:

<?php
$appName = "PHP Tutorial";
$version = "8.3";

function displayInfo(): void
{
    // ANTI-PATTERN: assuming global variables are available inside functions
    echo $appName; // PHP Warning: Undefined variable $appName
    echo $version; // PHP Warning: Undefined variable $version
}

// CORRECT: pass them as parameters
function displayInfoV2(string $appName, string $version): void
{
    echo "$appName version $version";
}

displayInfoV2($appName, $version); // PHP Tutorial version 8.3

This isolation is a feature, not a bug — it prevents functions from accidentally depending on or modifying global state, which makes code easier to test and debug.

The global Keyword #

PHP provides the global keyword to access global variables from inside functions, but it’s almost always a code smell:

<?php
$dbConnection = null; // global variable

// ANTI-PATTERN: accessing globals from inside a function
function fetchUser(int $id): array
{
    global $dbConnection; // takes a reference to the global variable

    // this function now has a hidden dependency on the global $dbConnection
    // impossible to test without setting up the global variable first
    $stmt = $dbConnection->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$id]);
    return $stmt->fetch();
}

// CORRECT: dependency injection — pass the connection as a parameter
function fetchUserV2(PDO $db, int $id): array
{
    $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->execute([$id]);
    return $stmt->fetch() ?: [];
}

Static Variables #

Static variables inside functions keep their value between calls. Unlike regular local variables, which are reset every time the function is called:

<?php
function countCalls(): int
{
    static $count = 0; // initialized ONCE, then the value is preserved
    $count++;
    return $count;
}

echo countCalls(); // 1
echo countCalls(); // 2
echo countCalls(); // 3

// Useful for simple memoization
function fibonacci(int $n): int
{
    static $cache = [];

    if ($n <= 1) return $n;
    if (isset($cache[$n])) return $cache[$n];

    $cache[$n] = fibonacci($n - 1) + fibonacci($n - 2);
    return $cache[$n];
}

echo fibonacci(10); // 55
echo fibonacci(10); // 55 — from cache, not recomputed

Pass-by-Value vs Pass-by-Reference #

This is one of the most important — and most misunderstood — concepts about PHP variables.

Pass-by-Value (Default) #

By default, PHP passes a copy of the value to functions. Modifying a parameter inside the function doesn’t affect the original variable:

<?php
function addOne(int $number): int
{
    $number++; // modifies the copy, not the original value
    return $number;
}

$value = 10;
$result = addOne($value);

echo $value;  // 10 — unchanged
echo $result;  // 11

// The same applies to ordinary assignment
$a = [1, 2, 3];
$b = $a;       // $b is a COPY of $a

$b[] = 4;      // adds an element to the copy

var_dump(count($a)); // int(3) — $a is unaffected
var_dump(count($b)); // int(4)

Pass-by-Reference #

By adding &, you pass a reference to the original variable — not a copy. Changes inside the function directly affect the original variable:

<?php
function doubleValue(int &$number): void
{
    $number *= 2; // modifies the original variable directly
}

$value = 15;
doubleValue($value); // no return needed — modified directly
echo $value; // 30 — the original value changed!

// Practical example: PHP's built-in sort functions modify arrays by reference
$fruits = ["mango", "apple", "orange"];
sort($fruits); // sort() takes the array by reference, modifies the original
print_r($fruits); // ["apple", "orange", "mango"]

When to Use a Reference vs a Return Value #

Use return values for almost every case. References are only appropriate in specific situations:

<?php
// ANTI-PATTERN: using a reference when a return value is clearer
function applyDiscount(float &$price, float $percent): void
{
    $price = $price * (1 - $percent);
}

$price = 100000.0;
applyDiscount($price, 0.1);
// readers have to know that $price changed — not intuitive from the caller's side

// CORRECT: an explicit return value is easier to understand
function applyDiscountV2(float $price, float $percent): float
{
    return $price * (1 - $percent);
}

$price        = 100000.0;
$discounted   = applyDiscountV2($price, 0.1);
// clear: the original $price is unchanged, the result is in $discounted

// ✓ References are appropriate for: swapping two variables
function swap(mixed &$a, mixed &$b): void
{
    $temp = $a;
    $a    = $b;
    $b    = $temp;
}

$x = "first";
$y = "second";
swap($x, $y);
echo $x; // second
echo $y; // first

References to Array Elements #

References can also be used when iterating over arrays with foreach when you need to modify elements:

<?php
$prices = [10000, 25000, 50000, 100000];

// ANTI-PATTERN: modifying the array via index — verbose
foreach ($prices as $i => $value) {
    $prices[$i] = $value * 1.11; // add VAT
}

// CORRECT: foreach with a reference — cleaner
foreach ($prices as &$value) {
    $value *= 1.11;
}
unset($value); // REQUIRED! break the reference after the loop finishes

// Why is unset() required? Because $value still references the last element.
// If later code assigns to $value, the last array element changes too!
Always call unset($refVariable) after a foreach that uses a reference (&). Without it, $refVariable still points at the array’s last element, and any later assignment to a variable with the same name will modify that element — this is a very subtle, hard-to-find source of bugs.

Checking and Removing Variables #

PHP provides several functions for checking whether variables exist and what type they are.

isset(), empty(), and is_null() #

These three functions look similar but behave differently in ways that matter:

<?php
$a = 0;
$b = "";
$c = null;
$d = false;
$e = [];
// $f isn't defined at all

// isset() — true if the variable EXISTS and is NOT null
var_dump(isset($a)); // true  — exists, value is 0
var_dump(isset($b)); // true  — exists, value is ""
var_dump(isset($c)); // false — exists but value is null
var_dump(isset($f)); // false — doesn't exist

// empty() — true if the variable is "empty" (falsy or nonexistent)
var_dump(empty($a)); // true  — 0 counts as empty
var_dump(empty($b)); // true  — "" counts as empty
var_dump(empty($c)); // true  — null counts as empty
var_dump(empty($d)); // true  — false counts as empty
var_dump(empty($e)); // true  — [] counts as empty
var_dump(empty($f)); // true  — nonexistent counts as empty

// is_null() — true only if the value is null (variable must exist)
var_dump(is_null($a)); // false
var_dump(is_null($c)); // true
// is_null($f) — will produce a warning if $f is not defined

A quick table to help you choose:

FunctionUndefinednull0""false[]
isset()falsefalsetruetruetruetrue
empty()truetruetruetruetruetrue
is_null()warningtruefalsefalsefalsefalse

Removing Variables with unset() #

unset() removes a variable from memory. An unset variable no longer exists in that scope:

<?php
$data = ["budi", "siti", "dani"];

unset($data[1]); // remove the element at index 1
print_r($data);
// Array ( [0] => budi [2] => dani )
// Note: indices are NOT automatically reindexed!

// To reindex after unset, use array_values()
$data = array_values($data);
print_r($data);
// Array ( [0] => budi [1] => dani )

// Remove a scalar variable
$temp = "temporary data";
unset($temp);
var_dump(isset($temp)); // bool(false)

Variable Types and Conversion #

PHP automatically converts a variable’s type according to its context of use. Understanding these conversion rules is important for avoiding bugs.

Checking a Variable’s Type #

<?php
$value = 42;

// gettype() — returns the type name as a string
echo gettype($value);        // "integer"

// is_*() — returns a boolean
var_dump(is_int($value));    // true
var_dump(is_float($value));  // false
var_dump(is_string($value)); // false
var_dump(is_bool($value));   // false
var_dump(is_array($value));  // false
var_dump(is_null($value));   // false
var_dump(is_numeric($value));// true — includes numeric strings like "42"

// var_dump() — shows type and value at once (most useful for debugging)
var_dump($value); // int(42)

Explicit Conversion (Casting) #

Rather than relying on PHP’s implicit conversion, it’s better to cast explicitly when you need a specific type:

<?php
$input = "42.7 percent"; // string from user input

// Explicit casting
$asInt   = (int)    $input;  // 42    — takes the integer part at the start of the string
$asFloat = (float)  $input;  // 42.7  — takes the float part at the start of the string
$asBool  = (bool)   $input;  // true  — any non-empty string is true
$asStr   = (string) 3.14;    // "3.14"

// intval(), floatval(), strval() — function alternatives
$asInt2 = intval($input);       // 42
$asInt3 = intval("0xFF", 16);   // 255 — intval() accepts a base

// settype() — in-place conversion (modifies the original variable)
$number = "100";
settype($number, "integer");
var_dump($number); // int(100)

Values Considered false (Falsy) #

PHP converts values to boolean when used in conditional contexts. The following values are considered false:

<?php
// All of these values count as false in a boolean context
$falsy = [
    false,    // boolean false
    0,        // integer zero
    0.0,      // float zero
    "",       // empty string
    "0",      // string "0" — this surprises many people!
    [],       // empty array
    null,     // null
];

foreach ($falsy as $value) {
    if (!$value) {
        echo gettype($value) . " '" . var_export($value, true) . "' is falsy\n";
    }
}

// All other values count as true, including:
var_dump((bool) "false");  // true! — non-empty string, even though it says "false"
var_dump((bool) "0.0");    // true! — string "0.0" isn't "0", so true
var_dump((bool) -1);       // true! — every non-zero integer is true
var_dump((bool) [0]);      // true! — an array with elements is true

Variable Variables #

PHP supports variable variables — a variable whose value is used as the name of another variable. This feature is rarely needed but important to recognize so you’re not confused when you come across it:

<?php
$key    = "name";
$$key   = "Budi"; // creates the variable $name with the value "Budi"

echo $name;    // Budi
echo $$key;    // Budi — same

// Second level
$a  = "b";
$b  = "c";
$c  = "final value";

echo $$$a; // "final value" — PHP evaluates $a → "b", then $b → "c", then $c
<?php
// An example use you might encounter (but should avoid):
$fields = ["name", "email", "phone"];
$name    = "Budi";
$email   = "[email protected]";
$phone   = "081234567890";

foreach ($fields as $field) {
    // ANTI-PATTERN: variable variables for "dynamic" access
    echo $$field . "\n"; // Budi, [email protected], 081234567890
}

// CORRECT: use an associative array instead
$data = [
    "name"    => "Budi",
    "email"   => "[email protected]",
    "phone"   => "081234567890",
];

foreach ($fields as $field) {
    echo $data[$field] . "\n"; // clearer, safer, easier to debug
}

Destructuring Arrays into Variables #

PHP provides an elegant way to extract values from an array into separate variables using list() or the [] syntax (PHP 7.1+).

Destructuring Indexed Arrays #

<?php
$coordinates = [10.5, 106.8, 50]; // lat, lng, altitude

// The old way — verbose
$lat      = $coordinates[0];
$lng      = $coordinates[1];
$altitude = $coordinates[2];

// The modern way with destructuring
[$lat, $lng, $altitude] = $coordinates;
echo "$lat, $lng, $altitude"; // 10.5, 106.8, 50

// Skip elements you don't need with a placeholder
[, $lng] = $coordinates; // only take the longitude
echo $lng; // 106.8

// Useful for swapping variables without a temp variable
$a = "first";
$b = "second";
[$a, $b] = [$b, $a];
echo $a; // second
echo $b; // first

Destructuring Associative Arrays #

<?php
$user = [
    "id"    => 42,
    "name"  => "Budi Santoso",
    "email" => "[email protected]",
    "role"  => "admin",
];

// Extract specific keys into variables
["name" => $name, "email" => $email] = $user;
echo $name;  // Budi Santoso
echo $email; // [email protected]

// Very useful for database query results
$rows = $pdo->query("SELECT id, name, email FROM users")->fetchAll();

foreach ($rows as ["id" => $id, "name" => $name, "email" => $email]) {
    echo "[$id] $name$email\n";
}

Destructuring in Functions #

<?php
// A function that returns multiple values via an array
function calculateStats(array $data): array
{
    return [
        "min"    => min($data),
        "max"    => max($data),
        "avg"    => array_sum($data) / count($data),
        "total"  => array_sum($data),
    ];
}

// Destructuring a function's result
["min" => $min, "max" => $max, "avg" => $avg] = calculateStats([10, 20, 30, 40, 50]);
echo "Min: $min, Max: $max, Average: $avg";
// Min: 10, Max: 50, Average: 30

Variables in Strings #

PHP supports variable interpolation inside double-quoted strings. There are two syntaxes to understand:

<?php
$name   = "Budi";
$product = ["name" => "Laptop", "price" => 15000000];

// Simple interpolation — scalar variables directly inside the string
echo "Hello, $name!";                     // Hello, Budi!

// Interpolation with curly braces — for more complex expressions
echo "Hello, {$name}!";                   // Hello, Budi! (same)
echo "Product: {$product['name']}";        // Product: Laptop

// Expression interpolation — only possible with the ${expr} syntax
$i = 2;
echo "Element number-$i";                 // Element number-2

// What CANNOT be interpolated directly (needs concatenation)
echo "Price: Rp " . number_format($product['price']); // functions must be concatenated

// ANTI-PATTERN: repeated concatenation that could be simplified
$message = "Hello " . $name . ", welcome to " . $appName . "!";

// CORRECT: interpolation is cleaner for strings with many variables
$message = "Hello, {$name}, welcome to {$appName}!";

Heredoc and Nowdoc #

For long multi-line strings, PHP provides two special syntaxes:

<?php
$name  = "Budi";
$total = 150000;

// Heredoc — like double quotes, supports interpolation
$email = <<<EOT
Dear {$name},

Thank you for your purchase.
Total paid: Rp {$total}

Best regards,
Customer Service Team
EOT;

echo $email;

// Nowdoc — like single quotes, NO interpolation
$template = <<<'EOT'
The variable $name will not be interpolated.
This is literal text, as-is.
EOT;

echo $template;
// Output: The variable $name will not be interpolated.

Heredoc is very useful for HTML templates or long emails containing many variables. Nowdoc is useful for storing text that genuinely contains $ characters literally, such as scripts or other code.


Summary #

  • Every PHP variable starts with $ — there’s no type declaration; PHP determines the type from the assigned value. Variables are case-sensitive: $name and $Name are two different variables.
  • Function scope is isolated — global variables aren’t automatically available inside functions. Pass data via parameters, not the global keyword.
  • Static variables (static) retain their value between function calls — useful for counters and simple memoization.
  • Pass-by-value is the default — functions receive a copy; modifications inside the function don’t affect the original variable. Use & for references when genuinely needed.
  • Always unset() a reference after foreach & — without it, the reference variable still points at the array’s last element and can cause subtle bugs.
  • isset() vs empty() vs is_null() — understand the difference: isset() checks existence and not-null; empty() checks falsy; is_null() checks for null exactly.
  • Destructuring [] — an elegant way to extract values from an array into separate variables, supporting both indexed and associative arrays.
  • String interpolation — use "$var" or "{$var}" inside double-quoted strings instead of repeated concatenation for cleaner code.
  • Avoid variable variables ($$var) — use an associative array instead; it’s clearer, safer, and easier to debug.

← Previous: Comments   Next: Constants →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact