Home » Php » php – Type Hinting For Multiple Unrelated Interfaces

php – Type Hinting For Multiple Unrelated Interfaces

Posted by: admin July 12, 2020 Leave a comment

Questions:

Is there a way in php to type hint for two different, unrelated interfaces? For example:

interface errorable {
   function error($msg);
}

interface recordable {
   ssh_for_recorder();
}

class uploader__module extends base__module implements errorable, recordable {
   public function ssh_for_recorder() {
      return new ssh2;
   }
   public function error($msg) {
      $this->errors[] = $msg;
   }

   public function upload() {
      $recorder = new recorder($this);
      $recorder->run();
   }
}

class recorder {
   private $ssh2;
   private $module;
   private function upload() {
      if (!$this->ssh2) {
         $this->module->error("No SSH2 connection");
      }
   }
   public function __construct({recordable,errorable} $module) {
      $this->module = $module;
      $this->ssh2 = $module->ssh_for_recorder();
   }
}

As you can see in the above code, the recorder class expects its module to have the ability to run both error() and ssh_for_recorder(), but these are defined by different interfaces. errorable need not be recordable and vice versa either.

Is there a best practice for doing this? I was thinking of creating an interface that extends from recordable and errorable and having upload__module implement that, but I don’t know what to call it.

How to&Answers:

No, this is not possible in php.

There are other languages (mostly functional) that support this feature which is called a union type ( http://en.wikipedia.org/wiki/Sum_type ).

Answer:

The only hack within PHP is a helper function to do the checks for you within the method like so:

function CheckInterfaces($object,array $interfaces)
{
    foreach($interfaces as $i)
    {
         if(!is_a($object,$i))
         {
             return false;
         }
    }
    return true;
}

And then within the method do:

public function Something($object)
{
    if(CheckInterfaces($object,array("foo","bar")))
    {
        throw new ArgumentException(gat_class($object) . " Must be a member of foo,bar to be passed to Something");
    }
}

another method around this issue ius to create a union interface for your required interfaces, heres quick example

interface foobar extends foo,bar{}

then you can just require foobar for the method.

Answer:

I’ve decided to answer this question even though you’ve already accepted an answer on the grounds that none of the given answers are really acceptable. While the accepted answer is technically correct, there is a way getting around it. As for the other answers, their workarounds are inelegant and not entirely satisfactory.

PHP supports a fairly obscure feature that allows one interface to inherit from another, and in fact an interface is capable of inheriting from multiple base interfaces.

For example, the following is perfectly valid:

interface iFoo
{
    public function doFoo ();
}

interface iBar
{
    public function doBar ();
}

interface iBaz extends iFoo, iBar
{
    // This interface implicitly has all the methods of iFoo and iBar
}

Semantically, if you want a method/function to only accept an argument that implements multiple interfaces then that would tend to suggest that you expect that classes that implement the same set of multiple interfaces should in fact be implementing an interface that covers both the interfaces you want your argument to conform to.

In your case if you want something that is both a errorable and a recordable then you simply need to add the following interface:

interface RecordableErrorable extends Recordable, Errorable { }

And then the constructor for your Recorder class would simply expect that interface as its argument.

public function __construct(RecordableErrorable $module) { }

One possible sticking point could be if Recordable and Errorable both implement methods with the same name. There would be a clash there that would need resolving. I do believe there are mechanisms in PHP for handling that case, though I couldn’t tell you what they are.

Answer:

public function __construct(recordable $recordable_module, errorable $errorable_module) {

  if($recordable_module == $errorable_module){
     $module = $recordable_module;
  }
  $this->module = $module;
  $this->ssh2 = $module->ssh_for_recorder();
}