11

I'm fairly new to using traits in PHP and am wondering if there is a way to ensure that a class including a trait has particular properties.

I know that you can use something like the following to ensure the using class has particular methods.

protected abstract function some_function();

Is there a way to do the same kind of thing for member variables, since a trait has access to the member variables in the using class?

I can just document the requirement, but it would be nice to throw an exception in case a class trying to use a trait is missing a required property.

The closest that I can find is to create a function in the trait that attempts to grab the variable and return it ... and throw an exception if it is not found. Then the methods in the trait can use that to access the parent classes property instead of accessing them directly.

Thoughts, suggestions, and recommendations welcome ... as I am just getting used to traits.

The use case is that I have certain things that only run on the public face of the site and certain other things that only run on the private face of the site, but both share a common set of base functionality (various configuration settings, error handling, etc).

I could avoid using traits by making the class files for each of the inherited classes extremely long, but I'm trying to keep like functionality together in smaller files if possible.

I suppose one possibility would be to require an accessor variable in the calling class to return the wanted property.

abstract function get_my_value();

Anything else that might work cleanly?

Thanks in advance!

3
  • 2
    I am putting into a comment since it doesn't exactly answer your question. Anyway. There is no way to ensure things like that because trait is not part of the class. You can think of it (it's a bit oversimplified) as of an include for classes. It is a simple way of doing copy/paste without code duplication. If you have a shared functionality in your trait then this trait should already have everything to work by itself. If it doesn't you might already have some conceptual issues. Commented Jun 28, 2015 at 18:12
  • Thanks awons. Reviewing what I was doing at the time I did have a couple of conceptual problems.
    – Privateer
    Commented Aug 17, 2015 at 21:49
  • Stop and think for a moment why you think you need a trait in the first place. Traits cannot enforce properties, the same reason you cannot put properties on interfaces in PHP. Traits should be your last resort, dont use them if you dont have to.
    – slepic
    Commented Mar 16, 2020 at 5:49

2 Answers 2

8

The best way I have found to do this is as follows:

class This_Is_My_Class {
    use My_Trait;

    protected $prefix = '';

    function __construct() {
        $this->prefix = 'not_empty';
    }

    protected function get_prefix() {
       if ( empty($this->prefix) ) {
           throw new Exception("Prefix must be non empty.");
       }
       return $this->prefix;
    }

    ... rest of class here
}

trait My_Trait {

    abstract protected function get_prefix();

    protected function do_whatever() {
        $prefix = $this->get_prefix();
        ... do stuff
    }
}

Now, when using the trait, you properly get an exception when attempting to do things with it when the class using it either lacks the get_prefix function (due to the abstract declaration) or tries to use the prefix (due to the exception in the parent class).

Of course, if there is a cleaner solution to enforce that a class using a trait has a particular property, I'd love to hear it.

1
  • An improved version might be to define accessors (setter only in case the trait needs writing on the prop) and just ask the user to provide implementation inside the class; as you say. Imho the getter shouldn't do any validation of requirements from the trait; it should not throw an exception, but just access the data in the custom property defined on the class. Is IoC, implicitly gives the responsibility of choosing the data source and returning the correct value, to the user of the trait. It reduces boilerplate and prevents trying to validate any possible wrong thing the user could do Commented Jan 20, 2020 at 21:41
2

Best way I think

trait ProductTrait
{

    final private function getTable() {
        if ( empty($this->table_product) ) {
           throw new \Exception('$table_product must be defined in '. __CLASS__);
        }
        return $this->table_product;
    }
    //From here, just use $this->getTable() instead of $this->table_product

}

If a class using this Trait does not have the table_product property, exception will occur. We dont need anything in that class (and others too) to detect it :)

1
  • As of PHP 8, private methods cannot be final, because they are never overridden by other classes anyway (unless the constructor). It never made sense to declare a private method as final because private methods cannot be overridden by child classes. However, since the trait is part of the class, it does not matter much anyway how much you protect or final, at all, since the class using the trait can override everything it uses, it only will matter when other child classes of the using class start messing with those methods. private is probably the best bet.
    – user414301
    Commented May 2, 2022 at 5:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.