Search code examples
phptraitsphp-7.4

traits with colliding method names - how to work with?


I have an interface in essence defining getEscapedString() which is implemented in a couple of different traits; the actual classes pick one of them. This looks something similar to (excuse typos as this is just an example and not c/p); similar for Layout_B, Layout_C etc.

trait Layout_A {
  abstract protected function getUnescapedString() : String;
  protected function getCSS() : ?String {
    return NULL;
  }
  public function getEscapedString() : String {
    echo sprintf('<td class="%s">%s</td>', $this->getCSS(), $this->getUnescapedString());
  }
}

Works fine. Different layout use different abstract methods (rather by fortune), but most of them implement getCSS(). Some classes overwrite this to implement their own style; however, this method is not part of the interface as an implementation can perfectly live without it.

Now there is the need to combine two of these traits into a single class (by concatenating the output):

class DoubleOutput {
  use Layout_A, Layout_B {
    Layout_A::getEscapedString as getEscapedString_Layout_A;
    Layout_B::getEscapedString as getEscapedString_Layout_B;
  }

  public function getEscapedString() : String {
    echo getEscapedString_Layout_A();
    echo getEscapedString_Layout_B();
  }
}

This causes a collision of getCSS() which I could resolve e.g. by adding this into the use-clause:

    Layout_A::getCSS insteadof Layout_B::getCSS;
    Layout_A::getCSS as getCSS_Layout_A;
    Layout_B::getCSS as getCSS_Layout_B;

Now the result runs fine except that both traits access Layout_A::getCSS or - if I overwrite the method within the class - both traits access the new implementation.

Is there any (nice) way to let $trait::getEscapedString() use the matching $traig::getCSS() method? Only way I can think of so far is to change the traits to:

trait Layout_A {
  abstract protected function getUnescapedString() : String;
  protected function getCSS_Layout_A() : ?String {
    return NULL;
  }
  public function getEscapedString() : String {
    echo sprintf('<td class="%s">%s</td>', $this->getCSS_Layout_A(), $this->getUnescapedString());
  }
}

But this is really not good-looking and rather confusing for all usages of this trait except the double one.


Solution

  • For example you have serveral classes and their instances

    class Layout_Currency;
    class Layout_SimpleImage;
    ...
    
    $layout_currency = new Layout_Currency();
    $layout_simpleimage = new Layout_SimpleImage();
    ...
    

    Like the traits, each of them outputs data in the desired format.

    Now if you want to output Currency + SimpleImage with a single class, you could implement it like this:

    class DoubleOutput {
        public function getEscapedString(){
             global $layout_currency, $layout_simpleimage;
             $layout_currency->getEscapedString();
             $layout_simpleimage->getEscapedString();
        }
    }