Static Properties & Methods
Every property and method you've written so far has belonged to a
specific instance — $product1->name and $product2->name are two
separate pieces of storage, even though both objects come from the
same Product class. static properties and methods flip that
around: instead of belonging to an object, they belong to the class
itself. There is exactly one copy, shared by every instance, and you
can often use it without ever creating an object at all.
Declaring and accessing static members
You mark a property or method static, and you reach it with the
scope resolution operator :: against the class name — ClassName::$property
or ClassName::method() — never $this->. $this refers to a
particular object, and a static member does not belong to any
particular object.
A static helper method
<?php
class Product {
public static function formatPrice(float $amount): string
{
return '$' . number_format($amount, 2);
}
}
echo Product::formatPrice(19.9), "\n";
echo Product::formatPrice(1250), "\n";$19.90 $1,250.00
formatPrice() doesn't read or write any particular product's data — it's a pure utility that happens to live inside the Product class because that's where it's conceptually at home. Calling it never required a new Product(...) at all.
Static properties are shared across every instance
This is the part that surprises people coming from instance properties: a static property is not "reset" per object. There is one storage location for the entire class, and every instance reads and writes the same value.
Counting every Product ever created
<?php
class Product {
public static int $totalCreated = 0;
public string $name;
public function __construct(string $name)
{
$this->name = $name;
self::$totalCreated++;
}
}
$p1 = new Product('Keyboard');
$p2 = new Product('Mouse');
$p3 = new Product('Monitor');
echo Product::$totalCreated, "\n";3
Notice self::$totalCreated++ inside the constructor — from inside the class, you use self:: rather than the class name, which matters more once inheritance is involved (covered on the Inheritance page). From outside, Product::$totalCreated reads the one shared counter, no matter which instance last touched it.
A registry-style static property
Because a static property persists for the lifetime of the request and is reachable from anywhere the class is visible, it's a natural fit for a lightweight in-memory registry — for example, tracking every SKU that has been registered in a catalog without wiring a database call through every layer of the code.
A simple static catalog registry
<?php
class Catalog {
private static array $skus = [];
public static function register(string $sku): void
{
self::$skus[] = $sku;
}
public static function count(): int
{
return count(self::$skus);
}
}
Catalog::register('SKU-KEYBOARD-001');
Catalog::register('SKU-MOUSE-002');
echo Catalog::count(), "\n";2
When static helps, and when it hurts
Static members earn their keep for small, self-contained jobs: pure utility functions like Product::formatPrice() that don't depend on any object's state, simple counters, or a single shared registry/singleton where "exactly one" is actually the correct model. In those cases, static removes the ceremony of instantiating an object just to call one method.
The cost shows up in testing and flexibility. A static property is effectively global mutable state — any part of the codebase can change Catalog::$skus, and every other part will see that change immediately, which makes it hard to reason about and hard to reset between test cases. Static methods also can't be swapped out the way instance methods can (there's no straightforward way to "mock" Catalog::register() the way you can substitute an object that implements an interface). If a class's behavior needs to vary — say, a test double that fakes registration instead of really doing it — prefer an ordinary object passed around explicitly (dependency injection) over reaching for a static class.
ClassName::$propertyandClassName::method()access static members from outside the class;self::$propertyandself::method()access them from inside.A static property has exactly one value for the entire class — every instance reads and writes the same storage.
Good fits: pure utility functions, simple counters, small registries/singletons.
Poor fits: anything that needs to vary between tests or between callers — static state is hard to isolate and hard to mock.