Constants and Static Members
A normal Property belongs to an Object. A Class Constant or Static Member belongs to the Class level. Choosing between them depends on whether the value is fixed, shared and mutable, or independent for every Object.
Three different kinds of data
Section titled “Three different kinds of data”final class Order{ public const STATUS_PENDING = 'pending';
private static int $createdCount = 0;
private string $status = self::STATUS_PENDING;}STATUS_PENDINGis a fixed value associated with the Class.$createdCountis one mutable value shared by the Class.$statusis independent State stored by every Order Object.
Constants have no $ when accessed. Static Properties do:
Order::STATUS_PENDING;self::STATUS_PENDING;self::$createdCount;A complete Order example
Section titled “A complete Order example”<?php
declare(strict_types=1);
final class Order{ public const STATUS_PENDING = 'pending'; public const STATUS_PAID = 'paid';
private static int $createdCount = 0;
private string $status = self::STATUS_PENDING;
public function __construct() { self::$createdCount++; }
public function markAsPaid(): void { $this->status = self::STATUS_PAID; }
public function status(): string { return $this->status; }
public static function createdCount(): int { return self::$createdCount; }}
$first = new Order();$second = new Order();
$first->markAsPaid();
echo $first->status() . PHP_EOL; // paidecho $second->status() . PHP_EOL; // pendingecho Order::createdCount(); // 2The two Objects share the Class-level counter but retain independent status. Keeping both Properties private prevents callers from inventing a status or corrupting the counter.
self, static, and parent
Section titled “self, static, and parent”These keywords select a Class when accessing a Class-level Member:
self::selects the Class containing the expression.static::selects the Class from which the call began at runtime. This is Late Static Binding.parent::selects the direct Parent of the Class containing the expression.
class Payment{ protected const TYPE = 'generic';
public static function usingSelf(): string { return self::TYPE; }
public static function usingStatic(): string { return static::TYPE; }}
class CardPayment extends Payment{ protected const TYPE = 'card';
public static function parentType(): string { return parent::TYPE; }}
echo CardPayment::usingSelf(); // genericecho CardPayment::usingStatic(); // cardecho CardPayment::parentType(); // genericThe Child redeclares its own Constant; it does not mutate the Parent’s value.
A final Constant prevents redeclaration. In a final Class, self:: is often
clearer than static:: because no Child Class can benefit from late binding.
Static Method or Object Service?
Section titled “Static Method or Object Service?”A small calculation with explicit inputs can be a reasonable Static Method:
final class PriceFormatter{ public static function format(float $amount): string { return number_format($amount, 2) . ' EGP'; }}A checkout operation usually collaborates with payment, persistence, and notification Objects. Making that workflow static may hard-code or hide those Dependencies. An Object Service can receive them once and expose an Instance Method:
final class CheckoutService{ public function __construct( private PaymentGateway $paymentGateway, private OrderRepository $orders, private EmailSender $emailSender, ) {}
public function checkout(Order $order): void { // Coordinate the injected collaborators. }}static is a PHP mechanism, not a synonym for a Service. Avoid shared mutable
Static State when ordinary Object State or explicit collaboration represents
the problem more clearly.
Common mistakes
Section titled “Common mistakes”- Treating a Static Property as independent State for every Object.
- Writing
self::countinstead ofself::$countfor a Static Property. - Assuming
selfalways refers to the first Parent Class. - Using
static::without needing late binding. - Exposing shared mutable State publicly.
- Converting dependency-heavy Services into Static Methods for convenient access.
Summary and knowledge check
Section titled “Summary and knowledge check”Constants are fixed Class-level values. Static Properties are mutable State
shared at Class level. Instance Properties belong independently to Objects.
self, static, and parent select different Classes during member access.
- Why is an Order status an Instance Property while a creation counter can be static?
- Why does a Static Property require
$but a Constant does not? - When can
self::TYPEandstatic::TYPEproduce different results? - Why can a dependency-heavy Static Service be difficult to replace and test?