How to restrict PHP traits to certain classes
Asked Answered
B

2

12

I have the following trait:

trait ARCacheableTrait
{
    public function instantiate() {
        // this will need to call some ActiveRecord methods using parent::
    }
}

It's purpose is to override the instantiate method of ActiveRecord classes. What is the proper way to ensure that it's applied only over such classes? I'd want to throw an exception if someone tries to add it to classes that are not or do not extend ActiveRecord or even better, ensure type safety by throwing a compile time error...

Bibulous answered 13/7, 2014 at 11:12 Comment(2)
There's no such limitation for traits. Traits are explicitly independent of any class that implements them, that's the point. Simply don't use the trait on classes it's not indented to be used in, period.Freedom
You may check at runtime if the class is ActiveRecord, and if not, throw some exception.Burget
F
19

The best you can do is use abstract method definitions to impose requirements upon the exhibiting class:

trait ARCacheableTrait {

    public function instantiate() {
        parent::foo();
    }

    abstract public function foo();

}

That forces the exhibiting class to implement a method foo, so as to ensure that the trait can call it. However, there's no way to restrict a trait to be exhibited exclusively within a certain class hierarchy. If you want that, you probably want to implement a specific sub class of ActiveRecord which implements this specific instantiate behaviour instead of using a trait.

Freedom answered 13/7, 2014 at 11:42 Comment(0)
G
0

Personally, I use a check method that I call before each Trait method:

trait ARCacheableTrait
{
    public function instantiate()
    {
        static::canUseTraitARCacheableTrait();
        // this will need to call some ActiveRecord methods using parent::
    }

    private static function canUseTraitARCacheableTrait()
    {
        static $isCorrectClass;

        $isCorrectClass = $isCorrectClass ?? is_a(static::class, ActiveRecord::class, true);
        if (!$isCorrectClass) {
            throw new LogicException(
                sprintf(
                    'Only classes that inherit %s may use %s trait. The %s class cannot use it',
                    ActiveRecord::class,
                    __TRAIT__,
                    static::class
                )
            );
        }
    }
}

The downside of this solution is that an exception is thrown only after a some method is called.

On the other hand, the advantage in relation to what "deceze" proposed is that Trait decides who can use it. And you don't need to add an implementation of the "foo" method in every class, and the fact that the implementation of "foo" does not clearly indicate that it is the right class.

Garnetgarnett answered 27/6, 2023 at 13:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.