Static functions and static local variables
Concept
Static functions are defined with the static keyword on a class method. They belong to the class itself, not to any instance — you call them with ClassName::method() without needing an object. They cannot access $this.
Static local variables are different: they're regular (non-method) function variables declared with static $varName. A static local variable retains its value between calls to the same function within the same PHP request. Unlike regular local variables, it is not initialized fresh on each call — it persists.
When static functions are appropriate: utility/helper functions with no state that are logically grouped with a class (e.g., DateTime::createFromFormat()), factory methods (User::fromRequest()), and the singleton pattern (though DI is preferred). Laravel uses static methods extensively for its facades and fluent APIs.
Static local variable use cases: Counters, caches within a function, "call-once" initialization (lazy initialization without a class), debug call counting. They're a lightweight form of state that doesn't require passing state through parameters or using a class. Be aware they can make functions harder to test — functions using static locals are not pure.
Static vs singleton: A static local for caching is lightweight. A full singleton class adds boilerplate. For simple single-request caching, static locals are fine. For multi-dependency objects that need injection, use a proper container.
Code Example
<?php
declare(strict_types=1);
// Static method — no instance needed
class MathHelper
{
public static function clamp(float $value, float $min, float $max): float
{
return max($min, min($max, $value));
}
public static function lerp(float $a, float $b, float $t): float
{
return $a + ($b - $a) * $t;
}
}
echo MathHelper::clamp(150.0, 0.0, 100.0); // 100.0
echo MathHelper::lerp(0.0, 10.0, 0.5); // 5.0
// Factory static method pattern
class Color
{
private function __construct(
public readonly int $r,
public readonly int $g,
public readonly int $b,
) {}
public static function fromHex(string $hex): self
{
$hex = ltrim($hex, '#');
return new self(
hexdec(substr($hex, 0, 2)),
hexdec(substr($hex, 2, 2)),
hexdec(substr($hex, 4, 2)),
);
}
public static function fromRgb(int $r, int $g, int $b): self
{
return new self($r, $g, $b);
}
}
$red = Color::fromHex('#ff0000');
// Static local variable — counter
function callCount(): int
{
static $count = 0; // initialized ONCE to 0, then persists
return ++$count;
}
echo callCount(); // 1
echo callCount(); // 2
echo callCount(); // 3
// Static local — lazy initialization / memoization
function getExpensiveConfig(): array
{
static $config = null;
if ($config === null) {
$config = loadFromDatabase(); // only called once per request
}
return $config;
}
// Static local — debug tracer
function trace(string $label): void
{
static $startTime = null;
if ($startTime === null) {
$startTime = microtime(true);
}
$elapsed = round((microtime(true) - $startTime) * 1000, 2);
echo "[$elapsed ms] $label\n";
}