Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Ability to use Constants in Traits in PHP 8.2

November 18, 2022 ·

Constants are a great way to define values that are not going to change. They are also a great way to define values that are going to be used in multiple places.

For example, if you have a class that represents a person, you might want to define a constant for the minimum age of a person. This constant can be used in multiple places, such as in the constructor of the class, in a method that checks if the person is an adult, and so on.

The problem

Up until now, constants could only be defined in classes. This means that if you wanted to use a constant in a trait, you had to define it in a class that uses the trait.

Here’s an example of a trait that utilizes a constant defined in a compounding class (i.e. a class where the trait is used):

<?php

trait PersonTrait
{
    public function isAdult(): bool
    {
        return $this->age >= Person::MIN_AGE;
    }
}

class Person
{
    use PersonTrait;

    public const MIN_AGE = 18;

    private int $age;

    public function __construct(int $age)
    {
        $this->age = $age;
    }
}

This is not a big deal, but it can be a bit annoying. You have to define the constant in every class that uses the trait, and you have to make sure that the constant is defined before the trait is used.

The solution

This is going to be changed in PHP 8.2 with the introduction of constant traits. This means that you can now define constants in traits, and they can be used in classes that use the trait as well as in the trait itself.

Here’s an example of a trait that defines a constant:

<?php

trait PersonTrait
{
    public const MIN_AGE = 18;

    public function isAdult(): bool
    {
        return $this->age >= self::MIN_AGE;
    }
}

class Person
{
    use PersonTrait;

    private int $age;

    public function __construct(int $age)
    {
        $this->age = $age;
    }
}

echo Person::MIN_AGE;
// Output: 18

$person = new Person(20);

echo $person->isAdult();
// Output: true

As you can see in the example above, the constant MIN_AGE is defined in the trait PersonTrait. This constant can be used in the trait itself, as well as in the class Person.

You can also define final constants in traits. This means that the constant cannot be overridden in a class that uses the trait unless the class also defines the constant as final.

<?php

trait PersonTrait
{
    final public const MIN_AGE = 18;

    public function isAdult(): bool
    {
        return $this->age >= self::MIN_AGE;
    }
}

class Person
{
    use PersonTrait;

    private int $age;

    final public const MIN_AGE = 18;

    public function __construct(int $age)
    {
        $this->age = $age;
    }
}

So, if you have a constant that you want to use in multiple places, you can now define it in a trait and use it in multiple classes.

Apart from this, constants in traits can be used in Enums as well.

<?php

trait SuitTrait
{
    public const HEARTS = 'Hearts';
    public const DIAMONDS = 'Diamonds';
    public const CLUBS = 'Clubs';
    public const SPADES = 'Spades';
}

enum Suit: string
{
    use SuitTrait;

    case Hearts = self::HEARTS;
    case Diamonds = self::DIAMONDS;
    case Clubs = self::CLUBS;
    case Spades = self::SPADES;
}

Limitations

There are a few limitations to how you use constants in traits.

  • First of all, you can not directly access a constant defined in a trait from a class that uses the trait. You have to use the self keyword to access the constant.

So, this will not work:

trait PersonTrait
{
    public const MIN_AGE = 18;
}

class Person
{
    use PersonTrait;

    public function __construct(
        private int $age,
    ) {}

    public function isAdult(): bool
    {
        return $this->age >= PersonTrait::MIN_AGE;
    }
}

$person = new Person(20);

var_dump($person->isAdult());
// PHP Fatal error:  Uncaught Error: Cannot access trait constant PersonTrait::MIN_AGE directly

You can only access the constant from the class that uses the trait if you use the descendant class name, self, static, parent, or its instance.

  • You can not override a constant defined in a trait. This means that if you define a constant with the same name in a class that uses the trait and the value/visibility of the constant is different, you will get a fatal error.
<?php

trait PersonTrait
{
    public const MIN_AGE = 18;
}

class Person
{
    use PersonTrait;

    private const MIN_AGE = 21;
}

// PHP Fatal error:  Person and PersonTrait define the same constant (MIN_AGE) in the composition of Person. However, the definition differs and is considered incompatible.
  • If you declare a final constant in a trait, you can not override it in a class that uses the trait.
<?php

trait PersonTrait
{
    final public const MIN_AGE = 18;
}

class Person
{
    use PersonTrait;

    public const MIN_AGE = 21;
}

// PHP Fatal error:  Person and PersonTrait define the same constant (MIN_AGE) in the composition of Person. However, the definition differs and is considered incompatible.

In conclusion

Constant traits are a great addition to PHP. They allow you to define constants in traits and use them in classes that use the trait. This is especially useful if you have a constant that you want to use in multiple places. This makes it easier to maintain your code, as you only have to define the constant in one place.

Learn the fundamentals of PHP 8 (includes 8.1 and 8.2), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-read guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

👋 Hi there! I'm Amit. I write articles about all things web development. If you like what I write and want me to continue doing the same, I would like you buy me some coffees. I'd highly appreciate that. Cheers!

Comments?