PHP 7-Schnittstellen, Hinweise zum Rückgabetyp und selbst

Lesezeit: 7 Minuten

Benutzer-Avatar
GordonM

AKTUALISIEREN: PHP 7.4 jetzt unterstützt Kovarianz und Kontravarianz die das Hauptproblem anspricht, das in dieser Frage aufgeworfen wird.


Ich bin auf ein Problem mit der Verwendung von Rückgabetyphinweisen in PHP 7 gestoßen. Mein Verständnis ist dieser Hinweis : self bedeutet, dass Sie beabsichtigen, dass eine implementierende Klasse sich selbst zurückgibt. Deshalb habe ich verwendet : self in meinen Schnittstellen, um dies anzuzeigen, aber als ich versuchte, die Schnittstelle tatsächlich zu implementieren, bekam ich Kompatibilitätsfehler.

Das Folgende ist eine einfache Demonstration des Problems, auf das ich gestoßen bin:

interface iFoo
{
    public function bar (string $baz) : self;
}

class Foo implements iFoo
{

    public function bar (string $baz) : self
    {
        echo $baz . PHP_EOL;
        return $this;
    }
}

(new Foo ()) -> bar ("Fred") 
    -> bar ("Wilma") 
    -> bar ("Barney") 
    -> bar ("Betty");

Die erwartete Ausgabe war:

Fred Wilma Barney Betty

Was ich eigentlich bekomme ist:

PHP Fatal error: Declaration of Foo::bar(int $baz): Foo must be compatible with iFoo::bar(int $baz): iFoo in test.php on line 7

Die Sache ist, dass Foo eine Implementierung von iFoo ist, also sollte die Implementierung, soweit ich das beurteilen kann, perfekt mit der gegebenen Schnittstelle kompatibel sein. Ich könnte dieses Problem vermutlich beheben, indem ich entweder die Schnittstelle oder die implementierende Klasse (oder beide) ändere, um den Hinweis auf die Schnittstelle nach Namen zurückzugeben, anstatt sie zu verwenden selfaber mein Verständnis ist das semantisch self bedeutet “gibt die Instanz der Klasse zurück, für die Sie gerade die Methode aufgerufen haben”. Daher würde das Ändern in die Schnittstelle theoretisch bedeuten, dass ich jede Instanz von etwas zurückgeben könnte, das die Schnittstelle implementiert, wenn meine Absicht darin besteht, dass die aufgerufene Instanz zurückgegeben wird.

Ist dies ein Versehen in PHP oder eine bewusste Designentscheidung? Wenn es ersteres ist, gibt es eine Chance, dass es in PHP 7.1 behoben wird? Wenn nicht, was ist dann der richtige Weg, um darauf hinzuweisen, dass Ihre Schnittstelle erwartet, dass Sie die Instanz zurückgeben, für die Sie gerade die Methode zum Verketten aufgerufen haben?

  • Ich denke, es ist ein Fehler im PHP-Rückgabetyp-Hinweis, vielleicht sollten Sie ihn als auslösen Insekt; aber es ist unwahrscheinlich, dass es in diesem späten Stadium noch einen Fix in PHP 7.1 gibt

    – Markus Bäcker

    21. August 2016 um 21:33 Uhr


  • Da die letzte Beta-Version von 7.1 vor einigen Tagen online gegangen ist, ist es sehr unwahrscheinlich, dass ein Fix in 7.1 einfließt.

    – Charlotte Dunois

    21. August 2016 um 21:38 Uhr

  • Aus Interesse, wo liest du deine Interpretation, wie das geht self Rückgabetyp soll funktionieren?

    – Adam Cameron

    21. August 2016 um 21:45 Uhr


  • @ Adam: Es scheint nur logisch zu sein self bedeutet “Gib die Instanz zurück, auf der du dies aufgerufen hast, und nicht eine andere Instanz, die dieselbe Schnittstelle implementiert”. Ich meine mich zu erinnern, dass Java einen ähnlichen Rückgabetyp hatte (obwohl es eine Weile her ist, seit ich Java-Programmierung gemacht habe).

    – GordonM

    21. August 2016 um 21:53 Uhr

  • Hallo Gordon. Nun, wenn es nicht dokumentiert ist, dass es irgendwo funktioniert, würde ich mich nicht darauf verlassen, dass das, was logisch sein könnte, Realität ist. TBH mit der Situation, die ich beschreiben würde, würde ich nur so deklarativ wie möglich sein und iFoo als Rückgabetyp verwenden. Gibt es eine Situation, in der das nicht wirklich funktioniert? (Mir ist klar, dass dies eher “Rat” / Meinung als “Antwort” ist.

    – Adam Cameron

    21. August 2016 um 23:25 Uhr

Benutzer-Avatar
Benutzer3942918

Anmerkung der Redaktion: Die folgende Antwort ist veraltet. als php PHP7.4.0 ist Folgendes vollkommen legal:

<?php
Interface I{
    public static function init(?string $url): self;
}
class C implements I{
    public static function init(?string $url): self{
        return new self();
    }
}
$o = C::init("foo");
var_dump($o);

ursprüngliche Antwort:

self bezieht sich nicht auf die Instanz, sondern auf die aktuelle Klasse. Es gibt keine Möglichkeit für eine Schnittstelle, dasselbe anzugeben Beispiel muss zurückgegeben werden – mit self in der Art und Weise, wie Sie es versuchen, würde nur erzwingen, dass die zurückgegebene Instanz derselben Klasse angehört.

Das heißt, Rückgabetypdeklarationen in PHP müssen invariant sein, während das, was Sie versuchen, kovariant ist.

Ihre Verwendung von self ist äquivalent zu:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : Foo  {...}
}

was nicht erlaubt ist.


Das Rückgabetypdeklarationen RFC hat dies zu sagen:

Die Erzwingung des deklarierten Rückgabetyps während der Vererbung ist unveränderlich; Das bedeutet, dass, wenn ein Untertyp eine übergeordnete Methode überschreibt, der Rückgabetyp des untergeordneten Elements genau mit dem übergeordneten übereinstimmen muss und nicht weggelassen werden darf. Wenn der Elterntyp keinen Rückgabetyp deklariert, darf der Kindtyp einen deklarieren.

Dieser RFC schlug ursprünglich kovariante Rückgabetypen vor, wurde aber aufgrund einiger Probleme in invariant geändert. Es ist möglich, zu einem späteren Zeitpunkt kovariante Rückgabetypen hinzuzufügen.


Im Moment ist zumindest das Beste, was Sie tun können:

interface iFoo
{
    public function bar (string $baz) : iFoo;
}

class Foo implements iFoo
{

    public function bar (string $baz) : iFoo  {...}
}

  • Das heißt, ich hätte einen Rückgabetyp-Hinweis von erwartet static funktionieren, aber es wird nicht einmal erkannt

    – Markus Bäcker

    22. August 2016 um 7:20 Uhr

  • Paul, das Löschen der Kommentare, die Sie hier gelöscht haben, ist tatsächlich schädlich, weil (A) dadurch wichtige Informationen verloren gehen und (B) es den Diskussionsfluss in Bezug auf die anderen Kommentare stört. Ich sehe keinen Grund, warum Ihre Kommentare, die sich auf Mark und Gordon berufen, gelöscht werden mussten. Tatsächlich machst du das überall und es muss aufhören. Es gibt absolut keinen guten Grund, zu einer ein Jahr alten Frage zurückzukehren und alle Ihre Kommentare zu entfernen, wodurch der Diskussionsfluss vollständig zerstört wird. Tatsächlich ist es schädlich und störend.

    – Cody Grey

    22. August 2017 um 14:00 Uhr


  • Es gibt ein wichtiges Vorwort zu einem Teil Ihres Zitats, das hier zu sehen ist: “Kovariante Rückgabetypen gelten als Typsound und werden in vielen anderen Sprachen verwendet (C++ und Java, aber nicht C#, glaube ich). Dieser RFC schlug ursprünglich kovariante Rückgabetypen vor, wurde aber aufgrund einiger Probleme in invariant geändert es sei denn, Sie lockern die Beschränkungen für Rückgabetypen (wie in einigen Antworten unten zu sehen).Sehr frustrierend.

    – John Pancoast

    3. Januar 2020 um 0:06 Uhr


  • @MarkBaker der Rückgabetyp static wird zu PHP 8 hinzugefügt.

    – Ricardo Boss

    5. August 2020 um 19:33 Uhr

  • Vielleicht verdient es auch die “beste” Option zu sein /** @return Foo */ (wenn Sie Ihre PHP-Version nicht aktualisieren können). Auf diese Weise würde die IDE zumindest den richtigen Typhinweis vorschlagen. Habe es mit Netbeans getestet.

    – volvpavl

    11. Januar 2021 um 21:56 Uhr


Es kann auch eine Lösung sein, dass Sie den Rückgabetyp nicht explizit im Interface definieren, sondern nur im PHPDoc und dann den bestimmten Rückgabetyp in den Implementierungen definieren können:

interface iFoo
{
    public function bar (string $baz);
}

class Foo implements iFoo
{
    public function bar (string $baz) : Foo  {...}
}

  • Oder statt Foo benutz einfach self.

    – stattdessen

    25. Dezember 2018 um 20:20 Uhr

PHP 8 fügt “statischen Rückgabetyp” hinzu, der Ihr Problem löst.

Schauen Sie sich diesen RFC an: https://wiki.php.net/rfc/static_return_type

Das sieht für mich nach dem erwarteten Verhalten aus.

Ändere einfach deine Foo::bar Methode zurückzugeben iFoo Anstatt von self und fertig damit.

Erläuterung:

self wie in der Schnittstelle verwendet, bedeutet “ein Objekt des Typs iFoo.”
self wie in der Implementierung verwendet bedeutet “ein Objekt des Typs Foo.”

Daher sind die Rückgabetypen in der Schnittstelle und der Implementierung eindeutig nicht identisch.

Einer der Kommentare erwähnt Java und ob Sie dieses Problem haben würden. Die Antwort ist ja, Sie hätten das gleiche Problem wenn Java Ihnen erlauben würde, solchen Code zu schreiben – was es nicht tut. Da Java erfordert, dass Sie den Namen des Typs anstelle von PHP verwenden self Verknüpfung, Sie werden das nie wirklich sehen. (Sehen hier für eine Diskussion über a ähnlich Problem in Java.)

1354690cookie-checkPHP 7-Schnittstellen, Hinweise zum Rückgabetyp und selbst

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy