How to type hint a trait when using the trait to realize multi-inheritance?

Viewed 89

Multi-inheritance is realized by using trait, but the type of parameters cannot be limited. For example, the following example

trait T{
    function t(){
        echo 'trait';
    }
    // Omit many other codes
}

class B{ } // Assume that B is a third-party module

class C extends B{ // Assume that C must inherit the module B
    use T;
}

// Some functions in some classes in the following client code may need to use several methods in T, like the following.
function f(T $t){ // Type-hinting for the trait T, but there is a problem
    $t->t();
}

f(new C);

Sandbox test found an error Fatal error: Uncaught TypeError: f(): Argument #1 ($t) must be of type T, C given

If f(T $t) is changed to f(C $t) or f($t), it will be fine, but this is not what I want. It won't work if the trait is changed to an interface, because the function body can't be written in the interface. It won't work if the trait is changed to a class, because C has already inherited one class and can't inherit another. What can we do to realize multi-inheritance and restrict types?


I have an idea. I wonder if it can be realized. That is to say, write a module specially designed to handle the exception of trait type, and then include the module globally at the beginning of my project. The logic of the module is roughly as follows:

  1. Match the keywords T and C in the exception message must be of type T, C given;
  2. Use the ReflectionClass to get the code block of C;
  3. Search for the keyword use T in the code block of C. If found, ignore the exception and continue to execute the code, otherwise, throw the exception.

We can use set_exception_handler('exception _ handler') to catch exceptions. The question is how to ignore this exception and ensure that the program continues to execute?

2 Answers

Traits are not classes, we just use them to reuse some code in multiple classes. You may create an interface and use it as the common type in your method.

PHP doesn’t support multiple inheritance so it uses traits and interfaces to get around that limitation. PHP does not treat a trait as a class (as you have experienced).

I believe you're trying to force PHP to do something it just can't do. Traits are used as "vehicle" or an independent package to augment, expand, and inject some functionality into classes when the code for that functionality doesn't belong in a parent class, or in an interface.

For example, I've created database access traits to give specific classes the ability to read and write data, and once I wrapped Guzzle functionality into a trait so some classes could make API calls.

You can argue whether these are good examples, or whether these specific examples should have been traits at all (and I might agree). But the point is, traits are generally used to add something that shouldn't be in a parent class. In my cases I chose to add them as traits because it seemed like way too much code to add to a parent class when most of the children were not going to be using those methods, and because multiple classes needed access to that functionality.

In your case,

function f(T $t){ $t->t(); }

would be,

function f(C $c){ $c->t(); }

because C is using T and t() is a method in T.

Related