Static Variables
What Is a Static Variable?
In simple terms: A static variable is a variable inside a function that remembers its value between calls to that function.
Technically: A static variable is declared inside a function with the static keyword. It has local scope — it can only be used inside that function — but its lifetime extends across all calls to the function for the rest of the script's execution. It is initialized only once, the first time the declaration is reached.
Why Static Variables Exist
Normal local variables are destroyed when a function returns, and each new call starts from scratch. Sometimes a function needs to keep information between calls:
- Counting how many times it has been called
- Caching a result that is expensive to compute
- Creating something once (such as a database connection) and reusing it
- Generating unique sequential IDs
A global variable could store this, but it would be visible to — and changeable by — the entire program. A static variable keeps the value private to the function while still preserving it between calls.
The Problem Static Variables Solve
Without static
<?php
function countCall()
{
$count = 0;
$count++;
echo "Called $count time(s)\n";
}
countCall();
countCall();
countCall();
Output
Called 1 time(s)
Called 1 time(s)
Called 1 time(s)
Each call creates a new $count, sets it to 0, increments it to 1, and destroys it when the function returns.
With static
<?php
function countCall()
{
static $count = 0;
$count++;
echo "Called $count time(s)\n";
}
countCall();
countCall();
countCall();
Output
Called 1 time(s)
Called 2 time(s)
Called 3 time(s)
How It Works
- On the first call, PHP reaches
static $count = 0;, creates the static variable, and sets it to0. $count++makes it1, which is printed.- When the function returns,
$countis not destroyed. PHP keeps its value attached to the function. - On the second call, PHP sees that
$countalready exists, so the initializer= 0is skipped. The previous value1is used, and$count++makes it2. - The third call continues from
2to3.
The key detail: the initializer runs only once. That is what distinguishes static $count = 0; from $count = 0;.
Syntax
static $variableName = initialValue;
| Part | Meaning |
|---|---|
static | Keyword that gives the variable a lifetime beyond a single call |
$variableName | A normal variable name, local to the function |
= initialValue | Optional. Value used the first time only. Defaults to null if omitted. |
Multiple static variables can be declared in one statement:
static $calls = 0, $lastArgument = null;
What the Initial Value Can Be
Before PHP 8.3, the initial value had to be a constant expression — a literal, a constant, or a simple calculation using them:
static $count = 0; // literal
static $limit = 60 * 60; // constant arithmetic
static $items = []; // empty array
static $rate = TAX_RATE; // constant
Function calls were not allowed:
static $startTime = microtime(true); // Fatal error before PHP 8.3
Since PHP 8.3, static variable initializers may contain arbitrary expressions, including function calls. The expression is still evaluated only once, on the first call.
If your code must support versions older than 8.3, initialize with null and assign on first use, as shown in the caching examples below. That pattern works on every PHP version.
Static vs Local vs Global
| Aspect | Local Variable | Static Variable | Global Variable |
|---|---|---|---|
| Declared | Inside a function | Inside a function with static | Outside functions |
| Visible | Only inside the function | Only inside the function | Only outside functions (unless imported with global) |
| Lifetime | One function call | Entire script execution | Entire script execution |
| Initialized | Every call | Once | When assigned |
| Can other code change it? | No | No | Yes — any code using global or $GLOBALS |
Static variables combine the privacy of local variables with the persistence of global variables.
Lifetime: One Request, Not Forever
Static variables keep their values for the duration of the current script execution — in a web application, a single HTTP request. They are not shared between requests or between users.
Example
<?php
function pageViews()
{
static $views = 0;
$views++;
return $views;
}
echo "Views: " . pageViews();
Output (every time the page is refreshed)
Views: 1
Why
PHP's shared-nothing architecture destroys all memory when a request ends, including static variables. The next request starts a fresh script. To count page views across requests, store the count in a database, a file, or a cache server.
Note: Long-running PHP runtimes (such as queue workers or application servers like RoadRunner, Swoole, or FrankenPHP worker mode) keep the same process alive across many jobs or requests. In those environments, static variables do persist between requests, which can leak data from one user to another if you are not careful. Avoid storing user-specific data in static variables.
Practical Use 1: Caching an Expensive Result
A common use of static variables is memoization — computing a value once and returning the stored result on later calls.
Example
<?php
function getSettings(): array
{
static $settings = null;
if ($settings === null) {
echo "(Loading settings from storage...)\n";
// In a real application: read a file, query a database, or call an API
$settings = [
'siteName' => 'GoLearn',
'currency' => 'INR',
'itemsPerPage' => 20,
];
}
return $settings;
}
echo getSettings()['siteName'] . "\n";
echo getSettings()['currency'] . "\n";
echo getSettings()['itemsPerPage'] . "\n";
Output
(Loading settings from storage...)
GoLearn
INR
20
Step-by-Step Explanation
- First call:
$settingsis initialized tonull. The condition$settings === nullis true, so the settings are loaded and stored in the static variable. - Second and third calls:
$settingsalready holds the array, so the loading block is skipped entirely and the stored array is returned immediately. - The message "Loading settings..." appears only once, proving the expensive work happened once.
Why === null Instead of empty()
If the loaded value could legitimately be empty (for example, an empty array when no settings exist), empty($settings) would reload on every call. Checking strictly against the initial null distinguishes "not loaded yet" from "loaded, but empty".
This pattern is useful when many parts of a page request the same data. Without caching, each call would repeat the database query or file read.
Practical Use 2: Memoizing Recursive Calculations
Recursive functions often recalculate the same values many times. A static cache can eliminate this repeated work.
Example
<?php
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) . "\n";
echo fibonacci(50) . "\n";
Output
55
12586269025
How It Works
- The Fibonacci sequence is 0, 1, 1, 2, 3, 5, 8, … where each number is the sum of the previous two.
- Without caching,
fibonacci(50)would make billions of recursive calls, because the same values are recomputed again and again. - With the static
$cachearray, each value from 2 to 50 is calculated once and stored. Later requests for the same$nreturn the stored value immediately. - All recursive calls share the same
$cache, because they are calls to the same function. - The cache persists after the first call, so the second call,
fibonacci(50), reuses the values already computed forfibonacci(10).
Recursion is covered in the Recursive Functions lesson.
Practical Use 3: Generating Unique IDs
Example
<?php
function nextFieldId(string $prefix = 'field'): string
{
static $counter = 0;
$counter++;
return $prefix . '-' . $counter;
}
$nameId = nextFieldId();
$emailId = nextFieldId();
$phoneId = nextFieldId('input');
?>
<label for="<?= $nameId ?>">Name</label>
<input id="<?= $nameId ?>" name="name">
<label for="<?= $emailId ?>">Email</label>
<input id="<?= $emailId ?>" name="email">
<label for="<?= $phoneId ?>">Phone</label>
<input id="<?= $phoneId ?>" name="phone">
Output (HTML)
<label for="field-1">Name</label>
<input id="field-1" name="name">
<label for="field-2">Email</label>
<input id="field-2" name="email">
<label for="input-3">Phone</label>
<input id="input-3" name="phone">
Why This Is Useful
HTML id attributes must be unique on a page, and <label for="..."> must match the input's id for accessibility and for clicking a label to focus its input. When form fields are generated by functions or included components, a static counter guarantees unique IDs without passing a counter around manually.
Notice that the counter is shared across all calls regardless of the prefix argument, which is why the phone field gets 3, not 1.
Practical Use 4: Creating a Resource Once
Opening a database connection is relatively slow. A static variable can ensure that a page uses a single connection, no matter how many functions request it.
<?php
function db(): PDO
{
static $pdo = null;
if ($pdo === null) {
$pdo = new PDO(
'mysql:host=localhost;dbname=shop;charset=utf8mb4',
'shop_user',
'secret',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
}
return $pdo;
}
// Anywhere in the application:
// $products = db()->query('SELECT name FROM products')->fetchAll();
How It Works
- The first call to
db()creates the PDO connection and stores it. - Every later call returns the same connection object.
- PDO is covered in the MySQL Database section; here, the focus is the "create once, reuse" pattern.
Note: In real projects, keep credentials in environment variables or configuration files outside the web root, not in source code. In larger applications, a dependency injection container usually manages shared resources like this, because it makes them easier to replace in tests.
Static Variables in Class Methods
Static variables can also be used inside methods of a class. Their behavior with inheritance changed in PHP 8.1.
Example
<?php
class Counter
{
public function increment(): int
{
static $count = 0;
return ++$count;
}
}
class SpecialCounter extends Counter
{
}
$a = new Counter();
$b = new SpecialCounter();
echo $a->increment() . "\n";
echo $b->increment() . "\n";
echo $a->increment() . "\n";
Output (PHP 8.1 and newer)
1
2
3
Explanation
- The static variable belongs to the method, not to an object. All objects calling the same method share one
$count, so$aand$bincrement the same counter. - Since PHP 8.1, an inherited method (one the child class does not override) shares its static variables with the parent's method. Before PHP 8.1,
SpecialCounterhad its own separate copy, and the output was1,1,2.
When state belongs to a class, a static property (private static int $count = 0;) is usually clearer than a static variable inside a method, because it is visible in the class definition. Static properties are covered in PHP Static Properties.
Not the same thing:
staticis also used for static methods, static properties,static fnclosures, and late static binding (static::). These are different features that share the keyword. This lesson covers only static variables inside functions.
Limitations
- No access from outside. No other code can read or reset a function's static variable. If you need to clear a cache (for example, in tests), you must design the function with an explicit way to do so, such as a reset parameter.
- Hidden state. A function with a static variable can return different results for the same arguments depending on earlier calls. This makes behavior harder to predict and to test.
- Memory growth. A static cache that stores every result keeps growing for the whole request. For a large number of possible inputs, limit the cache size.
- Per-request only in traditional PHP; cross-request in long-running workers.
Common Mistakes
Expecting a Value to Persist Across Page Loads
What happens: A counter or cache resets on every request.
Why: Static variables live for one script execution.
Fix: Use sessions for per-user data, and a database, file, or cache server for shared data.
Using a Function Call as the Initial Value on PHP Older Than 8.3
static $start = time();
What happens: Fatal error: Constant expression contains invalid operations on PHP 8.2 and older.
Fix: Upgrade to PHP 8.3+, or initialize with null and assign the value on first use.
Resetting the Value on Every Call
<?php
function countCall()
{
static $count;
$count = 0; // runs on every call
$count++;
return $count;
}
What happens: The function always returns 1.
Why: $count = 0; is an ordinary assignment, not the static initializer, so it executes every time.
Fix: Put the initial value in the declaration: static $count = 0;.
Storing User-Specific Data in Long-Running Workers
What happens: Data from one request (such as the current user) appears in another request handled by the same worker process.
Fix: Do not keep request- or user-specific data in static variables; pass it explicitly.
Using Static Variables Where Parameters Belong
What happens: A function's result depends on previous calls in surprising ways, and tests become order-dependent.
Fix: Use static variables only for caching, counters, and one-time setup — not as a way to pass data between calls.
Best Practices
- Use static variables for caching, counting, and one-time initialization inside a single function.
- Initialize caches with
nulland check with=== nullto distinguish "not loaded" from "empty". - Keep cached values small, or limit the number of stored entries.
- Prefer static properties when the state belongs to a class.
- Avoid storing user- or request-specific data in static variables.
- For shared resources in larger applications, consider dependency injection instead of hidden static state.
Official reference: https://www.php.net/manual/en/language.variables.scope.php#language.variables.scope.static