Использование const в классах PHP: синтаксис и нюансы
Константы класса в PHP
Константой класса называют неизменяемое значение, которое принадлежит самому классу, а не его экземплярам. Для объявления используется ключевое слово const внутри класса. Доступ к константе осуществляется через имя класса или self:: внутри методов. Константы не могут быть изменены после определения.
class Product {
const TAX = 0.20;
public static function getPriceWithTax(float $price): float {
return $price * (1 + self::TAX);
}
}
echo Product::TAX; // 0.2
echo Product::getPriceWithTax(100); // 120
Php class const (константы класса php)
Частая ошибка:
- Попытка изменить константу: Product::TAX = 0.15; вызовет фатальную ошибку.
- Обращение через $product->TAX не работает, так как константы не являются свойствами объекта.
Как ограничить область видимости константы?
Начиная с PHP 7.1 константам можно назначать модификаторы видимости: public, protected, private. По умолчанию константы публичны.
class Config {
public const PUBLIC_KEY = 'pub-xyz';
protected const PROTECTED_KEY = 'protected-abc';
private const SECRET = 's3cr3t';
public function revealSecret(): string {
return self::SECRET; // доступен внутри класса
}
}
echo Config::PUBLIC_KEY; // pub-xyz
// echo Config::PROTECTED_KEY; // ошибка доступа
// echo Config::SECRET; // ошибка доступа
$cfg = new Config();
echo $cfg->revealSecret(); // s3cr3t
При использовании приватных или защищённых констант за пределами класса возникает фатальная ошибка. В дочерних классах доступны только публичные и защищённые константы.
Какие значения можно присвоить константе?
Константам можно присваивать скалярные значения (int, float, string, bool), а также массивы и выражения, которые могут быть вычислены на этапе компиляции. Выражения могут включать арифметические операции, константы других классов, но не вызовы функций (кроме define() и конструкций __CLASS__ и подобных не допускаются).
class Math {
const PI = 3.14159;
const DOUBLE_PI = self::PI * 2;
const ALLOWED_TYPES = ['jpg', 'png', 'gif'];
}
echo Math::DOUBLE_PI; // 6.28318
print_r(Math::ALLOWED_TYPES); // Array ( [0] => jpg [1] => png [2] => gif )
Следующий код вызовет ошибку, поскольку вызов функции недопустим в выражении константы:
class Foo {
const BAR = time(); // Ошибка!
}
Для получения динамических значений следует использовать статические свойства или методы.
Можно ли переопределить константу в дочернем классе?
Да, константы можно переопределять в дочерних классах (переопределение не является обязательным). При обращении через имя дочернего класса будет использовано его значение, а при обращении через имя родительского класса - родительское.
class ParentClass {
const VALUE = 'parent';
}
class ChildClass extends ParentClass {
const VALUE = 'child';
}
echo ParentClass::VALUE; // parent
echo ChildClass::VALUE; // child
Если в дочернем классе не переопределена константа, то используется значение родительского класса. Нельзя переопределить константу, объявленную как private в родителе.
Зачем нужны константы в интерфейсах?
Интерфейсы могут содержать константы, которые автоматически наследуются всеми классами, реализующими интерфейс. Это удобно для фиксации общих значений, связанных с поведением.
interface Statusable {
const STATUS_ACTIVE = 1;
const STATUS_INACTIVE = 0;
}
class User implements Statusable {
public function getStatus(): int {
return self::STATUS_ACTIVE; // доступна через self
}
}
echo User::STATUS_ACTIVE; // 1
Константы интерфейса не могут быть переопределены в реализующем классе - они принадлежат интерфейсу. Попытка переопределить константу в классе приведёт к предупреждению (с PHP 8.1 это фатальная ошибка).
Как обратиться к константе в контексте вызывающего класса (позднее статическое связывание)?
Обычное self:: всегда ссылается на класс, где написано определение. Для получения константы того класса, который вызвал метод (позднее статическое связывание), используется static::. Это особенно полезно при наследовании.
class ParentClass {
const NAME = 'Parent';
public function who(): string {
return static::NAME;
}
}
class ChildClass extends ParentClass {
const NAME = 'Child';
}
$obj = new ChildClass();
echo $obj->who(); // Child
Если метод вызывает self::NAME вместо static::NAME, то результатом всегда будет 'Parent', независимо от конкретного класса объекта.
Можно ли объявить константы в трейтах?
Трейты с PHP 8.2 могут содержать константы. Если трейт используется в нескольких классах, каждый класс получает собственную копию константы (возможны конфликты, которые разрешаются так же, как для методов).
trait Identifiable {
const PREFIX = 'ID_';
public function getId(): string {
return self::PREFIX . spl_object_id($this);
}
}
class Entity {
use Identifiable;
}
echo Entity::PREFIX; // ID_
Если два трейта (или трейт и класс) объявляют константу с одним именем, возникает фатальная ошибка. Для разрешения конфликта можно использовать оператор insteadof, но только для методов. Для констант конфликт неразрешим, поэтому следует избегать дублирования.
Как использовать константы класса для централизованного хранения значений?
Константы класса применяются для хранения фиксированных настроек, статусов, кодов ошибок, названий событий - всего, что не должно меняться во время выполнения. Такой подход улучшает читаемость и уменьшает количество «магических чисел».
class HttpStatus {
const OK = 200;
const NOT_FOUND = 404;
const INTERNAL_SERVER_ERROR = 500;
}
function handleResponse(int $code): void {
if ($code === HttpStatus::OK) {
echo 'Success';
} elseif ($code === HttpStatus::NOT_FOUND) {
echo 'Not found';
}
}
handleResponse(404); // Not found
Если в будущем понадобится изменить значение, это делается только в одном месте - в определении константы. Использование строковых литералов вместо констант затрудняет рефакторинг.
Дополнительные примеры работы с константами класса
Пример 1. Константа-массив и доступ к элементам
class FruitBasket {
const FRUITS = ['apple', 'banana', 'cherry'];
}
echo FruitBasket::FRUITS[1]; // banana
banana
Пример 2. Битовые маски на основе констант
class Permission {
const READ = 1;
const WRITE = 2;
const EXECUTE = 4;
const ALL = self::READ | self::WRITE | self::EXECUTE;
}
$userPerms = Permission::READ | Permission::EXECUTE;
if ($userPerms & Permission::READ) {
echo 'Read access granted';
}
Read access granted
Пример 3. Зависимость констант друг от друга
class Circle {
const PI = 3.1416;
const RADIUS = 5;
const AREA = self::PI * self::RADIUS ** 2;
}
echo Circle::AREA; // 78.54
78.54
Пример 4. Константа как значение по умолчанию для аргумента метода
class Logger {
const DEFAULT_LEVEL = 'info';
public function log(string $message, string $level = self::DEFAULT_LEVEL): void {
echo "[$level] $message";
}
}
$l = new Logger();
$l->log('Server started'); // [info] Server started
[info] Server started
Пример 5. Позднее статическое связывание в абстрактном классе
abstract class Animal {
const SOUND = '...';
public function speak(): string {
return static::SOUND;
}
}
class Dog extends Animal {
const SOUND = 'Woof!';
}
class Cat extends Animal {
const SOUND = 'Meow!';
}
$animals = [new Dog(), new Cat()];
foreach ($animals as $a) {
echo $a->speak() . "\n";
}
Woof! Meow!
Пример 6. Константы в трейтах с разрешением конфликта (конфликт неразрешим для констант)
trait A {
const ID = 'A';
}
trait B {
const ID = 'B';
}
// class C { use A, B; } // Фатальная ошибка - конфликт константы ID
class D {
use A, B {
A::ID insteadof B; // Ошибка: insteadof не поддерживается для констант
}
}
Fatal error: Trait method ID has not been applied, because there are collisions with other trait methods
Рекомендация:
Для констант в трейтах избегать одинаковых имён, либо использовать модификатор final в классе.
Пример 7. Приватная константа и её чтение через рефлексию
class SecretHolder {
private const SECRET = 'hidden';
}
$ref = new ReflectionClassConstant(SecretHolder::class, 'SECRET');
$ref->setAccessible(true);
echo $ref->getValue(); // hidden
hidden