PHP Interface, Implementation, Parameter und abgeleitete Klasse

Yuuri

Fleet Admiral
Registriert
Okt. 2010
Beiträge
13.923
Hallo,

ich wollte mal nachfragen ob PHP einfach noch nicht so weit ist oder es irgend eine Eigenheit besitzt?

cl.png
Code:
interface I { public function F( A $p ); }

class A {}
class A_B extends A {}

class X implements I
{
	public function F( A $p ) { }
}

class Y implements I
{
	public function F( A_B $p ) { }
}
gibt mir den Fehler
Code:
Fatal error: Declaration of Y::F() must be compatible with I::F(A $p)
Typenlos würde ich das Interface nur ungern gestalten, aber wenn es nicht anders geht...


LG
 
Nicht das ich mich so im Detail mit der OO Unterstützung von PHP auskennen würde, aber die von dir verwendete PHP Version wäre sicherlich noch hilfreich :)
 
5.4.4-14 (Linux) hier auf Arbeit, 5.5.3 daheim (Windows) - macht bei beiden Versionen keinen Unterschied.
 
Hi,

korrigiere mich wenn ich falsch liege, aber bei der Interfacemethode übergibst du einen Parameter $p vom Typ "A", bei der Implementierung dann vom Typ "A_B". Das kann so nicht klappen wegen Typinkompatibilität. Oder täusche ich mich jetzt?

VG,
Mad
 
Ohne meine Hand ins Feuer legen zu wollen (bin nicht so wirklich in der OO Welt zuhause), aber PHP hat doch recht. Du deklarierst im Interface eine Methode mit einer bestimmten Signatur, benutzt in der Klasse Y, die dieses Interface implementiert, für die Methode aber eine andere Signatur (Typehint "A_B" anstatt "A").
 
Eben. So funktioniert das eigentlich nirgendwo. Wenn dein Interface typenbehaftet ist, dann musst du dich an die Typen auch halten.
 
Ist doch aber eine abgeleitete Klasse von A, ergo sind die gleichen Variablen/Methoden in A_B vorhanden und somit problemlos ansprechbar?!
Code:
class I { public function F( A $p ) {} }

class A {}
class A_B extends A {}

class X extends I
{
	public function F( A $p ) { }
}

class Y extends I
{
	public function F( A_B $p ) { }
}
Macht PHP problemlos mit (so wie man es ja gewohnt ist), aber warum regt er sich hier bei Interfaces auf? Oder stellt das eine allgemeine Beschränktheit von Interfaces dar? Ist doch im Prinzip nichts anderes als eine Klasse mit abstraken Methoden.
 
Normalerweise kann man beim Überschreiben von Methoden in der erbenden Klasse keine spezielleren Typen für Methodenparameter vorschreiben, allgemeinere Parametertypen sollten jedoch gehen (weiß nicht, ob PHP das unterstützt). Das nennt man dann Kontravarianz:

https://de.wikipedia.org/wiki/Kovarianz_und_Kontravarianz

Ist ja auch sinnvoll, denn die implementierende Klasse soll ja (mindestens) alles können/akzeptieren, was das Interface vorgibt. Und wenn die nur speziellere Typen akzeptiert, dann kann sie weniger.

Nachtrag
In der Doku von PHP steht:
The class implementing the interface must use the exact same method signatures as are defined in the interface. Not doing so will result in a fatal error."
Quelle: http://www.php.net/manual/en/language.oop5.interfaces.php
 
Zuletzt bearbeitet:
Wenn du I als Klasse definierst, dann sind die Methoden in den von I erbenden Klassen einfach Methoden, bzw. überschreiben die Methode von I.
 
Vielleicht mal als konkretes Beispiel:

Ich bau mir grad einen Wrapper für das TS 3 PHP Framework. Im Prinzip ist dort alles in ein TeamSpeak3_Node_Abstract gepackt, von dem alle Nodes erben (Channel -> TeamSpeak3_Node_Channel, Client -> TeamSpeak3_Node_Client, etc.).

Ich hab dann eine Wrapperklasse Client, die von einem ähnlichen Interface erbt. Diese hat nur eine Methode, um von einem TeamSpeak3_Node_Abstract die nötigen Infos auszulesen (mittels getInfo()).

Mal ganz kurz skizziert (mit Einfluss von #7):
Code:
class TeamSpeak3_Node_Abstract {}
class TeamSpeak3_Node_Channel extends TeamSpeak3_Node_Abstract {}
class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract {}

class Object
{
	static public function FromNode( TeamSpeak3_Node_Abstract $N ) {}
}
class Channel extends Object
{
	static public function FromNode( TeamSpeak3_Node_Channel $N )
	{
		// ...
	}
}
class Client extends Object
{
	static public function FromNode( TeamSpeak3_Node_Client $N )
	{
		// ...
	}
}
Damit würde es ja funktionieren (gibt zumindest keinen Fehler aus und ein Test mit obig skizzierter Variante gibt auch keine semantischen Fehler), nur erschließt sich mir der Sinn nicht, wieso dies mit dem Interface nicht geht. Wissenslücke irgendwo? Ich will ihm ja nur sagen, dass die Methode FromNode() eine Implementation benötigt, aber ohne konkreten Code zu hinterlegen (weil sonst vielleicht gar keine Implementation drin ist).

Den Parameter der Funktion typenlos zu machen bringt mir ja nichts, da ich dann genauso auf if( $N instanceof ... ) prüfen und sonst eine Exception oder irgendwas werfen muss.
 
In C# geht das ganze übrigens auch nicht. Da wäre das ganze nur eine Überladung (sofern beide Funktionen implementiert wären).
 
Zuletzt bearbeitet:
Tja das gibts ja in PHP nun mal nicht... :(
 
Ich hab das ganze übrigens immer so gelöst:

PHP:
class Y implements I
{
    public function F(A $a)
    {
        if ($a instanceof A_B) {
            return $this->_F($a);
        }
        throw new Exception('Wrong Type given');
    }

    protected function _F(A_B $ab)
    {
        // do Stuff here
    }
}

PHP lässt leider nichts schöneres zu.
 
Deswegen der Thread, weil ich das sehr unschön finde. ;) Aber wenn nichts anderes geht, werd ich das wohl so machen müssen. :(
 
Hi,

in meinen Augen ist es genau so wie im Beispiel von trialgod einfach am Thema vorbei. Ein Interface soll in meinen Augen genau und exakt festlegen, was genau vor sich geht. Diese Workarounds und Hacks finde ich nicht wirklich schön. Die Fehlermeldung ist in meinen Augen vollkommen korrekt - wenn du ein Interface implementieren willst dann musst du dich an die Konventionen halten, die das Interface vorgibt und nicht konkretere abgeleitete Objekte übergeben.

VG,
Mad
 
Madman hat schon Recht. Die Interfaces sind ja dafür da eine Schnittstelle zu beschreiben (ja ich kann aus dem englischen übersetzen, doll was ;) ). Und diese Schnittstelle sollte dann auch so vorzufinden sein. Wenn man es ganz toll macht, müsste man hier eigentlich die Klasse A auch nochmal interfacen und dann den Typ des zweiten Interfaces in der Methode übergeben.

Leider bietet PHP oftmals nicht die richtigen Werkzeuge um mit einheitlicher Typisierung immer umzugehen.
 
Hi,

das sollte auch nicht böse gemeint sein oder so :) Nicht dass das falsch rüber kommt.

Wenn man es braucht kommt man nicht anders weiter an der Stelle. Leider. Da habt ihr schon recht.

VG,
Mad
 
Zurück
Oben