PHP Class Constants
What Are Class Constants?
In simple terms: A class constant is a fixed value that belongs to a class — a value that never changes while the program runs, such as a tax rate, a maximum length, or a status name. Instead of scattering the literal 18 or 'pending' throughout the code, you give it a meaningful name in the class it belongs to: Tax::GST_RATE, Order::STATUS_PENDING.
Technically: Class constants are declared with the const keyword inside a class, interface, trait (PHP 8.2+), or enum. They are accessed with the scope resolution operator :: — ClassName::NAME from outside, self::NAME or static::NAME inside. Their values are evaluated from constant expressions. Since PHP 7.1 they can have visibility, since PHP 8.1 they can be final, and since PHP 8.3 they can have a type.
Constants vs Global Constants vs Properties
| Class Constant | Global Constant (define/const) | Property | |
|---|---|---|---|
| Belongs to | A class | The whole program | An object (or class, if static) |
| Can change? | No | No | Yes (unless readonly) |
| Access | Order::STATUS_PAID | APP_VERSION | $order->status |
| Grouping | Grouped with related code | Global namespace | Per object |
Declaring and Using Class Constants
Example
<?php
class Order
{
public const STATUS_PENDING = 'pending';
public const STATUS_PAID = 'paid';
public const STATUS_SHIPPED = 'shipped';
public const MAX_ITEMS = 50;
public const RETURN_WINDOW_SECONDS = 7 * 24 * 60 * 60;
private string $status = self::STATUS_PENDING;
public function markAsPaid(): void
{
$this->status = self::STATUS_PAID;
}
public function status(): string
{
return $this->status;
}
}
$order = new Order();
echo $order->status() . "\n";
$order->markAsPaid();
var_dump($order->status() === Order::STATUS_PAID);
echo "Max items: " . Order::MAX_ITEMS . "\n";
echo "Return window: " . Order::RETURN_WINDOW_SECONDS / 86400 . " days\n";
Output
pending
bool(true)
Max items: 50
Return window: 7 days
How It Works
constdeclares a constant; by convention, names are written inUPPER_SNAKE_CASE.- Inside the class,
self::STATUS_PAIDrefers to the constant; outside,Order::STATUS_PAID. - Constants can be used as property defaults (
= self::STATUS_PENDING), which is not possible with function calls. RETURN_WINDOW_SECONDSuses a constant expression (7 * 24 * 60 * 60); the expression documents how the value was derived.
Why Constants Help
- No magic values:
Order::STATUS_PAIDexplains itself;'paid'scattered in fifty places invites typos ('payed'). - One place to change: if the return window changes to 10 days, only the constant changes.
- IDE support: editors autocomplete constant names and find all usages.
Constant Expressions
Constant values can use literals, arithmetic, string concatenation, arrays, other constants, and (since PHP 8.1) enum cases — but no function calls and no object creation:
<?php
class Tax
{
public const GST_STANDARD = 18;
public const GST_RATES = [
'books' => 0,
'stationery' => 12,
'electronics' => self::GST_STANDARD,
];
public const CURRENCY = 'INR';
public const LABEL = 'GST (' . self::CURRENCY . ')';
}
echo Tax::LABEL . "\n";
echo Tax::GST_RATES['electronics'] . "%\n";
Output
GST (INR)
18%
Array constants can be read element by element, but — like all constants — never modified.
Visibility of Constants
class PaymentGateway
{
public const CURRENCY = 'INR'; // part of the public API
protected const RETRY_LIMIT = 3; // for subclasses
private const API_PATH = '/v2/pay'; // internal detail
}
Constants without a modifier are public. Making internal constants private keeps them out of the class's public API, so they can change freely (see PHP Access Modifiers).
Typed Class Constants (PHP 8.3+)
Constants can declare a type, which PHP checks — important when child classes or implementing classes redefine a constant:
<?php
interface HasVersion
{
public const string VERSION = '1.0';
}
class ApiV2 implements HasVersion
{
public const string VERSION = '2.0'; // must still be a string
}
echo ApiV2::VERSION . "\n";
Output
2.0
Redefining it with a value of a different type is a fatal error:
PHP Fatal error: Cannot use int as value for class constant ApiV3::VERSION of type string
Without types, a subclass could accidentally change a constant from a string to an integer, breaking code that relies on string operations.
self:: vs static:: for Constants
self:: refers to the class where the code is written; static:: refers to the class of the object (or the class the method was called on). Child classes can redefine non-final constants, and static:: picks up the child's value:
<?php
class Notification
{
public const CHANNEL = 'email';
public function describe(): string
{
return 'self: ' . self::CHANNEL . ', static: ' . static::CHANNEL;
}
}
class SmsNotification extends Notification
{
public const CHANNEL = 'sms';
}
echo (new Notification())->describe() . "\n";
echo (new SmsNotification())->describe() . "\n";
Output
self: email, static: email
self: email, static: sms
Use static:: when subclasses are expected to customize a constant; use self:: when the value must be exactly the one defined in the current class. (This is late static binding, covered in Late Static Binding.) Mark constants final (PHP 8.1+) when subclasses must not change them (see The final Keyword).
Accessing Constants Dynamically
<?php
class Order
{
public const STATUS_PENDING = 'pending';
public const STATUS_PAID = 'paid';
}
$name = 'STATUS_PAID';
echo Order::{$name} . "\n"; // PHP 8.3+
echo constant(Order::class . '::' . $name) . "\n"; // all versions
$statuses = array_filter(
(new ReflectionClass(Order::class))->getConstants(),
fn(string $constant): bool => str_starts_with($constant, 'STATUS_'),
ARRAY_FILTER_USE_KEY
);
print_r($statuses);
Output
paid
paid
Array
(
[STATUS_PENDING] => pending
[STATUS_PAID] => paid
)
Order::{$name}(PHP 8.3+) andconstant()fetch a constant whose name is in a variable.- Reflection lists all constants of a class — useful for building select lists or validation rules (see Reflection API).
Constants vs Enums
Groups of related constants such as order statuses are a classic use of class constants. Since PHP 8.1, enums are often the better tool for them:
| Class Constants | Enums |
|---|---|
| Values are plain strings/ints; any string is accepted where a status is expected | OrderStatus is a type; only valid cases are accepted |
| No built-in list of allowed values | OrderStatus::cases() lists all values |
| Cannot have methods per value | Enum cases can have methods (label(), color()) |
Use class constants for configuration-like values (limits, rates, versions, keys) and enums for sets of states or options (see PHP Enums).
Practical Example: GST Calculation
<?php
final class GstCalculator
{
private const RATES = [
'books' => 0,
'stationery' => 12,
'electronics' => 18,
];
public const DEFAULT_CATEGORY = 'electronics';
public function tax(float $amount, string $category = self::DEFAULT_CATEGORY): float
{
if (!array_key_exists($category, self::RATES)) {
throw new InvalidArgumentException("Unknown category: $category");
}
return round($amount * self::RATES[$category] / 100, 2);
}
}
$gst = new GstCalculator();
$items = [
['Desk Lamp', 1299, 'electronics'],
['Notebook', 60, 'stationery'],
['PHP Handbook', 499, 'books'],
];
foreach ($items as [$name, $price, $category]) {
printf("%-13s ₹%8.2f GST ₹%6.2f\n", $name, $price, $gst->tax($price, $category));
}
Output
Desk Lamp ₹ 1299.00 GST ₹233.82
Notebook ₹ 60.00 GST ₹ 7.20
PHP Handbook ₹ 499.00 GST ₹ 0.00
Step-by-Step Explanation
- The rate table is a private constant: an internal detail that callers cannot read or change.
DEFAULT_CATEGORYis public, so callers can refer to the default, and it is also used as a parameter default (= self::DEFAULT_CATEGORY).- Unknown categories are rejected, so a typo cannot silently produce a tax of zero.
- When rates change, only the constant changes; every calculation follows automatically.
(In a real application, tax rates that change over time are usually stored in the database with validity dates; constants suit values that change only with a code release.)
Common Mistakes
Using $ or -> With Constants
Effect: $this->MAX_ITEMS or Order::$MAX_ITEMS refer to properties, not constants.
Fix: Use self::MAX_ITEMS or Order::MAX_ITEMS.
Calling Functions in Constant Values
Effect: Compile error, because constants require constant expressions.
Fix: Use literals and operators, or initialize a property in the constructor.
Using self:: When Subclasses Override the Constant
Effect: The parent's value is used even in child objects.
Fix: Use static:: for overridable constants.
Using Constants Where an Enum Fits Better
Effect: Any string is accepted as a "status", and invalid values slip through.
Fix: Use enums for closed sets of values.
Best Practices
- Replace magic numbers and strings with well-named class constants.
- Keep constants in the class they relate to, and make internal ones
private. - Add types to constants (PHP 8.3+) and mark non-overridable ones
final. - Use
static::for constants that subclasses may customize. - Prefer enums for sets of related states or options.
Official reference: https://www.php.net/manual/en/language.oop5.constants.php