Type Hinting For Multiple Unrelated Interfaces
Asked Answered
G

5

7

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.

Growing answered 1/3, 2011 at 22:51 Comment(3)
what does this have to do with type hinting?Wye
See the constructor for recorder. I want to type hint both recordable and errorable.Growing
I think @Hammish is getting confused, Type hinting in PHP is requiring a class to be of a certain type, where @tandu requires to check multiples types of interfaces.Bucentaur
I
10

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 ).

Innes answered 1/3, 2011 at 23:37 Comment(3)
How would you suggest to get around this in PHP?Growing
Well, make a new interface that extends both recordable and errorable. However you will have to change the classes that implement both interfaces to implement the new interface instead.Innes
As I thought. I ended up having recordable extend errorable so that worked out too in my application.Growing
B
6

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.

Bucentaur answered 1/3, 2011 at 23:51 Comment(3)
#1 is interesting but I think it goes way overboard. I prefer #2. Just a note, though, interfaces extend other interfaces, they do not implement them. Your code will cause a syntax error.Growing
Though it's the only reasonable solution, I don't like 2 because I also have to change the class to implement the union interface, instead of the separate interfaces individuallyMeagan
It's #2. That's the way.Courteous
P
5

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.

Psychobiology answered 21/8, 2016 at 21:33 Comment(0)
P
1

I know it is old but there have been some changes to PHP. PHP8.2 now supports Disjunctive Normal Form Types https://wiki.php.net/rfc/dnf_types

Something like public function __construct(Recordable & Errorable $module) is possible.

Pitchdark answered 10/11, 2023 at 12:3 Comment(0)
P
0
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();
}
Phillie answered 10/4, 2015 at 4:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.