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:
| Context | Convention | Examples |
|---|---|---|
| Local variables | camelCase | $fullName, $totalPrice |
| Function parameters | camelCase | $userId, $isActive |
| Class properties | camelCase | $this->firstName |
| Global variables (avoid) | snake_case or UPPER_SNAKE | $_config, $MAX_RETRY |
| Short loop variables | Single 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 callunset($refVariable)after aforeachthat uses a reference (&). Without it,$refVariablestill 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:
| Function | Undefined | null | 0 | "" | false | [] |
|---|---|---|---|---|---|---|
isset() | false | false | true | true | true | true |
empty() | true | true | true | true | true | true |
is_null() | warning | true | false | false | false | false |
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:$nameand$Nameare two different variables.- Function scope is isolated — global variables aren’t automatically available inside functions. Pass data via parameters, not the
globalkeyword.- 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 afterforeach &— without it, the reference variable still points at the array’s last element and can cause subtle bugs.isset()vsempty()vsis_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.