Php. Traits. Constants

WebCoder

Прошу совета толковых программистов.

Мне часто не хватает возможности (php 5.6) описывать в трейтах константы. Свойства класса есть, а вот константы не ввели.

Собственно 2 вопроса.

Главный: Как правильно обеспечить множественное наследование с константами на уровне самого php?

Интересует: Почему разработчики не дали возможность подмешивать в классы константы с помощью трейтов? Может это плохая практика...

Дополнение: (многие не могут понять "А зачем?") Попробую показать. Есть в БД много таблиц в которых есть поле status: 0 - выкл, 1 - вкл, делаем трейт (зачем создавать таблицу для двух значений?)

trait Status
{
    const STATUS_INACTIVE = 0;
    const STATUS_ACTIVE = 1;

    public static function getStatuses()
    {
        return [
            static::STATUS_INACTIVE => 'Inactive',
            static::STATUS_ACTIVE => 'Active',
        ];
    }

    public function getStatusLabel($nullLabel = '')
    {
        $statuses = static::getStatuses();
        return isset($statuses[$this->status]) ? $statuses[$this->status] : $nullLabel;
    }
}

getStatuses() используем для выпадающего списка в форме, getStatusLabel() - тут я думаю и так ясно. и это упрощенный пример.

также можно создать другие трейты, а потом уже решать какие трейты подключать для какой-то сущности а какие нет. Одним словом мне нужны именно трейты с константами и я не понимаю почему не дали возможность их использовать в php.

Конечно я могу извратиться и сделать константы через статические свойства

trait Status
{
    protected static $STATUS_INACTIVE = 0;
    public static function STATUS_INACTIVE() { return static::$STATUS_INACTIVE; }

    protected static $STATUS_ACTIVE = 1;
    public static function STATUS_ACTIVE() { return static::$STATUS_ACTIVE; }

но хотелось бы понять почему не дали трейтам константы

2 ответа

WebCoder

Трейты в php реализованы, как copy-paste на уровне интерпретатора. Не стоит ими злоупотреблять. Решение не позволять константы в трейтах мотивировано сложностью разрешения коллизий имён констант. Возможно, когда-нибудь эту проблему решат.

А пока, ничто не мешает вам написать так:

interface IActiveStatus {
    const STATUS_INACTIVE = 0;
    const STATUS_ACTIVE = 1;
}
trait Status
{
    public static function getStatuses()
    {
        return [
            static::STATUS_INACTIVE => 'Inactive',
            static::STATUS_ACTIVE => 'Active',
        ];
    }

    public function getStatusLabel($nullLabel = '')
    {
        $statuses = static::getStatuses();
        return isset($statuses[$this->status]) ? $statuses[$this->status] : $nullLabel;
    }
}

class A implements IActiveStatus {
  use Status;
}

или лучше:

interface IActiveStatus {
    const STATUS_INACTIVE = 0;
    const STATUS_ACTIVE = 1;
}
trait Status
{
    public static function getStatuses()
    {
        return [
            IActiveStatus::STATUS_INACTIVE => 'Inactive',
            IActiveStatus::STATUS_ACTIVE => 'Active',
        ];
    }

    public function getStatusLabel($nullLabel = '')
    {
        $statuses = static::getStatuses();
        return isset($statuses[$this->status]) ? $statuses[$this->status] : $nullLabel;
    }
}

class A {
  use Status;
}

Хотя, я бы порекомендовал немножко пересмотреть интерфейс:

trait Status{
  protected $status = IActiveStatus::STATUS_INACTIVE;
  public function isActive() {
    return $this->status === IActiveStatus::STATUS_ACTIVE;
  }
  public function isInactive() {
    return $this->status === IActiveStatus::STATUS_INACTIVE;
  }
  public function setActive() {
    $this->status = IActiveStatus::STATUS_ACTIVE;
  }
  public function setInactive() {
    $this->status = IActiveStatus::STATUS_INACTIVE;
  }
  public function getStatusLabel() {
    return ([
        IActiveStatus::STATUS_ACTIVE => 'Active',
        IActiveStatus::STATUS_INACTIVE => 'Inactive',
    ])[$this->status];
  }
}


WebCoder

Можно не через трейты а через интерфейсы сделать. Отлично вписывается в множественное наследование

interface IStatus {
    const STATUS_ENABLED = 1;
    const STATUS_DISABLED = 0;
}

interface IExtStatus {
    const STATUS_BLOCKED = 2;
}

class Test implements IStatus, IExtStatus {
    const STATUS_LOCAL = 3;
}


echo Test::STATUS_BLOCKED;
echo Test::STATUS_LOCAL;
echo Test::STATUS_ENABLED;

licensed under cc by-sa 3.0 with attribution.